diff --git a/gracedb/alert.py b/gracedb/alert.py index 780833114a75161d1aa7f1a1072c9e050ef2b8ac..15e4f4b36f21713d4684a7f42866a4aa0c47ed5e 100644 --- a/gracedb/alert.py +++ b/gracedb/alert.py @@ -11,8 +11,8 @@ import logging log = logging.getLogger('gracedb.alert') -def issueAlert(event, location, temp_data_loc, event_url): - issueXMPPAlert(event, location, temp_data_loc) +def issueAlert(event, location, event_url, serialized_object=None): + issueXMPPAlert(event, location, serialized_object=serialized_object) issueEmailAlert(event, event_url) def indent(nindent, text): @@ -23,14 +23,16 @@ def prepareSummary(event): return "GPS Time: %s" % event.gpstime -def issueAlertForUpdate(event, description, doxmpp, filename=""): +# The serialized object passed in here will normally be an EventLog or EMBB log entry +def issueAlertForUpdate(event, description, doxmpp, filename="", serialized_object=None): if doxmpp: - issueXMPPAlert(event, filename, "", "update", description) + issueXMPPAlert(event, filename, "update", description, serialized_object) # XXX No emails for this. Argh. -def issueAlertForLabel(event, label, doxmpp): +# The only kind of serialized object relevant for a Label is an event. +def issueAlertForLabel(event, label, doxmpp, serialized_event=None): if doxmpp: - issueXMPPAlert(event, "", "", "label", label) + issueXMPPAlert(event, "", "label", label, serialized_event) # Email profileRecips = [] pipeline = event.pipeline @@ -109,7 +111,8 @@ Event Summary: #send_mail(subject, message, fromaddress, toaddresses) -def issueXMPPAlert(event, location, temp_data_loc, alert_type="new", description=""): +def issueXMPPAlert(event, location, alert_type="new", description="", serialized_object=None): + log.debug('issueXMPPAlert: inside') nodename = "%s_%s" % (event.group.name, event.pipeline.name) nodename = nodename.lower() @@ -134,12 +137,13 @@ def issueXMPPAlert(event, location, temp_data_loc, alert_type="new", description lva_data = { 'file': location, 'uid': event.graceid(), - 'data_loc': temp_data_loc, 'alert_type': alert_type, # The following string cast is necessary because sometimes # description is a label object! 'description': str(description), } + if serialized_object: + lva_data['object'] = serialized_object msg = json.dumps(lva_data) log.debug("issueXMPPAlert: writing message %s" % msg) @@ -148,6 +152,7 @@ def issueXMPPAlert(event, location, temp_data_loc, alert_type="new", description null = open('/dev/null','w') p = Popen( ["lvalert_send", +# "--server=jabber.phys.uwm.edu", "--username=gracedb", "--password=w4k3upal1ve", "--file=-", diff --git a/gracedb/api.py b/gracedb/api.py index dd843db4f6b4222c9b36dc3105a34e8a436a6b1f..7339b5349cba74457481b7eaee5f99f11d05b658 100644 --- a/gracedb/api.py +++ b/gracedb/api.py @@ -4,8 +4,6 @@ from django.http import HttpResponseForbidden, HttpResponseServerError from django.core.urlresolvers import reverse as django_reverse from django.conf import settings -from django.utils.http import urlquote -from django.utils import dateformat from django.utils.functional import wraps from django.db import IntegrityError @@ -20,6 +18,9 @@ from view_logic import create_label, get_performance_info from view_logic import _createEventFromForm from view_logic import create_eel from view_utils import fix_old_creation_request +from view_utils import eventToDict, eventLogToDict, labelToDict +from view_utils import embbEventLogToDict +from view_utils import reverse from translator import handle_uploaded_data from forms import CreateEventForm @@ -33,7 +34,6 @@ import os import urllib import shutil import exceptions -import pytz from utils.vfile import VersionedFile @@ -62,9 +62,6 @@ MAX_FAILED_OPEN_ATTEMPTS = 5 from forms import SimpleSearchForm -from rest_framework.reverse import reverse as rest_framework_reverse -from django.core.urlresolvers import resolve, get_script_prefix - ################################################################## # Stuff for the LigoLwRenderer from glue.ligolw import ligolw @@ -77,66 +74,6 @@ import StringIO use_in(LIGOLWContentHandler) -# Note about reverse() in this file -- there are THREE versions of it here. -# -# SOURCE LOCAL NAME -# django.core.urlresolvers.reverse ==> django_reverse -# rest_framework.reverse.reverse ==> rest_framework_reverse -# reverse defined below ==> reverse -# -# The Django reverse returns relative paths. -# -# The rest framework reverse is basically the same as the Django version -# but will return full paths if the request is passed in using the request kw arg. -# -# The reverse defined below is basically the rest framework reverse, but -# will attempt to deal with multiply-include()-ed url.py-type files with -# different namespaces. (see the comments in the function) - -def reverse(name, *args, **kw): - """Find a URL. Respect where that URL was defined in urls.py - - Allow for a set of URLs to have been include()-ed on multiple URL paths. - - eg urlpatterns = ( - (r'^api1/', include('someapp.urls', app_name="api", namespace="x509")), - (r'^api2/', include('someapp.urls', app_name="api", namespace="shib")), - ...) - - then reverse("api:root", request=self.request) will give the obviously - correct full URL for the URL named "root" in someapp/urls.py. Django's - reverse will pick one URL path and use it no matter what path the - URL resolver flows through and it will do so whether you specify an app_name - or not. - - This function solves that issue. app_name and namespace are required. - The request must be the value at kw['request'] - - Assembled with hints from http://stackoverflow.com/a/13249060 - """ - # XXX rashly assuming app is "api:" brutal. - if type(name) == str and not name.startswith("api:"): - name = "api:"+name - - # Idea is to put 'current_app' into the kw args of reverse - # where current_app is the namespace of the urlpattern we got here from. - # Given that, reverse will find the right patterns in your urlpatterns. - # I do know know why Django does not do this by default. - - # This probably only works if you give app_names which are the same - # and namespaces that are different. - - if 'request' in kw and 'current_app' not in kw: - request = kw['request'] - # For some reason, resolve() does not seem to like the script_prefix. - # So, remove it. - prefix = get_script_prefix() - path = request.path.replace(prefix, '/') - current_app = resolve(path).namespace - kw['current_app'] = current_app - - return rest_framework_reverse(name, *args, **kw) - # # We do not want to handle authentication here because it has already # been taken care of by Apache/Shib or Apache/mod_ssl. Moreover the @@ -435,111 +372,6 @@ class TSVRenderer(BaseRenderer): #================================================================== # Events -SERVER_TZ = pytz.timezone(settings.TIME_ZONE) -def timeToUTC(dt): - if not dt.tzinfo: - dt = SERVER_TZ.localize(dt) - return dateformat.format(dt.astimezone(pytz.utc), settings.GRACE_DATETIME_FORMAT) - -def eventToDict(event, columns=None, request=None): - """Convert an Event to a dictionary so it can be serialized. (ugh)""" - - # XXX Need to understand serializers. - - rv = {} - - graceid = event.graceid() - try: - rv['submitter'] = event.submitter.username - except: - rv['submitter'] = 'Unknown' - - rv['created'] = timeToUTC(event.created) - rv['group'] = event.group.name - rv['graceid'] = graceid - rv['pipeline'] = event.pipeline.name - if event.search: - rv['search'] = event.search.name -# rv['analysisType'] = event.get_analysisType_display() - rv['gpstime'] = event.gpstime - rv['instruments'] = event.instruments - rv['nevents'] = event.nevents - rv['far'] = event.far - rv['likelihood'] = event.likelihood - rv['labels'] = dict([ - (labelling.label.name, - reverse("labels", - args=[graceid, labelling.label.name], - request=request)) - for labelling in event.labelling_set.all()]) - # XXX Try to produce a dictionary of analysis specific attributes. Duck typing. - rv['extra_attributes'] = {} - try: - # GrbEvent - rv['extra_attributes']['GRB'] = { - "ivorn" : event.ivorn, - "author_ivorn" : event.author_ivorn, - "author_shortname" : event.author_shortname, - "observatory_location_id" : event.observatory_location_id, - "coord_system" : event.coord_system, - "ra" : event.ra, - "dec" : event.dec, - "error_radius" : event.error_radius, - "how_description" : event.how_description, - "how_reference_url" : event.how_reference_url, - } - except: - pass - try: - # CoincInspiralEvent - rv['extra_attributes']['CoincInspiral'] = { - "ifos" : event.ifos, - "end_time" : event.end_time, - "end_time_ns" : event.end_time_ns, - "mass" : event.mass, - "mchirp" : event.mchirp, - "minimum_duration" : event.minimum_duration, - "snr" : event.snr, - "false_alarm_rate" : event.false_alarm_rate, - "combined_far" : event.combined_far, - } - except: - pass - try: - # MultiBurstEvent - rv['extra_attributes']['MultiBurst'] = { - "ifos" : event.ifos, - "start_time" : event.start_time, - "start_time_ns" : event.start_time_ns, - "duration" : event.duration, - "peak_time" : event.peak_time, - "peak_time_ns" : event.peak_time_ns, - "central_freq" : event.central_freq, - "bandwidth" : event.bandwidth, - "amplitude" : event.amplitude, - "snr" : event.snr, - "confidence" : event.confidence, - "false_alarm_rate" : event.false_alarm_rate, - "ligo_axis_ra" : event.ligo_axis_ra, - "ligo_axis_dec" : event.ligo_axis_dec, - "ligo_angle" : event.ligo_angle, - "ligo_angle_sig" : event.ligo_angle_sig, - } - except: - pass - rv['links'] = { - "neighbors" : reverse("neighbors", args=[graceid], request=request), - "log" : reverse("eventlog-list", args=[graceid], request=request), - "embb" : reverse("embbeventlog-list", args=[graceid], request=request), - "files" : reverse("files", args=[graceid], request=request), - "filemeta" : reverse("filemeta", args=[graceid], request=request), - "labels" : reverse("labels", args=[graceid], request=request), - "self" : reverse("event-detail", args=[graceid], request=request), - "tags" : reverse("eventtag-list", args=[graceid], request=request), - } - return rv - - class EventList(APIView): """ This resource represents the collection of all candidate events in GraceDB. @@ -660,6 +492,9 @@ class EventList(APIView): #@pipeline_auth_required def post(self, request, format=None): + import logging + logger = logging.getLogger(__name__) + logger.debug("Hello from inside the event creator.") # XXX Deal with POSTs coming in from the old client. # Eventually, we will want to get rid of this check and just let it fail. @@ -922,16 +757,6 @@ class EventNeighbors(APIView): # XXX NOT FINISHED -def labelToDict(label, request=None): - return { - "name" : label.label.name, - "creator" : label.creator.username, - "created" : label.created, - "self" : reverse("labels", - args=[label.event.graceid(), label.label.name], - request=request), - } - class EventLabel(APIView): """Event Label""" authentication_classes = (LigoAuthentication,) @@ -975,36 +800,6 @@ class EventLabel(APIView): #================================================================== # EventLog -# Janky serialization -def eventLogToDict(log, request=None): - uri = None - taglist_uri = None - file_uri = None - if request: - uri = reverse("eventlog-detail", - args=[log.event.graceid(), log.N], - request=request) - taglist_uri = reverse("eventlogtag-list", - args=[log.event.graceid(), log.N], - request=request) - if log.filename: - actual_filename = log.filename - if log.file_version: - actual_filename += ',%d' % log.file_version - filename = urlquote(actual_filename) - file_uri = reverse("files", - args=[log.event.graceid(), filename], - request=request) - - return { - "comment" : log.comment, - "created" : log.created, - "issuer" : log.issuer.username, - "self" : uri, - "tags" : taglist_uri, - "file" : file_uri, - } - class EventLogList(APIView): """Event Log List Resource @@ -1093,9 +888,12 @@ class EventLogList(APIView): # Issue alert. description = "LOG: " + fname = "" if uploadedFile: description = "UPLOAD: '%s' " % uploadedFile.name - issueAlertForUpdate(event, description+message, doxmpp=True, filename=uploadedFile.name) + fname = uploadedFile.name + issueAlertForUpdate(event, description+message, doxmpp=True, + filename=fname, serialized_object=rv) return response @@ -1112,42 +910,6 @@ class EventLogDetail(APIView): #================================================================== # EMBBEventLog (EEL) -# EEL serializer. -def embbEventLogToDict(eel, request=None): - uri = None - if request: - uri = reverse("embbeventlog-detail", - args=[eel.event.graceid(), eel.N], - request=request) - return { - "self" : uri, - "created" : eel.created, - "submitter" : eel.submitter.username, - "group" : eel.group.name, - "instrument" : eel.instrument, - "footprintID" : eel.footprintID, - "waveband" : eel.waveband, - - "ra" : eel.ra, - "dec" : eel.dec, - "raWidth" : eel.raWidth, - "decWidth" : eel.decWidth, - "gpstime" : eel.gpstime, - "duration" : eel.duration, - - "raList" : json.loads('['+eel.raList+']'), - "decList" : json.loads('['+eel.decList+']'), - "raWidthList" : json.loads('['+eel.raWidthList+']'), - "decWidthList" : json.loads('['+eel.decWidthList+']'), - "gpstimeList" : json.loads('['+eel.gpstimeList+']'), - "durationList" : json.loads('['+eel.durationList+']'), - - "eel_status" : eel.get_eel_status_display(), - "obs_status" : eel.get_obs_status_display(), - "comment" : eel.comment, - "extra_info_dict" : eel.extra_info_dict, - } - class EMBBEventLogList(APIView): """EMBB Event Log List Resource @@ -1195,7 +957,8 @@ class EMBBEventLogList(APIView): # Issue alert. description = "New EMBB log entry." - issueAlertForUpdate(event, description, doxmpp=True) + issueAlertForUpdate(event, description, doxmpp=True, + filename="", serialized_object=rv) return response @@ -1267,13 +1030,20 @@ class TagList(APIView): def get(self, request): # Return a list of links to all tag objects. + tag_dict = {} + for tag in Tag.objects.all(): + tag_dict[tag.name] = { + 'displayName': tag.displayName, + 'blessed': tag.name in settings.BLESSED_TAGS + } rv = { # 'tags' : [ reverse("tag-detail", args=[tag.name], # request=request) # for tag in Tag.objects.all() ] # For now, we just output the tag names, since we don't know what # tag-detail should look like. - 'tags' : [ tag.name for tag in Tag.objects.all() ] +# 'tags' : [ tag.name for tag in Tag.objects.all() ] + 'tags' : tag_dict, } return Response(rv) @@ -1893,7 +1663,8 @@ class Files(APIView): try: description = "UPLOAD: {0}".format(filename) - issueAlertForUpdate(event, description, doxmpp=True, filename=filename) + issueAlertForUpdate(event, description, doxmpp=True, + filename=filename, serialized_object = eventLogToDict(logentry)) except: # XXX something should be done here. pass diff --git a/gracedb/management/commands/write_rate_info.py b/gracedb/management/commands/write_rate_info.py new file mode 100644 index 0000000000000000000000000000000000000000..399d3a7b0098e5edc1cc6bb4bf294d2ab7e75bc0 --- /dev/null +++ b/gracedb/management/commands/write_rate_info.py @@ -0,0 +1,13 @@ +from django.conf import settings +from django.core.management.base import NoArgsCommand +from gracedb.reports import rate_data +import json + +class Command(NoArgsCommand): + help = "I write down rate data in JSON to disk. That's about it." + + def handle_noargs(self, **options): + outfile = open(settings.RATE_INFO_FILE, 'w') + json_data = json.dumps(rate_data()) + outfile.write(json_data) + outfile.close() diff --git a/gracedb/migrations/0035_add_mdc_search.py b/gracedb/migrations/0035_add_mdc_search.py new file mode 100644 index 0000000000000000000000000000000000000000..efe6cb292c388fef2fb367710b3cc9f79d023fec --- /dev/null +++ b/gracedb/migrations/0035_add_mdc_search.py @@ -0,0 +1,268 @@ +# -*- 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 + +class Migration(DataMigration): + + def forwards(self, orm): + orm.Search.objects.create(name='MDC') + + def backwards(self, orm): + orm.Search.objects.get(name='MDC').delete() + + 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.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.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.PositiveIntegerField', [], {'null': 'True'}), + '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'}), + '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'}) + } + } + + complete_apps = ['gracedb'] + symmetrical = True diff --git a/gracedb/migrations/0036_auto__add_field_singleinspiral_spin1x__add_field_singleinspiral_spin1y.py b/gracedb/migrations/0036_auto__add_field_singleinspiral_spin1x__add_field_singleinspiral_spin1y.py new file mode 100644 index 0000000000000000000000000000000000000000..162a177c1edec9f40b14ab4b3d78dbb8bf500f6b --- /dev/null +++ b/gracedb/migrations/0036_auto__add_field_singleinspiral_spin1x__add_field_singleinspiral_spin1y.py @@ -0,0 +1,320 @@ +# -*- 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 'SingleInspiral.spin1x' + db.add_column(u'gracedb_singleinspiral', 'spin1x', + self.gf('django.db.models.fields.FloatField')(null=True), + keep_default=False) + + # Adding field 'SingleInspiral.spin1y' + db.add_column(u'gracedb_singleinspiral', 'spin1y', + self.gf('django.db.models.fields.FloatField')(null=True), + keep_default=False) + + # Adding field 'SingleInspiral.spin1z' + db.add_column(u'gracedb_singleinspiral', 'spin1z', + self.gf('django.db.models.fields.FloatField')(null=True), + keep_default=False) + + # Adding field 'SingleInspiral.spin2x' + db.add_column(u'gracedb_singleinspiral', 'spin2x', + self.gf('django.db.models.fields.FloatField')(null=True), + keep_default=False) + + # Adding field 'SingleInspiral.spin2y' + db.add_column(u'gracedb_singleinspiral', 'spin2y', + self.gf('django.db.models.fields.FloatField')(null=True), + keep_default=False) + + # Adding field 'SingleInspiral.spin2z' + db.add_column(u'gracedb_singleinspiral', 'spin2z', + self.gf('django.db.models.fields.FloatField')(null=True), + keep_default=False) + + + def backwards(self, orm): + # Deleting field 'SingleInspiral.spin1x' + db.delete_column(u'gracedb_singleinspiral', 'spin1x') + + # Deleting field 'SingleInspiral.spin1y' + db.delete_column(u'gracedb_singleinspiral', 'spin1y') + + # Deleting field 'SingleInspiral.spin1z' + db.delete_column(u'gracedb_singleinspiral', 'spin1z') + + # Deleting field 'SingleInspiral.spin2x' + db.delete_column(u'gracedb_singleinspiral', 'spin2x') + + # Deleting field 'SingleInspiral.spin2y' + db.delete_column(u'gracedb_singleinspiral', 'spin2y') + + # Deleting field 'SingleInspiral.spin2z' + db.delete_column(u'gracedb_singleinspiral', 'spin2z') + + + 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.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.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.PositiveIntegerField', [], {'null': 'True'}), + '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'}) + } + } + + complete_apps = ['gracedb'] \ No newline at end of file diff --git a/gracedb/models.py b/gracedb/models.py index 60a0f804fa416465d950d6a4fb55651b7cf396c7..338e84a666ff77dce0cab61cea10d14fe6977bd9 100644 --- a/gracedb/models.py +++ b/gracedb/models.py @@ -137,6 +137,8 @@ class Event(models.Model): def graceid(self): if self.group.name == "Test": return "T%04d" % self.id + elif str(self.search) == str("MDC"): + return "M%04d" % self.id elif self.pipeline == "HardwareInjection": return "H%04d" % self.id elif self.group.name == "External": @@ -210,6 +212,8 @@ class Event(models.Model): return e if (id[0] == "E") and (e.group.name == "External"): return e + if (id[0] == "M") and (e.search.name == "MDC"): + return e if (id[0] == "G"): return e raise cls.DoesNotExist("Event matching query does not exist") @@ -721,6 +725,12 @@ class SingleInspiral(models.Model): Gamma7 = models.FloatField(null=True) Gamma8 = models.FloatField(null=True) Gamma9 = models.FloatField(null=True) + spin1x = models.FloatField(null=True) + spin1y = models.FloatField(null=True) + spin1z = models.FloatField(null=True) + spin2x = models.FloatField(null=True) + spin2y = models.FloatField(null=True) + spin2z = models.FloatField(null=True) def end_time_full(self): return LIGOTimeGPS(self.end_time, self.end_time_ns) diff --git a/gracedb/query.py b/gracedb/query.py index b846d1ec82411350bf1923a7cbefffb80119e0dd..db0a2666fe7d66e10ca29e0376d94f67b5ebc91d 100644 --- a/gracedb/query.py +++ b/gracedb/query.py @@ -138,6 +138,12 @@ eidRange = eid + Suppress("..") + eid eidQ = Optional(Suppress(Keyword("eid:"))) + (eid^eidRange) eidQ = eidQ.setParseAction(maybeRange("eid", dbname="id")) +# MDC event id +mid = Suppress("M")+Word("0123456789") +midRange = mid + Suppress("..") + mid +midQ = Optional(Suppress(Keyword("mid:"))) + (mid^midRange) +midQ = midQ.setParseAction(maybeRange("mid", dbname="id")) + # Submitter submitter = QuotedString('"').setParseAction(lambda toks: Q(submitter__username=toks[0])) submitterQ = Optional(Suppress(Keyword("submitter:"))) + submitter @@ -258,7 +264,7 @@ ifoQ = ifoListQ | nifoQ ########################### #q = (ifoQ | hasfarQ | gidQ | hidQ | tidQ | eidQ | labelQ | atypeQ | groupQ | gpsQ | createdQ | submitterQ | runQ | attributeQ).setName("query term") -q = (ifoQ | hasfarQ | gidQ | hidQ | tidQ | eidQ | labelQ | searchQ | pipelineQ | groupQ | gpsQ | createdQ | submitterQ | runQ | attributeQ).setName("query term") +q = (ifoQ | hasfarQ | gidQ | hidQ | tidQ | eidQ | midQ | labelQ | searchQ | pipelineQ | groupQ | gpsQ | createdQ | submitterQ | runQ | attributeQ).setName("query term") #andTheseTags = ["attr"] andTheseTags = ["nevents"] @@ -286,6 +292,8 @@ def parseQuery(s): d["hid"] = d["hid"] & Q(pipeline__name="HardwareInjection") if "eid" in d: d["eid"] = d["eid"] & Q(group__name="External") + if "mid" in d: + d["mid"] = d["mid"] & Q(search__name="MDC") if "id" in d: d["id"] = d["id"] & ~Q(pipeline__name="HardwareInjection") & ~Q(group__name="External") if "id" in d and "hid" in d: diff --git a/gracedb/reports.py b/gracedb/reports.py index 29e11ebdbd07dfd8c60042cae36085b7bc740474..7949c33ceaa49a4adefc2242e037f0c750aa8737 100644 --- a/gracedb/reports.py +++ b/gracedb/reports.py @@ -57,17 +57,25 @@ def histo(request): uptime = None + # Rate information + try: + rate_info = open(settings.RATE_INFO_FILE).read() + except IOError: + rate_info = None + return render_to_response( 'gracedb/histogram.html', {'table': table, 'ifar' : ifar, 'uptime' : uptime, - 'rate' : json.dumps(rate_data(request)), + #'rate' : json.dumps(rate_data(request)), + 'rate' : rate_info, 'url_prefix' : settings.REPORT_INFO_URL_PREFIX, }, context_instance=RequestContext(request)) -def rate_data(request): +#def rate_data(request): +def rate_data(): # XXX there is a better way -- should be using group_by or something. # WAAY too many queries (~300) going on here. now = datetime.now() @@ -76,7 +84,7 @@ def rate_data(request): ts_min = now - 60 * day ts_max = now ts_step = day - window_size = day +# window_size = day types = [ ("total", Q()), diff --git a/gracedb/translator.py b/gracedb/translator.py index 2650765856673751d4d835ee445ccf7c2bad0583..1a596cba49b3077c13955374e11939dff95fd803 100644 --- a/gracedb/translator.py +++ b/gracedb/translator.py @@ -55,6 +55,9 @@ def handle_uploaded_data(event, datafilename, comment="Original Data") log.save() + # XXX If you can manage to get rid of the MBTA .gwf parsing and + # the Omega event parsing, you can deprecate temp_data_loc. It + # has already been removed from the alerts. temp_data_loc = "" warnings = [] diff --git a/gracedb/urls.py b/gracedb/urls.py index 397699b595c888908bbe033d6f589985f36223ca..76dcd7fd72fe148e2672307caee652365a3f3574 100644 --- a/gracedb/urls.py +++ b/gracedb/urls.py @@ -11,17 +11,17 @@ urlpatterns = patterns('gracedb.views', url (r'^$', 'index', name="home-events"), url (r'^create/$', 'create', name="create"), url (r'^search/(?P<format>(json|flex))?$', 'search', name="search"), - url (r'^view/(?P<graceid>[GEHT]\d+)', 'view', name="view"), - url (r'^voevent/(?P<graceid>[GEHT]\d+)', 'voevent', name="voevent"), -# url (r'^skyalert/(?P<graceid>[GEHT]\d+)', 'skyalert', name="skyalert"), - url (r'^neighbors/(?P<graceid>[GEHT]\d+)/\(?(?P<delta1>[-+]?\d+)(,(?P<delta2>[-+]?\d+)\)?)?', 'neighbors', name="neighbors"), - url (r'^(?P<graceid>[GEHT]\d+)$', 'view', name="view2"), - url (r'^(?P<graceid>[GEHT]\d+)/perms/$', 'modify_permissions', name="modify_permissions"), - url (r'^(?P<graceid>[GEHT]\d+)/files/$', 'file_list', name="file_list"), - url (r'^(?P<graceid>[GEHT]\d+)/files/(?P<filename>.*)$', download, name="file"), - url (r'^(?P<graceid>[GEHT]\d+)/log/(?P<num>([\d]*|preview))$', 'logentry', name="logentry"), - url (r'^(?P<graceid>[GEHT]\d+)/embblog/(?P<num>([\d]*|preview))$', 'embblogentry', name="embblogentry"), - url (r'^(?P<graceid>[GEHT]\d+)/log/(?P<num>\d+)/tag/(?P<tagname>\w+)$', 'taglogentry', name="taglogentry"), + url (r'^view/(?P<graceid>[GEHMT]\d+)', 'view', name="view"), + url (r'^voevent/(?P<graceid>[GEHMT]\d+)', 'voevent', name="voevent"), +# url (r'^skyalert/(?P<graceid>[GEHMT]\d+)', 'skyalert', name="skyalert"), + url (r'^neighbors/(?P<graceid>[GEHMT]\d+)/\(?(?P<delta1>[-+]?\d+)(,(?P<delta2>[-+]?\d+)\)?)?', 'neighbors', name="neighbors"), + url (r'^(?P<graceid>[GEHMT]\d+)$', 'view', name="view2"), + url (r'^(?P<graceid>[GEHMT]\d+)/perms/$', 'modify_permissions', name="modify_permissions"), + url (r'^(?P<graceid>[GEHMT]\d+)/files/$', 'file_list', name="file_list"), + url (r'^(?P<graceid>[GEHMT]\d+)/files/(?P<filename>.*)$', download, name="file"), + url (r'^(?P<graceid>[GEHMT]\d+)/log/(?P<num>([\d]*|preview))$', 'logentry', name="logentry"), + url (r'^(?P<graceid>[GEHMT]\d+)/embblog/(?P<num>([\d]*|preview))$', 'embblogentry', name="embblogentry"), + url (r'^(?P<graceid>[GEHMT]\d+)/log/(?P<num>\d+)/tag/(?P<tagname>\w+)$', 'taglogentry', name="taglogentry"), # RDW Aug 2014 #(r'^admin/', include(admin.site.urls)), diff --git a/gracedb/urls_rest.py b/gracedb/urls_rest.py index db2d38a5e1f70728974a5b2b683134a50b71bf74..65b32c934d5488b0bae997b005b9fb11e9236ef8 100644 --- a/gracedb/urls_rest.py +++ b/gracedb/urls_rest.py @@ -28,23 +28,23 @@ urlpatterns = patterns('gracedb.api', # events/[{graceid}[/{version}]] url (r'events/$', EventList.as_view(), name='event-list'), - url (r'events/voevent/(?P<graceid>[GEHT]\d+)$', + url (r'events/voevent/(?P<graceid>[GEHMT]\d+)$', EventVODetail.as_view(), name='event-vo-detail'), - url (r'events/(?P<graceid>[GEHT]\d+)$', + url (r'events/(?P<graceid>[GEHMT]\d+)$', EventDetail.as_view(), name='event-detail'), # Event Log Resources # events/{graceid}/logs/[{logid}] - url (r'events/(?P<graceid>[GEHT]\d+)/log/$', + url (r'events/(?P<graceid>[GEHMT]\d+)/log/$', EventLogList.as_view(), name='eventlog-list'), - url (r'events/(?P<graceid>[GEHT]\d+)/log/(?P<n>\d+)$', + url (r'events/(?P<graceid>[GEHMT]\d+)/log/(?P<n>\d+)$', EventLogDetail.as_view(), name='eventlog-detail'), # EMBB Event Log Resources # events/{graceid}/logs/[{logid}] - url (r'events/(?P<graceid>[GEHT]\d+)/embb/$', + url (r'events/(?P<graceid>[GEHMT]\d+)/embb/$', EMBBEventLogList.as_view(), name='embbeventlog-list'), - url (r'events/(?P<graceid>[GEHT]\d+)/embb/(?P<n>\d+)$', + url (r'events/(?P<graceid>[GEHMT]\d+)/embb/(?P<n>\d+)$', EMBBEventLogDetail.as_view(), name='embbeventlog-detail'), # Tag Resources @@ -53,21 +53,21 @@ urlpatterns = patterns('gracedb.api', # XXX unclear what the tag detail resource should be. #url (r'^tag/(?P<tagname>\w+)$', # TagDetail.as_view(), name='tag-detail'), - url (r'events/(?P<graceid>[GEHT]\d+)/tag/$', + url (r'events/(?P<graceid>[GEHMT]\d+)/tag/$', EventTagList.as_view(), name='eventtag-list'), - url (r'events/(?P<graceid>[GEHT]\d+)/tag/(?P<tagname>\w+)$', + url (r'events/(?P<graceid>[GEHMT]\d+)/tag/(?P<tagname>\w+)$', EventTagDetail.as_view(), name='eventtag-detail'), - url (r'events/(?P<graceid>[GEHT]\d+)/log/(?P<n>\d+)/tag/$', + url (r'events/(?P<graceid>[GEHMT]\d+)/log/(?P<n>\d+)/tag/$', EventLogTagList.as_view(), name='eventlogtag-list'), - url (r'events/(?P<graceid>[GEHT]\d+)/log/(?P<n>\d+)/tag/(?P<tagname>\w+)$', + url (r'events/(?P<graceid>[GEHMT]\d+)/log/(?P<n>\d+)/tag/(?P<tagname>\w+)$', EventLogTagDetail.as_view(), name='eventlogtag-detail'), # Permission Resources - url (r'events/(?P<graceid>[GEHT]\d+)/perms/$', + url (r'events/(?P<graceid>[GEHMT]\d+)/perms/$', EventPermissionList.as_view(), name='eventpermission-list'), - url (r'events/(?P<graceid>[GEHT]\d+)/perms/(?P<group_name>.+)/$', + url (r'events/(?P<graceid>[GEHMT]\d+)/perms/(?P<group_name>.+)/$', GroupEventPermissionList.as_view(), name='groupeventpermission-list'), - url (r'events/(?P<graceid>[GEHT]\d+)/perms/(?P<group_name>.+)/(?P<perm_shortname>\w+)$', + url (r'events/(?P<graceid>[GEHMT]\d+)/perms/(?P<group_name>.+)/(?P<perm_shortname>\w+)$', GroupEventPermissionDetail.as_view(), name='groupeventpermission-detail'), # Event File Resources diff --git a/gracedb/view_logic.py b/gracedb/view_logic.py index f79efe9dcc4cf415eaea096acda73fd99c7eed0a..b7a0c3697711761ae03ed6156f1f0fb3c1dba20c 100644 --- a/gracedb/view_logic.py +++ b/gracedb/view_logic.py @@ -12,6 +12,7 @@ from translator import handle_uploaded_data from utils.vfile import VersionedFile from view_utils import _saveUploadedFile +from view_utils import eventToDict, eventLogToDict from permission_utils import assign_default_event_perms from django.contrib.contenttypes.models import ContentType @@ -28,6 +29,8 @@ import json import datetime def _createEventFromForm(request, form): + import logging + logger = logging.getLogger(__name__) saved = False warnings = [] try: @@ -100,11 +103,12 @@ def _createEventFromForm(request, form): # XXX This reverse will give the web-interface URL, not the REST URL. # This could be a problem if anybody ever tries to use it. # NOTE: The clusterurl method should be considered deprecated. + logger.debug("Bout to issue the alert.") issueAlert(event, #os.path.join(event.clusterurl(), "private", f.name), request.build_absolute_uri(reverse("file", args=[event.graceid(),f.name])), - temp_data_loc, - request.build_absolute_uri(reverse("view", args=[event.graceid()]))) + request.build_absolute_uri(reverse("view", args=[event.graceid()])), + eventToDict(event, request=request)) except Exception, e: warnings += ["Problem issuing an alert (%s)" % e] except Exception, e: @@ -187,7 +191,9 @@ def _createLog(request, graceid, comment, uploadedFile=None): description = "LOG: " if uploadedFile: description = "UPLOAD: '%s' " % uploadedFile.name - issueAlertForUpdate(event, description+comment, doxmpp=True, filename=uploadedFile.name) + issueAlertForUpdate(event, description+comment, doxmpp=True, + filename=uploadedFile.name, + serialized_object=eventLogToDict(logEntry, request=request)) except Exception, e: rdict['error'] = "Failed to save log message: %s" % str(e) diff --git a/gracedb/view_utils.py b/gracedb/view_utils.py index 1deb144ad7768d57dac42104fc0ec60563e20a35..be5ec7006aaf0ce7b710dede07ca723a7d50ec57 100644 --- a/gracedb/view_utils.py +++ b/gracedb/view_utils.py @@ -1,7 +1,9 @@ from django.http import HttpResponse -from django.core.urlresolvers import reverse +from django.core.urlresolvers import reverse as django_reverse +from django.utils import dateformat from django.utils.html import escape, urlize +from django.utils.http import urlquote from django.utils.safestring import mark_safe from utils.vfile import VersionedFile @@ -17,6 +19,278 @@ MAX_QUERY_RESULTS = 1000 GRACEDB_DATA_DIR = settings.GRACEDB_DATA_DIR import json +import pytz + +SERVER_TZ = pytz.timezone(settings.TIME_ZONE) +def timeToUTC(dt): + if not dt.tzinfo: + dt = SERVER_TZ.localize(dt) + return dateformat.format(dt.astimezone(pytz.utc), settings.GRACE_DATETIME_FORMAT) + +#--------------------------------------------------------------------------------------- +#--------------------------------------------------------------------------------------- +# Modified reverse for REST API and serializers below +#--------------------------------------------------------------------------------------- +#--------------------------------------------------------------------------------------- + +from rest_framework.reverse import reverse as rest_framework_reverse +from django.core.urlresolvers import resolve, get_script_prefix + +# Note about reverse() in this file -- there are THREE versions of it here. + # + # SOURCE LOCAL NAME + # django.core.urlresolvers.reverse ==> django_reverse + # rest_framework.reverse.reverse ==> rest_framework_reverse + # reverse defined below ==> reverse + # + # The Django reverse returns relative paths. + # + # The rest framework reverse is basically the same as the Django version + # but will return full paths if the request is passed in using the request kw arg. + # + # The reverse defined below is basically the rest framework reverse, but + # will attempt to deal with multiply-include()-ed url.py-type files with + # different namespaces. (see the comments in the function) +def reverse(name, *args, **kw): + """Find a URL. Respect where that URL was defined in urls.py + + Allow for a set of URLs to have been include()-ed on multiple URL paths. + + eg urlpatterns = ( + (r'^api1/', include('someapp.urls', app_name="api", namespace="x509")), + (r'^api2/', include('someapp.urls', app_name="api", namespace="shib")), + ...) + + then reverse("api:root", request=self.request) will give the obviously + correct full URL for the URL named "root" in someapp/urls.py. Django's + reverse will pick one URL path and use it no matter what path the + URL resolver flows through and it will do so whether you specify an app_name + or not. + + This function solves that issue. app_name and namespace are required. + The request must be the value at kw['request'] + + Assembled with hints from http://stackoverflow.com/a/13249060 + """ + # XXX rashly assuming app is "api:" brutal. + if type(name) == str and not name.startswith("api:"): + name = "api:"+name + + # Idea is to put 'current_app' into the kw args of reverse + # where current_app is the namespace of the urlpattern we got here from. + # Given that, reverse will find the right patterns in your urlpatterns. + # I do know know why Django does not do this by default. + + # This probably only works if you give app_names which are the same + # and namespaces that are different. + + if 'request' in kw and 'current_app' not in kw: + request = kw['request'] + # For some reason, resolve() does not seem to like the script_prefix. + # So, remove it. + prefix = get_script_prefix() + path = request.path.replace(prefix, '/') + current_app = resolve(path).namespace + kw['current_app'] = current_app + + return rest_framework_reverse(name, *args, **kw) + + +#--------------------------------------------------------------------------------------- +#--------------------------------------------------------------------------------------- +# Custom serializers +#--------------------------------------------------------------------------------------- +#--------------------------------------------------------------------------------------- + +def eventToDict(event, columns=None, request=None): + """Convert an Event to a dictionary.""" + rv = {} + graceid = event.graceid() + try: + rv['submitter'] = event.submitter.username + except: + rv['submitter'] = 'Unknown' + + rv['created'] = timeToUTC(event.created) + rv['group'] = event.group.name + rv['graceid'] = graceid + rv['pipeline'] = event.pipeline.name + if event.search: + rv['search'] = event.search.name + rv['gpstime'] = event.gpstime + rv['instruments'] = event.instruments + rv['nevents'] = event.nevents + rv['far'] = event.far + rv['likelihood'] = event.likelihood + rv['labels'] = dict([ + (labelling.label.name, + reverse("labels", + args=[graceid, labelling.label.name], + request=request)) + for labelling in event.labelling_set.all()]) + # XXX Try to produce a dictionary of analysis specific attributes. Duck typing. + rv['extra_attributes'] = {} + try: + # GrbEvent + rv['extra_attributes']['GRB'] = { + "ivorn" : event.ivorn, + "author_ivorn" : event.author_ivorn, + "author_shortname" : event.author_shortname, + "observatory_location_id" : event.observatory_location_id, + "coord_system" : event.coord_system, + "ra" : event.ra, + "dec" : event.dec, + "error_radius" : event.error_radius, + "how_description" : event.how_description, + "how_reference_url" : event.how_reference_url, + } + except: + pass + try: + # CoincInspiralEvent + rv['extra_attributes']['CoincInspiral'] = { + "ifos" : event.ifos, + "end_time" : event.end_time, + "end_time_ns" : event.end_time_ns, + "mass" : event.mass, + "mchirp" : event.mchirp, + "minimum_duration" : event.minimum_duration, + "snr" : event.snr, + "false_alarm_rate" : event.false_alarm_rate, + "combined_far" : event.combined_far, + } + except: + pass + try: + # MultiBurstEvent + rv['extra_attributes']['MultiBurst'] = { + "ifos" : event.ifos, + "start_time" : event.start_time, + "start_time_ns" : event.start_time_ns, + "duration" : event.duration, + "peak_time" : event.peak_time, + "peak_time_ns" : event.peak_time_ns, + "central_freq" : event.central_freq, + "bandwidth" : event.bandwidth, + "amplitude" : event.amplitude, + "snr" : event.snr, + "confidence" : event.confidence, + "false_alarm_rate" : event.false_alarm_rate, + "ligo_axis_ra" : event.ligo_axis_ra, + "ligo_axis_dec" : event.ligo_axis_dec, + "ligo_angle" : event.ligo_angle, + "ligo_angle_sig" : event.ligo_angle_sig, + } + except: + pass + rv['links'] = { + "neighbors" : reverse("neighbors", args=[graceid], request=request), + "log" : reverse("eventlog-list", args=[graceid], request=request), + "embb" : reverse("embbeventlog-list", args=[graceid], request=request), + "files" : reverse("files", args=[graceid], request=request), + "filemeta" : reverse("filemeta", args=[graceid], request=request), + "labels" : reverse("labels", args=[graceid], request=request), + "self" : reverse("event-detail", args=[graceid], request=request), + "tags" : reverse("eventtag-list", args=[graceid], request=request), + } + return rv + +def eventLogToDict(log, request=None): + uri = None + taglist_uri = None + file_uri = None + if request: + uri = reverse("eventlog-detail", + args=[log.event.graceid(), log.N], + request=request) + taglist_uri = reverse("eventlogtag-list", + args=[log.event.graceid(), log.N], + request=request) + if log.filename: + actual_filename = log.filename + if log.file_version: + actual_filename += ',%d' % log.file_version + filename = urlquote(actual_filename) + file_uri = reverse("files", + args=[log.event.graceid(), filename], + request=request) + + # This is purely for convenience in working with the web interface. + tag_names = [tag.name for tag in log.tag_set.all() ]; + + issuer_info = { + "username": log.issuer.username, + "display_name": "%s %s" % (log.issuer.first_name, log.issuer.last_name), + } + + return { + "N" : log.N, + "comment" : log.comment, + "created" : log.created.isoformat(), + "issuer" : issuer_info, + "filename" : log.filename, + "file_version" : log.file_version, + "tag_names" : tag_names, + "self" : uri, + "tags" : taglist_uri, + "file" : file_uri, + } + + +def labelToDict(label, request=None): + return { + "name" : label.label.name, + "creator" : label.creator.username, + "created" : label.created.isoformat(), + "self" : reverse("labels", + args=[label.event.graceid(), label.label.name], + request=request), + } + +# EEL serializer. +def embbEventLogToDict(eel, request=None): + uri = None + if request: + uri = reverse("embbeventlog-detail", + args=[eel.event.graceid(), eel.N], + request=request) + return { + "N" : eel.N, + "self" : uri, + "created" : eel.created.isoformat(), + "submitter" : eel.submitter.username, + "group" : eel.group.name, + "instrument" : eel.instrument, + "footprintID" : eel.footprintID, + "waveband" : eel.waveband, + + "ra" : eel.ra, + "dec" : eel.dec, + "raWidth" : eel.raWidth, + "decWidth" : eel.decWidth, + "gpstime" : eel.gpstime, + "duration" : eel.duration, + + "raList" : json.loads('['+eel.raList+']'), + "decList" : json.loads('['+eel.decList+']'), + "raWidthList" : json.loads('['+eel.raWidthList+']'), + "decWidthList" : json.loads('['+eel.decWidthList+']'), + "gpstimeList" : json.loads('['+eel.gpstimeList+']'), + "durationList" : json.loads('['+eel.durationList+']'), + + "eel_status" : eel.get_eel_status_display(), + "obs_status" : eel.get_obs_status_display(), + "comment" : eel.comment, + "extra_info_dict" : eel.extra_info_dict, + } + + + +#--------------------------------------------------------------------------------------- +#--------------------------------------------------------------------------------------- +# Miscellany +#--------------------------------------------------------------------------------------- +#--------------------------------------------------------------------------------------- def assembleLigoLw(objects): from glue.ligolw import ligolw @@ -115,14 +389,14 @@ def flexigridResponse(request, objects): rows.append( { 'id' : object.id, 'cell': [ '<a href="%s">%s</a>' % - (reverse("view", args=[object.graceid()]), object.graceid()), + (django_reverse("view", args=[object.graceid()]), object.graceid()), #Labels " ".join(["""<span onmouseover="tooltip.show(tooltiptext('%s', '%s', '%s'));" onmouseout="tooltip.hide();" style="color: %s"> %s </span>""" % (label.label.name, label.creator.username, label.created, label.label.defaultColor, label.label.name) for label in object.labelling_set.all()]), # Links to neighbors ', '.join([ '<a href="%s">%s</a>' % - (reverse("view", args=[n.graceid()]), n.graceid()) + (django_reverse("view", args=[n.graceid()]), n.graceid()) for n in object.neighbors() ]), object.group.name, diff --git a/gracedb/views.py b/gracedb/views.py index e27efad687c118d100a6c349a3a64de8e3b3cecb..4f50ec708b8f52dcfeabe6c459d0f0c6e8491707 100644 --- a/gracedb/views.py +++ b/gracedb/views.py @@ -284,6 +284,7 @@ def view(request, event): context['single_inspiral_events'] = list(event.singleinspiral_set.all()) context['neighbor_delta'] = "[%+d,%+d]" % (-5,5) context['SKYMAP_VIEWER_SERVICE_URL'] = settings.SKYMAP_VIEWER_SERVICE_URL + context['BOWER_URL'] = settings.BOWER_URL # XXX This is something of a hack. In the future, we will want to show the # executive user a list of groups and a two column list of radio buttons, showing @@ -761,14 +762,12 @@ def modify_permissions(request, event): # Finished. Redirect back to the event. return HttpResponseRedirect(reverse("view", args=[event.graceid()])) -from hashlib import md5 - # A view to create embb log entries @event_and_auth_required def embblogentry(request, event, num=None): if request.method == "POST": try: - eel = create_eel(request.POST, event, request.user) + create_eel(request.POST, event, request.user) except ValueError, e: return HttpResponseBadRequest(str(e)) except Exception, e: diff --git a/settings/branson.py b/settings/branson.py index 046d67d5aa5df4e980ece03ae8a66ae9cbd4fe19..6f0a22638aaf630e24398f865594d10f3eb84a8b 100644 --- a/settings/branson.py +++ b/settings/branson.py @@ -19,6 +19,9 @@ DATABASES = { STATIC_URL = "/branson-static/" STATIC_ROOT = "/home/branson/gracedbdev/static/" +BOWER_URL = "/bower-static/" +BOWER_ROOT = "/home/branson/bower_components/" + GRACEDB_DATA_DIR = "/home/branson/fake_data" ALERT_EMAIL_FROM = "Dev Alert <root@moe.phys.uwm.edu>" @@ -46,12 +49,14 @@ XMPP_ALERT_CHANNELS = [ ] # Latency histograms. Where they go and max latency to bin. -LATENCY_REPORT_DEST_DIR = "/home/branson/data/latency" +LATENCY_REPORT_DEST_DIR = "/home/branson/fake_data/latency" LATENCY_REPORT_WEB_PAGE_FILE_PATH = LATENCY_REPORT_DEST_DIR + "/latency.inc" # Uptime reporting -UPTIME_REPORT_DIR = "/homebransonbmoe/data/uptime" +UPTIME_REPORT_DIR = "/home/branson/fake_data/uptime" +# Rate file location +RATE_INFO_FILE = "/home/branson/fake_data/rate_info.json" #SITE_ID = 1 diff --git a/settings/default.py b/settings/default.py index 65c17f434b3dd9dc14cf6d6cf5b4cf046f289302..331fc974b5b7bd072cb60a50cd4c61e1cdd10378 100644 --- a/settings/default.py +++ b/settings/default.py @@ -105,6 +105,9 @@ LATENCY_REPORT_WEB_PAGE_FILE_PATH = LATENCY_REPORT_DEST_DIR + "/latency.inc" # Uptime reporting UPTIME_REPORT_DIR = "/home/gracedb/data/uptime" +# Rate file location +RATE_INFO_FILE = "/home/gracedb/data/rate_info.json" + # URL prefix for serving report information (usually plots and tables) REPORT_INFO_URL_PREFIX = "/report_info/" @@ -285,6 +288,9 @@ REST_FRAMEWORK = { STATIC_URL = "/gracedb-static/" STATIC_ROOT = "/home/gracedb/graceproj/static/" +BOWER_URL = "/bower-static/" +BOWER_ROOT = "/home/gracedb/bower_components/" + STATICFILES_FINDERS = ( 'django.contrib.staticfiles.finders.FileSystemFinder', 'django.contrib.staticfiles.finders.AppDirectoriesFinder', @@ -294,10 +300,10 @@ STATICFILES_FINDERS = ( STATICFILES_DIRS = () # Added in order to perform data migrations on the auth app -#SOUTH_MIGRATION_MODULES = { -# 'auth' : 'migrations.auth', -# 'guardian' : 'migrations.guardian', -#} +SOUTH_MIGRATION_MODULES = { + 'auth' : 'migrations.auth', + 'guardian' : 'migrations.guardian', +} SOUTH_TESTS_MIGRATE = False diff --git a/static/css/labeltips.css b/static/css/labeltips.css index d20daba96e9ef694119b7e63e71bbef3342569af..0f798b71020adaa17e17cdb2d331526d3190e33c 100644 --- a/static/css/labeltips.css +++ b/static/css/labeltips.css @@ -1,13 +1,13 @@ #tt { position:absolute; display:block; - background:url(images/tt_left.gif) top left no-repeat; + /* background:url(images/tt_left.gif) top left no-repeat; */ } #tttop { display:block; height:5px; margin-left:5px; - background:url(images/tt_top.gif) top right no-repeat; + /* background:url(images/tt_top.gif) top right no-repeat; */ overflow:hidden; } #ttcont { @@ -21,6 +21,6 @@ display:block; height:5px; margin-left:5px; -background:url(images/tt_bottom.gif) top right no-repeat; +/* background:url(images/tt_bottom.gif) top right no-repeat; */ overflow:hidden; } diff --git a/static/css/style.css b/static/css/style.css index 5984822ad059b37dedf3cc372c28757fce48050a..c3994835bab3346defc09a90ae44374b46876ec9 100644 --- a/static/css/style.css +++ b/static/css/style.css @@ -30,29 +30,31 @@ table.gstlalcbc th {padding:3px;border:none;vertical-align:bottom;} table.figures tr.figrow {text-align:center;} table.figures {width:300px;height:270px;border:1px solid gray;} +/* This was trying to style the title pane buttons. Doesn't work anymore. .tundra.eventDetail .pmTitlePaneClass .dijitOpen .dijitArrowNode { background-repeat: no-repeat; height: 14px; width: 14px; } -.tundra.eventDetail .pmTitlePaneClass .dijitClosed .dijitArrowNode { +.dijitClosed .dijitArrowNode { background-repeat: no-repeat; height: 14px; width: 14px; } -.tundra.eventDetail .pmTitlePaneClass .dijitClosed .dijitArrowNode { +.dijitClosed .dijitArrowNode { background-image: url('../images/plusButton.gif'); background-position: 0px 0px; } -.tundra.eventDetail .pmTitlePaneClass .dijitOpen .dijitArrowNode { +.dijitOpen .dijitArrowNode { background-image: url('../images/minusButton.gif'); background-position: 0px 0px; } +*/ -.tundra.eventDetail .modButtonClass .dijitButtonNode { +.modButtonClass { border: none; border-bottom: none; background-image: none; @@ -60,18 +62,21 @@ table.figures {width:300px;height:270px;border:1px solid gray;} background-color: rgb(200, 200, 200); cursor: pointer; min-width: 20px; + /* min-height: 20px; */ + height: 20px; } -.tundra.eventDetail .modButtonClass.left.dijitButton { - padding: 0px 0px 0px 0px; +.modButtonClass.left { + padding: 2px 0px 2px 0px; margin: 2px 0px 2px 2px; } -.tundra.eventDetail .modButtonClass.right.dijitButton { - padding: 2px 0px 2px 0px; + +.modButtonClass.right { + padding: 2px 2px 2px 0px; margin: 2px 2px 2px 0px; } -.tundra.eventDetail .dijitDisabled .dijitButtonText { +.dijitDisabled .dijitButtonText { color: #000000; } @@ -85,7 +90,7 @@ table.figures {width:300px;height:270px;border:1px solid gray;} float: right; } -.tundra.eventDetail .permButtonClass { +.permButtonClass { font: x-small "Lucida Grande", "Lucida Sans Unicode", geneva, verdana, sans-serif; font-size: 150%; background-color : #ff6666; @@ -370,3 +375,79 @@ table thead th.sorted a { padding-right:13px; } table thead th.ascending a { background:url('../images/arrow-down.gif') right .4em no-repeat; } table thead th.descending a { background:url('../images/arrow-up.gif') right .4em no-repeat; } + +/* Stuff added for dgrid */ +/* XXX FIXME. Note how this assumes the bower stuff will be in your home directory. Not cool. */ +@import "~/bower_components/dgrid/css/dgrid.css"; + +.collapsed .expando { + display: none; +} + +div.dgrid-row.collapsed:hover { + /* background-color: rgb(240,248,255); */ + /* background-color: #ff6666; */ + background-color: #d8bfd8; + cursor: pointer; +} + +.supergrid-row.dgrid-row-odd {background-color:#edf3fe;} +.supergrid-row.dgrid-row-even {background-color:#fff;} + +.dgrid.dgrid-grid.ui-widget.dgird-subgrid { + height: auto; + border: none; +} + +.tundra.eventDetail .dgrid-cell { + border: none; +} + +.dgrid-cell.supergrid-cell { + border-top-style: solid; +} + +.dgrid-cell.subgrid-cell { + border: none; +} + +.dgrid-cell.field-N { + width: 5%; +} + +.dgrid-cell.field-comment { + width: 50%; +} + +.dgrid-cell.field-image { + width: 10%; +} + +th.dgrid-cell { + vertical-align: bottom; +} + +/* Stuff added for new event detail */ + +td.title { + cursor: pointer; +} + +div.expandGlyph { + background-image: url('/bower-static/dijit/themes/tundra/images/spriteArrows.png'); + background-repeat: no-repeat; + background-position: 0px 0px; + vertical-align: middle; + height: 7px; + width: 7px; + padding: 0 0 0 0; +} + +div.expandGlyph.closed { + background-position: -14px 0px; +} + +.expandFormButton { + color: blue; + text-decoration: underline; +} diff --git a/templates/base.html b/templates/base.html index 8b6a188fe5b8ccbc53b8cf5983378a88515c63fb..5d918ff4c2d6558670e0e536f4a987e49fc25b10 100644 --- a/templates/base.html +++ b/templates/base.html @@ -2,12 +2,13 @@ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> <head> +{% block headcontents %} <link rel="stylesheet" href="{{STATIC_URL}}css/style.css" /> <title>GraceDb | {% block title %}{% endblock %}</title> <!-- START TESTING --> <script type="text/javascript"> function changeTime(obj, label) { - var timetype= obj[obj.selectedIndex].value; + var timetype = obj.get("value"); if (timetype=="") { return; } var times = document.getElementsByName("time-"+label); for (i=0; i<times.length; i++) { @@ -16,6 +17,7 @@ function changeTime(obj, label) { } } </script> +{% endblock %} <!-- END TESTING --> {% block jscript %}{% endblock %} </head> diff --git a/templates/gracedb/eel_form_frag.html b/templates/gracedb/eel_form_frag.html new file mode 100644 index 0000000000000000000000000000000000000000..ed32a853509c77e03b096c4d4944fad411546b9e --- /dev/null +++ b/templates/gracedb/eel_form_frag.html @@ -0,0 +1,180 @@ +<div data-dojo-type="dijit/form/Form" id="eelFormContainer" +data-dojo-id="eelFormContainer" encType="multipart/form-data" action="" +method=""> +<script> + require(["dojo/parser", "dijit/form/Form", "dijit/form/Button", "dijit/form/ValidationTextBox", "dijit/form/DateTextBox"]); + </script> + +<script type="dojo/on" data-dojo-event="reset"> +return confirm('Press OK to reset widget values'); +</script> +<script type="dojo/on" data-dojo-event="submit"> +if(this.validate()){ +return confirm('Form is valid, press OK to submit'); +}else{ +alert('Form contains invalid data. Please correct first'); +return false; +} +return true; +</script> + +<form method="POST" action="{% url "embblogentry" object.graceid "" %}"> +<table> + +<tr><td><a href=# onclick="alert('Define here what subclass of report this is:\n FOOTPRINT: There is or could be image data taken of the sky, with a specific instrument, at a specific time, in a specific waveband, as followup of a specific LVC alert.\n SOURCE: There is a candidate source at the given location, that could be associated with the given LVC alert.\n COMMENT: The primary purpose of this report is a human-written comment concerning the given LVC alert.'); return false;"> +What type of report is this?</a></td> <td><select name="eel_status"> +<option value="FO">FOOTPRINT</option> +<option value="SO">SOURCE</option> +<option value="CO">COMMENT</option> +</select></td></tr> + +<tr><td><a href=# onclick="alert('There is also Observation Status which is:\n OBSERVATION: has been done.\n PREDICTION: this report represents intent or future observation.\n TEST: This EEL is part of a test and has no astrophysical significance.\n NOT APPLICABLE: It is neither observation nor prediction.'); return false;"> +Observation Status</a></td> <td><select name="obs_status"> +<option value="OB">OBSERVATION</option> +<option value="PR">PREDICTION</option> +<option value="TE">TEST</option> +<option value="NA">NOT APPLICABLE</option> +</select></td></tr> + +<tr><td><a href=# onclick="alert('Group with which the LSC has signed a trust agreement, hereby providing data +under its trust (required).'); return false;"> +Which MOU Group provides this report?</a></td> <td><select name="group"> +{% for g in groups %} +<option value="{{ g }}">{{ g }}</option> +{% endfor %} </select> </td> </tr> + +<tr><td><a href=# onclick="alert('An ID from the data owner to identify this footprint (optional).');return false;"> +Your reference</a></td> <td><input type="text" name="footprintID"/></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> + +<tr><td><a href=# onclick="alert('The name of the photon detector whose observational metadata is being recorded here (optional).');return false;"> +Instrument</a></td> <td><input type="text" name="instrument"> </td> </tr> + +<tr><td><a href=# onclick="alert('Specify waveband of the observation. Vocabulary is taken from the UCD vocabulary, a Virtual Observatory standard. Waveband can be either wide (eg Radio) or narrow (eg Radio 200-400 MHz).');return false;"> +Waveband</a></td> <td colspan=2><select name="waveband"> +<option value="em.gamma">Gamma rays part of the spectrum</option> +<option value="em.gamma.soft">Soft gamma ray (120 - 500 keV)</option> +<option value="em.gamma.hard">Hard gamma ray (>500 keV)</option> +<option value="em.X-ray">X-ray part of the spectrum</option> +<option value="em.X-ray.soft">Soft X-ray (0.12 - 2 keV)</option> +<option value="em.X-ray.medium">Medium X-ray (2 - 12 keV)</option> +<option value="em.X-ray.hard">Hard X-ray (12 - 120 keV)</option> +<option value="em.UV">Ultraviolet part of the spectrum</option> +<option value="em.UV.10-50nm">Ultraviolet between 10 and 50 nm</option> +<option value="em.UV.50-100nm">Ultraviolet between 50 and 100 nm</option> +<option value="em.UV.100-200nm">Ultraviolet between 100 and 200 nm</option> +<option value="em.UV.200-300nm">Ultraviolet between 200 and 300 nm</option> +<option value="em.UV.FUV">Far-Infrared, 30-100 microns</option> +<option value="em.opt">Optical part of the spectrum</option> +<option value="em.opt.U">Optical band between 300 and 400 nm</option> +<option value="em.opt.B">Optical band between 400 and 500 nm</option> +<option value="em.opt.V">Optical band between 500 and 600 nm</option> +<option value="em.opt.R">Optical band between 600 and 750 nm</option> +<option value="em.opt.I">Optical band between 750 and 1000 nm</option> +<option value="em.IR">Infrared part of the spectrum</option> +<option value="em.IR.NIR">Near-Infrared, 1-5 microns</option> +<option value="em.IR.J">Infrared between 1.0 and 1.5 micron</option> +<option value="em.IR.H">Infrared between 1.5 and 2 micron</option> +<option value="em.IR.K">Infrared between 2 and 3 micron</option> +<option value="em.IR.MIR">Medium-Infrared, 5-30 microns</option> +<option value="em.IR.3-4um">Infrared between 3 and 4 micron</option> +<option value="em.IR.4-8um">Infrared between 4 and 8 micron</option> +<option value="em.IR.8-15um">Infrared between 8 and 15 micron</option> +<option value="em.IR.15-30um">Infrared between 15 and 30 micron</option> +<option value="em.IR.30-60um">Infrared between 30 and 60 micron</option> +<option value="em.IR.60-100um">Infrared between 60 and 100 micron</option> +<option value="em.IR.FIR">Far-Infrared, 30-100 microns</option> +<option value="em.mm">Millimetric part of the spectrum</option> +<option value="em.mm.1500-3000GHz">Millimetric between 1500 and 3000 GHz</option> +<option value="em.mm.750-1500GHz">Millimetric between 750 and 1500 GHz</option> +<option value="em.mm.400-750GHz">Millimetric between 400 and 750 GHz</option> +<option value="em.mm.200-400GHz">Millimetric between 200 and 400 GHz</option> +<option value="em.mm.100-200GHz">Millimetric between 100 and 200 GHz</option> +<option value="em.mm.50-100GHz">Millimetric between 50 and 100 GHz</option> +<option value="em.mm.30-50GHz">Millimetric between 30 and 50 GHz</option> +<option value="em.radio">Radio part of the spectrum</option> +<option value="em.radio.12-30GHz">Radio between 12 and 30 GHz</option> +<option value="em.radio.6-12GHz">Radio between 6 and 12 GHz</option> +<option value="em.radio.3-6GHz">Radio between 3 and 6 GHz</option> +<option value="em.radio.1500-3000MHz">Radio between 1500 and 3000 MHz</option> +<option value="em.radio.750-1500MHz">Radio between 750 and 1500 MHz</option> +<option value="em.radio.400-750MHz">Radio between 400 and 750 MHz</option> +<option value="em.radio.200-400MHz">Radio between 200 and 400 MHz</option> +<option value="em.radio.100-200MHz">Radio between 100 and 200 MHz</option> +<option value="em.radio.20-100MHz">Radio between 20 and 100 MHz</option> + </select></td></tr> +</table> +<hr/> +<script> +function showRectanglesCheckbox(){ + var s = document.getElementById('showRectangles'); + showRectangles = s.checked; + if(showRectangles){ document.getElementById('rectanglesForm').style.display = "block" + } else { document.getElementById('rectanglesForm').style.display = "none" + } + redrawAll(); + return false; +} +</script> +<input id="showRectangles" type="checkbox" onclick="showRectanglesCheckbox()" />add Sky Footprints<br/> +<div id=rectanglesForm style="display: none;"> +<table> +<tr><td><a href=# onclick="alert('RA and Dec specify a center point of a rectangle that is aligned equatorially. Or list of centers. They must be in decimal degrees 0<=RA<=360 -90<=Dec<=90, in the J2000 frame.');return false;"> +RA (decimal degrees)</a></td> <td><input type="text" name="raList" value="" size=80/></td></tr> + +<tr><td><a href=# onclick="alert('RA and Dec specify a center point of a rectangle that is aligned equatorially. Or list of centers. They must be in decimal degrees 0<=RA<=360 -90<=Dec<=90, in the J2000 frame.');return false;"> +Dec (decimal degrees)</a></td> <td><input type="text" name="decList" value="" size=80/></td></tr> + +<!-- +<script language="javascript" type="text/javascript" src="https://losc.ligo.org/s/js/gpstimeutil.js"></script> +<script language="javascript" type="text/javascript" src="https://losc.ligo.org/s/js/sexg.js"></script> +--> + +<tr><td><a href=# onclick="alert('The time at the center of a time interval during which the observation was taken. Or list of times.');return false;"> +GPStime</a></td> +<td><input type="text" name="gpstimeList" value="" size=80/></td> +<!-- +<td><input onKeyUp="return TIMEcopy(2,1);" id="TIMEgps1" name="gpstimeList" maxlength="12" size="20" value="1000000000"/></td> +<td><input onKeyUp="return TIMEcopy(1,1);" id="TIMEiso1" name="TIMEiso1" maxlength="25" size="25" value="2011-09-14T01:46:25"/></td> +<td><span id="TIMEerr1" STYLE="font: 12px Arial; color:red">OK</span><br/></td> +--> +</tr> + +<tr><td><a href=# onclick="alert('RAWidth and DecWidth specify the size of a a rectangle that is aligned equatorially. Thus the edge of the box is distant from the center by half of the width.');return false;"> +RAwidth (decimal degrees)</a></td> <td><input type="text" name="raWidthList" value=""/></td></tr> + +<tr><td><a href=# onclick="alert('RAWidth and DecWidth specify the size of a a rectangle that is aligned equatorially. Thus the edge of the box is distant from the center by half of the width.');return false;"> +Decwidth (decimal degrees)</a></td> <td><input type="text" name="decWidthList" value=""/></td></tr> + +<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> + +</table> +</div> +<hr/> +<script> +function showJsonCheckbox(){ + var s = document.getElementById('showJson'); + showJson = s.checked; + if(showJson){ document.getElementById('jsonForm').style.display = "block" + } else { document.getElementById('jsonForm').style.display = "none" + } + redrawAll(); + return false; +} +</script> +<input id="showJson" type="checkbox" onclick="showJsonCheckbox()" />add JSON data<br/> +<div id=jsonForm style="display: none;"> +<table> +<tr><td><a href=# onclick="alert('This section allows for machine-readable data to be included in a flexible way. any key can be any value in a JSON dictionary. There is a useful validator at http://jsonlint.com.');return false;"> +JSON data</a></td> <td colspan=2><textarea name="extra_info_dict" rows="20" cols="50"></textarea></td></tr> +</table> +</div> +<hr/> +<input type="submit" value="Submit EMBB Report"/> +</form> + +</div> + diff --git a/templates/gracedb/event_detail.html b/templates/gracedb/event_detail.html index 8881fb24f8d5209db62ee689c158acd0eb337fa7..a1d45cfc72b458b83c8580a1a71d8889e69d4ff7 100644 --- a/templates/gracedb/event_detail.html +++ b/templates/gracedb/event_detail.html @@ -3,578 +3,44 @@ {% load scientific %} {% load sanitize_html %} {% load logtags %} -{% block title %}View {{ object.graceid }}{% endblock %} {% block heading %}{% endblock %} {% block bodyattrs %}class="tundra eventDetail"{% endblock %} {% block jscript %} - {# XXX http/https depending on this pages' protocol #} - <script type="text/javascript" - src="https://c328740.ssl.cf1.rackcdn.com/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML"> - </script> - <script src="http{% if request.is_secure %}s{% endif %}://ajax.googleapis.com/ajax/libs/dojo/1.8.1/dojo/dojo.js" type="text/javascript"></script> - <script src="{{STATIC_URL}}js/labeltips.js" type="text/javascript"></script> - <link rel="stylesheet" href="{{STATIC_URL}}css/labeltips.css"> - <style> - .emptyIcon{ - background-image: none; - background-position: center center; - background-repeat: no-repeat; - height: 10px; - width: 10px; - } - </style> - <script type="text/javascript"> - //------------------------------------------------------------------------------------------------------------------------ - //------------------------------------------------------------------------------------------------------------------------ - // Generate an array, the elements of which contain the inner html for each thematic digest - //------------------------------------------------------------------------------------------------------------------------ - //------------------------------------------------------------------------------------------------------------------------ - - function divFunction() { - } - - function getDigests() { - var digests = new Array(); -{% for tag in object.getAvailableTags %} - {% if tag.name in blessed_tags %} - digest = new Object(); - digest.title = "{{tag|tagUnicode}}"; - digest.contents = ""; - digest.hasfigs = false; - digest.figure_container_id = ""; - - // A section for the images - var dc = ""; - {% if object|logsForTagHaveImage:tag.name %} - digest.hasfigs = true; - digest.figure_container_id = "{{tag.name}}_figure_container"; - dc += ' <div id="{{tag.name}}_figure_container"> \n'; - dc += ' <table class="figure_container"> \n'; - dc += ' <tr> \n'; - {% for log in object|getLogsForTag:tag.name %} - {% if log.hasImage %} - dc += ' <td> \n'; - dc += ' <table class="figures"> \n'; - dc += ' <tr class="figrow"> \n'; - dc += ' <td> <a href="{{ log.fileurl }}" dojoType="dojox.image.LightboxNano"><img height="180" src="{{ log.fileurl }}"></a> \n'; - dc += ' </td> \n'; - dc += ' </tr> \n'; - dc += ' <tr> \n'; - dc += ' <td> {{log.comment|sanitize|escapejs}} \n'; - {% if log.fileurl %} - dc += ' <a href="{{log.fileurl}}">{{log.filename}}.</a> \n'; - {% endif %} - dc += ' Submitted by {{log.issuer}} on {{log.created}} \n'; - dc += ' </td> \n'; - dc += ' </tr> \n'; - dc += ' </table> \n'; - dc += ' </td> \n'; - {% endif %} - {% endfor %} - dc += ' </tr> \n'; - dc += ' </table> \n'; - dc += ' </div> \n'; - digest.contents += dc; - {% endif %} - - // A section for the text-only captions - {% if object|logsForTagHaveText:tag.name %} - var dc = ''; - dc += ' <table class="event"> \n'; - dc += ' <tr> \n'; - dc += ' <th>No.</th> \n'; - dc += ' <th>{{ "logtime"|timeselect:"utc"|escapejs }} Log Entry Created</th> \n'; - dc += ' <th>Submitter</th> \n'; - dc += ' <th>Comment</th> \n'; - dc += ' </tr> \n'; - {% for log in object|getLogsForTag:tag.name %} - {% if not log.hasImage %} - dc += ' <tr class="{% cycle 'odd' 'even'%}"> \n'; - dc += ' <td>{{log.N}}</td> \n'; - dc += ' <td>{{log.created|multiTime:"logtime"|escapejs}}</td> \n'; - dc += ' <td>{{log.issuer.first_name}} {{log.issuer.last_name}}</td> \n'; - dc += ' <td>{{log.comment|sanitize|escapejs}} \n'; - {% if log.fileurl %} - dc += ' <a href="{{log.fileurl}}">{{log.filename}}</a> \n'; - // If this is a skymap.json file, we want a button. - {% if log.filename == "skymap.json" %} - dc += ' <button type="button" data-dojo-type="dijit/form/Button" id="sV_button" class="modButtonClass">View in skymapViewer!</button> \n'; - {% endif %} - {% endif %} - dc += ' </td> \n'; - dc += ' </tr> \n'; - {% endif %} - {% endfor %} - dc += '</table>'; - digest.contents += dc; - {% endif %} - digests.push(digest); - {% endif %} -{% endfor %} - return digests; - } - - //------------------------------------------------------------------------------------------------------------------------ - //------------------------------------------------------------------------------------------------------------------------ - // Generate the html table for the full audit log - //------------------------------------------------------------------------------------------------------------------------ - //------------------------------------------------------------------------------------------------------------------------ - function getAuditLogContents() { - logs = new Array(); - tags = new Array(); - var ret = ""; - ret += ' <table class="event"> \n'; - ret += ' <tr> \n'; - ret += ' <th>No.</th> \n'; - ret += ' <th>{{ "logtime"|timeselect:"utc"|escapejs }} Log Entry Created</th> \n'; - ret += ' <th>Submitter</th> \n'; - ret += ' <th>Comment</th> \n'; - ret += ' <th> </th> \n'; - ret += ' </tr>\n'; -{% for log in object.eventlog_set.iterator %} - log = new Object(); - log.n = {{log.N}}; - log.button_id = "addtag_{{log.N}}"; - logs.push(log); - ret += ' <tr class="{% cycle 'odd' 'even'%}"> \n'; - ret += ' <td>{{log.N}} \n'; - ret += ' <td>{{log.created|multiTime:"logtime"}}</td> \n'; - ret += ' <td>{{log.issuer.first_name}} {{ log.issuer.last_name}}</td> \n'; - ret += ' <td> \n'; - ret += ' <div class="tagButtonContainerClass"> \n' - {% if object.getAvailableTags %} - {%for tag in log.tag_set.all %} - tag = new Object(); - tag.name = "{{tag.name}}"; - tag.button_id = "del_button_{{log.N}}_{{tag.name}}"; - tag.n = "{{log.N}}"; - tags.push(tag); - ret += ' <div class="tagDelButtonDivClass"> \n'; - ret += ' <button type="button" data-dojo-type="dijit/form/Button" id="' + tag.button_id + '" class="modButtonClass left"> \n'; - ret += ' × \n'; - ret += ' </button><button type="button" data-dojo-type="dijit/form/Button" class="modButtonClass right" disabled> \n'; - ret += ' ' + tag.name + ' \n'; - ret += ' </button> \n'; - ret += ' </div> \n'; - {% endfor %} - {% endif %} - ret += ' <button type="button" data-dojo-type="dijit/form/Button" id="addtag_{{log.N}}" class="modButtonClass">+</button> \n'; - ret += ' </div> \n'; - - ret += ' {{log.comment|sanitize|escapejs}} \n'; - {% if log.fileurl %} - ret += ' <a href="{{log.fileurl}}">{{log.filename}}</a> \n'; - {% endif %} - ret += ' </td> \n'; - ret += ' <td> \n'; - {% if log.hasImage %} - ret += ' <a href="{{ log.fileurl }}" dojoType="dojox.image.LightboxNano"><img height="60" src="{{ log.fileurl }}"></a> \n'; - {% endif %} - ret += ' </td> \n'; - ret += ' </tr> \n'; -{% endfor %} - ret += ' </table>'; - return {'logContents':ret, 'logs':logs, 'tags':tags}; - } - - //------------------------------------------------------------------------------------------------------------------------ - //------------------------------------------------------------------------------------------------------------------------ - // The main Dojo action happens here. - //------------------------------------------------------------------------------------------------------------------------ - //------------------------------------------------------------------------------------------------------------------------ - require(["dojo/dom", - "dojo/dom-style", - "dojo/parser", - "dojo/on", - "dojo/mouse", - "dojo/request", - "dojo/dom-form", - "dojo/dom-construct", - "dijit/Editor", - "dojox/editor/plugins/Save", - "dojox/editor/plugins/Preview", - "dijit/form/Button", - "dijit/form/TextBox", - "dijit/form/Form", - "dijit/TitlePane", - "dijit/Tooltip", - "dojox/layout/ScrollPane", - "dojo/store/Memory", - "dijit/form/ComboBox", - "dijit/Dialog", - "dojox/image/LightboxNano", - "dijit/_editor/plugins/TextColor", - "dijit/_editor/plugins/LinkDialog", - "dijit/_editor/plugins/ViewSource", - "dijit/_editor/plugins/NewPage", - "dijit/_editor/plugins/FullScreen", - "dojo/domReady!"], function (dom, domStyle, parser, on, mouse, request, domForm, domConstruct, Editor, Save, Preview, - Button, TextBox, Form, TitlePane, Tooltip, ScrollPane, Memory, ComboBox, Dialog) { - - parser.parse(); - - //-------------------------------------------------------------------------------------------------------------------- - // Remember the blessed tags, if we have them. - //-------------------------------------------------------------------------------------------------------------------- -{% if blessed_tags %} - var blessedTagStore = new Memory({ - data: [ - {% for bt in blessed_tags %} - {name:"{{bt}}"}, - {% endfor %} - ] - }); -{% endif %} - - //-------------------------------------------------------------------------------------------------------------------- - // a utility function to get the callbacks for when the tag delete button is clicked. - //-------------------------------------------------------------------------------------------------------------------- - function getTagDelCallback(tag) { - return function() { - var tagUrl = "{% url "taglogentry" object.graceid "000" "temp" %}" - tagUrl = tagUrl.replace("000",tag.n); - tagUrl = tagUrl.replace("temp",tag.name); - var tagResultDialog = new Dialog({ style: "width: 300px" }); - var actionBar = domConstruct.create("div", { "class": "dijitDialogPaneActionBar" }); - var tbnode = domConstruct.create("div", { - style: "margin: 0px auto 0px auto; text-align: center;" - }, actionBar); - var tagButton = new Button({ - label: "Ok", - onClick: function(){ - tagResultDialog.hide(); - }}).placeAt(tbnode); - request.del(tagUrl).then( - function(text){ - tagResultDialog.set("content", text); - domConstruct.place(actionBar, tagResultDialog.containerNode); - tagResultDialog.show(); - location.reload(true); - }, - function(error){ - tagResultDialog.set("content", "Error: " + error); - domConstruct.place(actionBar, tagResultDialog.containerNode); - tagResultDialog.show(); - }); - } - } - - //-------------------------------------------------------------------------------------------------------------------- - // a utility function to get the callbacks for when the tag add button is clicked. - //-------------------------------------------------------------------------------------------------------------------- - function getTagAddCallback(log) { - return function() { - // Create the tag result dialog. - var tagResultDialog = new Dialog({ style: "width: 300px" }); - var actionBar = domConstruct.create("div", { "class": "dijitDialogPaneActionBar" }); - var tbnode = domConstruct.create("div", { - style: "margin: 0px auto 0px auto; text-align: center;" - }, actionBar); - var tagButton = new Button({ - label: "Ok", - onClick: function(){ - tagResultDialog.hide(); - } - }).placeAt(tbnode); - - // Create the form - addTagForm = new Form(); - var msg = "<p> Choose a tag \ - name from the dropdown menu or enter a new one. If you are \ - creating a new tag, please also provide a display name. </p>"; - domConstruct.create("div", {innerHTML: msg} , addTagForm.containerNode); - - // Form for tagging existing log messages. - new ComboBox({ - name: "existingTagSelect", - value: "", - {% if blessed_tags %} - store: blessedTagStore, - searchAttr: "name" - {% endif %} - }).placeAt(addTagForm.containerNode); - - new TextBox({ - name: "tagDispName", - }).placeAt(addTagForm.containerNode); - - new Button({ - type: "submit", - label: "OK", - }).placeAt(addTagForm.containerNode); - - // Create the dialoge - addTagDialog = new Dialog({ - title: "Add Tag", - content: addTagForm, - style: "width: 300px" - }); - - // Define the form on submit handler - on(addTagForm, "submit", function(evt) { - evt.stopPropagation(); - evt.preventDefault(); - formData = addTagForm.getValues(); - var tagName = formData.existingTagSelect; - var tagDispName = formData.tagDispName; - var tagUrl = "{% url "taglogentry" object.graceid "000" "temp" %}" - tagUrl = tagUrl.replace("000",log.n); - tagUrl = tagUrl.replace("temp",tagName); - - request.post(tagUrl, { - data: {displayName: tagDispName} - }).then( - function(text){ - tagResultDialog.set("content", text); - domConstruct.place(actionBar, tagResultDialog.containerNode); - tagResultDialog.show(); - location.reload(true); - }, - function(error){ - tagResultDialog.set("content", "Error: " + error); - domConstruct.place(actionBar, tagResultDialog.containerNode); - tagResultDialog.show(); - } - ); - addTagDialog.hide(); - }); - - // show the dialog - addTagDialog.show(); - } - } - - //-------------------------------------------------------------------------------------------------------------------- - // Main processing begins here - //-------------------------------------------------------------------------------------------------------------------- - - // If we have blessed tags *and* some log messages have been tagged, - // we will want some TitlePanes. -{% if blessed_tags and object.getAvailableTags %} - var doingPanes = true; -{% else %} - var doingPanes = false; -{% endif %} - - var ret = getAuditLogContents(); - var auditLogContents = ret.logContents; - var tags = ret.tags; - - annotationsDiv = dom.byId("annotations"); - - //------------------------------------------------------------------- - // Set up the annotations section, depending on whether we're doing - // title panes with annotation digests. - //------------------------------------------------------------------- - if (doingPanes) { - // Go and get me an array of objects. - var digests = getDigests(); - for (var i = 0; i<digests.length; i++) { - tp = new dijit.TitlePane({title:digests[i].title, - content:digests[i].contents, - open:true}); - annotationsDiv.appendChild(tp.domNode); - }; - - - // Handle event log seperately. It will be closed by default. - tp = new dijit.TitlePane({title:"Full Event Log", - content:auditLogContents, - open:false}); - annotationsDiv.appendChild(tp.domNode); - - - // Deal with the figure containers. - var figure_scrollpanes = new Array(); - for (var i = 0; i<digests.length; i++) { - if (digests[i].hasfigs) { - figure_scrollpanes.push(new dojox.layout.ScrollPane({ - orientation:"horizontal", - style:"overflow:hidden;"}, - digests[i].figure_container_id)); - } - } - } else { -{% if object.eventlog_set.count %} - annotationsDiv.innerHTML += auditLogContents; -{% endif %} - } - - //------------------------------------------------------------------- - // Now that the annotations section has been added to the dom, we - // can work on its functionality. - //------------------------------------------------------------------- - var logtitle = dom.byId("logmessagetitle"); - var logtext = dom.byId("newlogtext"); - - var editor_div = dom.byId("editor"); - var preview_div = dom.byId("previewer"); - - // A pane holder for the form that will tag new log messages. - // I need it up here because we're going to integrate it with the - // editor components. - dojo.style(preview_div, { 'display':'none'}); - dojo.style(editor_div, { 'display':'none'}); - - var button_element = dojo.create('button'); - dojo.place(button_element, logtitle, "right"); - var button = new Button({ - label: "Add Log Entry", - state: "add", - onClick: function(){ - if (this.state == 'add') { - dojo.style(editor_div, {'display':'block'}); - button.set('label', "Cancel Log Entry"); - button.set('state', 'cancel'); - editor.focus(); - } - else { - dojo.style(editor_div, {'display':'none'}); - dojo.style(preview_div, {'display':'none'}); - button.set('label', "Add Log Entry"); - button.set('state', 'add'); - editor.set('value',''); - } - }, - }, button_element); - - var savebutton = new Save({ - url: "{% url "logentry" object.graceid "" %}", - onSuccess: function (resp, ioargs) { - //this.inherited(resp, ioargs); - this.button.set("disabled", false); - location.reload(true); - }, - onError: function (error, ioargs) { - //this.inherited(error, ioargs); - this.button.set("disabled", false); - alert("o hai " + error); - }, - save: function(postdata) { - var newTagName = "analyst_comments"; - var postArgs = { - url: this.url, - content: { comment: postdata, tagname: newTagName }, - handleAs: "json" - }; - this.button.set("disabled", true); - var deferred = dojo.xhrPost(postArgs); - deferred.addCallback(dojo.hitch(this, this.onSuccess)); - deferred.addErrback(dojo.hitch(this, this.onError)); - // Call whatever function is necessary to attach the tag - // or add to the postdata and handle in the django view? - } - }); - - var previewbutton = new Preview({ - _preview: function(){ - var content = this.editor.get("value"); - preview_div.innerHTML = editor.get('value'); - dojo.style(preview_div, { - 'display':'block', - 'border': ".2em solid #900", - 'padding': '10px' - }); - MathJax.Hub.Queue(["Typeset",MathJax.Hub, preview_div]); - } - }); - - var editor = new Editor({ - extraPlugins : ['foreColor','hiliteColor','|','createLink', - 'insertImage','fullscreen','viewsource','newpage', '|', previewbutton, savebutton] - }, editor_div); - - // Connect up the tag addition buttons and add helptext. - for (var i = 0; i<logs.length; i++) { - log = logs[i]; - on(dom.byId(log.button_id), "click", getTagAddCallback(log)); - new Tooltip({ connectId: log.button_id, label: "tag this log message"}); - } - - // Connect up the tag deletion buttons and add helptext. - for (var i = 0; i<tags.length; i++) { - tag = tags[i]; - on(dom.byId(tag.button_id), "click", getTagDelCallback(tag)); - new Tooltip({ connectId: tag.button_id, label: "delete this tag"}); - } - - - // Handle the post to skymapViewer button. - var sV_button = dom.byId("sV_button"); - if (sV_button) { - on(sV_button, "click", function() { - var embblog_json_url = "{% url "api:embbeventlog-list" object.graceid %}"; - var embblog_json; - dojo.xhrGet({ - url: embblog_json_url + "?format=json", - async: true, - load: function(embblog_json) { - - - // construct skymap.json url. - var skymap_json_url = "{% url "file" object.graceid "skymap.json" %}" - // fetch JSON content. - dojo.xhrGet({ - url: skymap_json_url, - load: function(result) { - // Find the form and set its value to the appropriate JSON - sV_form = dom.byId("sV_form"); - // Shove the skymap.json contents into the value for the second form field. - sV_form.elements[1].value = result; - sV_form.elements[2].value = embblog_json; - // Submit the form, which takes the user to the skymapViewer server. - sV_form.submit(); - } - }); // end of inside ajax - } - }); // end of outside ajax - }); - } - - }); +<!-- <script charset="utf-8" src="{{STATIC_URL}}js/labeltips.js" type=text/javascript"></script> --> +<link rel="stylesheet" href="{{STATIC_URL}}css/labeltips.css"> +<script src="{{BOWER_URL}}moment/moment.js"></script> +<script src="{{BOWER_URL}}moment-timezone/moment-timezone-with-data-2010-2020.min.js"></script> +<script src="{{BOWER_URL}}dojo/dojo.js" data-dojo-config="async: true"></script> +<!-- Styles for dgrid --> +<!-- <link rel="stylesheet" href="{{BOWER_URL}}dgrid/css/dgrid.css" /> --> +<!-- Styles for the editor components --> +<link rel="stylesheet" href="{{BOWER_URL}}dojox/editor/plugins/resources/css/PageBreak.css" /> +<link rel="stylesheet" href="{{BOWER_URL}}dojox/editor/plugins/resources/css/ShowBlockNodes.css" /> +<link rel="stylesheet" href="{{BOWER_URL}}dojox/editor/plugins/resources/css/Preview.css" /> +<link rel="stylesheet" href="{{BOWER_URL}}dojox/editor/plugins/resources/css/Save.css" /> +<link rel="stylesheet" href="{{BOWER_URL}}dojox/editor/plugins/resources/css/Breadcrumb.css" /> +<link rel="stylesheet" href="{{BOWER_URL}}dojox/editor/plugins/resources/css/FindReplace.css" /> +<link rel="stylesheet" href="{{BOWER_URL}}dojox/editor/plugins/resources/css/PasteFromWord.css" /> +<link rel="stylesheet" href="{{BOWER_URL}}dojox/editor/plugins/resources/css/InsertAnchor.css" /> +<link rel="stylesheet" href="{{BOWER_URL}}dojox/editor/plugins/resources/css/CollapsibleToolbar.css" /> +<link rel="stylesheet" href="{{BOWER_URL}}dojox/editor/plugins/resources/css/Blockquote.css" /> +<link rel="stylesheet" href="{{BOWER_URL}}dojox/editor/plugins/resources/css/Smiley.css" /> +<!-- Styles for the lightboxes. --> +<link rel="stylesheet" href="{{BOWER_URL}}dojox/image/resources/LightboxNano.css" /> +<!-- Local style declarations --> +<link rel="stylesheet" href="{{BOWER_URL}}dijit/themes/tundra/tundra.css" /> + +<!-- the main JavaScript block is pulled in with an include --> +<script> +{% include "gracedb/event_detail_script.js" %} +</script> - </script> - <link rel="stylesheet" href="https://ajax.googleapis.com/ajax/libs/dojo/1.7.1/dojox/editor/plugins/resources/css/PageBreak.css" /> - <link rel="stylesheet" href="https://ajax.googleapis.com/ajax/libs/dojo/1.7.1/dojox/editor/plugins/resources/css/ShowBlockNodes.css" /> - <link rel="stylesheet" href="https://ajax.googleapis.com/ajax/libs/dojo/1.7.1/dojox/editor/plugins/resources/css/Preview.css" /> - <link rel="stylesheet" href="https://ajax.googleapis.com/ajax/libs/dojo/1.7.1/dojox/editor/plugins/resources/css/Save.css" /> - <link rel="stylesheet" href="https://ajax.googleapis.com/ajax/libs/dojo/1.7.1/dojox/editor/plugins/resources/css/Breadcrumb.css" /> - <link rel="stylesheet" href="https://ajax.googleapis.com/ajax/libs/dojo/1.7.1/dojox/editor/plugins/resources/css/FindReplace.css" /> - <link rel="stylesheet" href="https://ajax.googleapis.com/ajax/libs/dojo/1.7.1/dojox/editor/plugins/resources/css/PasteFromWord.css" /> - <link rel="stylesheet" href="https://ajax.googleapis.com/ajax/libs/dojo/1.7.1/dojox/editor/plugins/resources/css/InsertAnchor.css" /> - <link rel="stylesheet" href="https://ajax.googleapis.com/ajax/libs/dojo/1.7.1/dojox/editor/plugins/resources/css/CollapsibleToolbar.css" /> - <link rel="stylesheet" href="https://ajax.googleapis.com/ajax/libs/dojo/1.7.1/dojox/editor/plugins/resources/css/Blockquote.css" /> - <link rel="stylesheet" href="https://ajax.googleapis.com/ajax/libs/dojo/1.7.1/dojox/editor/plugins/resources/css/Smiley.css" /> -<style> - @import "https://ajax.googleapis.com/ajax/libs/dojo/1.7.1/dijit/themes/tundra/tundra.css"; - a:hover .dojoxEnlarge { - display: block !important; - } - .dojoxEnlarge { - /* background: url(images/enlarge.png) no-repeat 0 0;*/ - top: -5px; - left: -5px; - margin: 0 !important; - width: 16px; - height: 16px; - } - .dojoxLoading { - /* background: #333 url(images/loading-dark.gif) no-repeat center center;*/ - border-radius: 5px; - -moz-border-radius: 5px; - -webkit-border-radius: 5px; - border: 2px solid #000; - height: 24px; - opacity: 0.8; - filter: alpha(opacity=80); - padding: 6px; - width: 24px; - } - </style> {% endblock %} {% block content %} +<div id='event_detail_content'> + <p>{{ message }}</p> <!-- XXX This next bit is super hacky. --> @@ -598,15 +64,9 @@ <div class="content-area"> {% block basic_info %} -<h3> Basic Info </h3> +<h2> Basic Info </h2> <table class="event"> - {# {% if skyalert_authorized %} #} - <!-- <tr><td colspan="4"> - <a href="{# {% url "skyalert" object.graceid %} #}"><button type="button">Submit to Skyalert</button></a> - </td></tr> --> - {# {% endif %} #} - <tr> <th valign="top">UID</th> <th>Labels</th> @@ -615,18 +75,16 @@ <th>Search</th> <th>Instruments</th> <th> - {{ "gps"|timeselect:"gps" }} - Event Time + <div id="basic_info_event_ts"></div> + <div> Event Time </div> </th> <th>FAR (Hz)</th> <th>Links</th> <th> - {{"created"|timeselect:"utc" }} - Submitted + <div id="basic_info_created_ts"></div> + <div> Submitted </div> </th> </tr> - - <tr> <td>{{ object.graceid }}</td> <td> @@ -646,7 +104,6 @@ <td><a href="{{ object.weburl }}">Data</a></td> <td>{{ object.created|multiTime:"created" }}</td> </tr> - </table> {% endblock %} </div> @@ -658,22 +115,7 @@ {% endblock %} </div> -{# Enable post to skymap viewer #} -<div id="sV_form_div" style="display: none"> - -<form action="{{ SKYMAP_VIEWER_SERVICE_URL }}" id="sV_form" method="post"> - <input type="hidden" name="skymapid" value="{{ object.graceid }}"> - <!-- <input type="hidden" name="json" value="{{ skymap_json }}"> --> - <!-- <input type="hidden" name="json" value="blaargh"> --> - - <input type="hidden" name="json" /> - <input type="hidden" name="embb" /> - - <input type="submit" value="View in skymapViewer!"> -</form> -</div> - -<!-- Neighbors Support refresh_neighbors() function --> +{# Neighbors #} <script type="text/javascript"> var refresh_neighbors= function() { alert("NOT SET YET"); }; require(["dojo/dom", "dojo/html", "dojo/request", "dojo/domReady!"], @@ -699,284 +141,12 @@ // refresh_neighbors("5"); }); require(["dijit/InlineEditBox", "dijit/form/NumberSpinner", "dijit/form/TextBox"]); - - </script> - - {% include "gracedb/neighbors_frag.html" %} -</p> - - <!-- Single Inspiral Data --> - - - <div id="bulletin_board" data-dojo-type="dijit/TitlePane" - data-dojo-props="title: 'EM Follow-up Bulletin Board', open: false"> - - <div data-dojo-type="dijit/form/Form" id="myForm" - data-dojo-id="myForm" encType="multipart/form-data" action="" - method=""> - <script> - require(["dojo/parser", "dijit/form/Form", "dijit/form/Button", "dijit/form/ValidationTextBox", "dijit/form/DateTextBox"]); - </script> - - <script type="dojo/on" data-dojo-event="reset"> - return confirm('Press OK to reset widget values'); - </script> - - <script type="dojo/on" data-dojo-event="submit"> - if(this.validate()){ - return confirm('Form is valid, press OK to submit'); - }else{ - alert('Form contains invalid data. Please correct first'); - return false; - } - return true; - </script> - - <h2>Make a report here</h2> - -<form method="POST" action="{% url "embblogentry" object.graceid "" %}"> -<table> - -<tr><td><a href=# onclick="alert('Define here what subclass of report this is:\n FOOTPRINT: There is or could be image data taken of the sky, with a specific instrument, at a specific time, in a specific waveband, as followup of a specific LVC alert.\n SOURCE: There is a candidate source at the given location, that could be associated with the given LVC alert.\n COMMENT: The primary purpose of this report is a human-written comment concerning the given LVC alert.'); return false;"> -What type of report is this?</a></td> <td><select name="eel_status"> -<option value="FO">FOOTPRINT</option> -<option value="SO">SOURCE</option> -<option value="CO">COMMENT</option> -</select></td></tr> - -<tr><td><a href=# onclick="alert('There is also Observation Status which is:\n OBSERVATION: has been done.\n PREDICTION: this report represents intent or future observation.\n TEST: This EEL is part of a test and has no astrophysical significance.\n NOT APPLICABLE: It is neither observation nor prediction.'); return false;"> -Observation Status</a></td> <td><select name="obs_status"> -<option value="OB">OBSERVATION</option> -<option value="PR">PREDICTION</option> -<option value="TE">TEST</option> -<option value="NA">NOT APPLICABLE</option> -</select></td></tr> - -<tr><td><a href=# onclick="alert('Group with which the LSC has signed a trust agreement, hereby providing data -under its trust (required).'); return false;"> -Which MOU Group provides this report?</a></td> <td><select name="group"> -{% for g in groups %} -<option value="{{ g }}">{{ g }}</option> -{% endfor %} </select> </td> </tr> - - - -<tr><td><a href=# onclick="alert('An ID from the data owner to identify this footprint (optional).');return false;"> -Your reference</a></td> <td><input type="text" name="footprintID"/></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> - -<tr><td><a href=# onclick="alert('The name of the photon detector whose observational metadata is being recorded here (optional).');return false;"> -Instrument</a></td> <td><input type="text" name="instrument"> </td> </tr> - -<tr><td><a href=# onclick="alert('Specify waveband of the observation. Vocabulary is taken from the UCD vocabulary, a Virtual Observatory standard. Waveband can be either wide (eg Radio) or narrow (eg Radio 200-400 MHz).');return false;"> -Waveband</a></td> <td colspan=2><select name="waveband"> -<option value="em.gamma">Gamma rays part of the spectrum</option> -<option value="em.gamma.soft">Soft gamma ray (120 - 500 keV)</option> -<option value="em.gamma.hard">Hard gamma ray (>500 keV)</option> -<option value="em.X-ray">X-ray part of the spectrum</option> -<option value="em.X-ray.soft">Soft X-ray (0.12 - 2 keV)</option> -<option value="em.X-ray.medium">Medium X-ray (2 - 12 keV)</option> -<option value="em.X-ray.hard">Hard X-ray (12 - 120 keV)</option> -<option value="em.UV">Ultraviolet part of the spectrum</option> -<option value="em.UV.10-50nm">Ultraviolet between 10 and 50 nm</option> -<option value="em.UV.50-100nm">Ultraviolet between 50 and 100 nm</option> -<option value="em.UV.100-200nm">Ultraviolet between 100 and 200 nm</option> -<option value="em.UV.200-300nm">Ultraviolet between 200 and 300 nm</option> -<option value="em.UV.FUV">Far-Infrared, 30-100 microns</option> -<option value="em.opt">Optical part of the spectrum</option> -<option value="em.opt.U">Optical band between 300 and 400 nm</option> -<option value="em.opt.B">Optical band between 400 and 500 nm</option> -<option value="em.opt.V">Optical band between 500 and 600 nm</option> -<option value="em.opt.R">Optical band between 600 and 750 nm</option> -<option value="em.opt.I">Optical band between 750 and 1000 nm</option> -<option value="em.IR">Infrared part of the spectrum</option> -<option value="em.IR.NIR">Near-Infrared, 1-5 microns</option> -<option value="em.IR.J">Infrared between 1.0 and 1.5 micron</option> -<option value="em.IR.H">Infrared between 1.5 and 2 micron</option> -<option value="em.IR.K">Infrared between 2 and 3 micron</option> -<option value="em.IR.MIR">Medium-Infrared, 5-30 microns</option> -<option value="em.IR.3-4um">Infrared between 3 and 4 micron</option> -<option value="em.IR.4-8um">Infrared between 4 and 8 micron</option> -<option value="em.IR.8-15um">Infrared between 8 and 15 micron</option> -<option value="em.IR.15-30um">Infrared between 15 and 30 micron</option> -<option value="em.IR.30-60um">Infrared between 30 and 60 micron</option> -<option value="em.IR.60-100um">Infrared between 60 and 100 micron</option> -<option value="em.IR.FIR">Far-Infrared, 30-100 microns</option> -<option value="em.mm">Millimetric part of the spectrum</option> -<option value="em.mm.1500-3000GHz">Millimetric between 1500 and 3000 GHz</option> -<option value="em.mm.750-1500GHz">Millimetric between 750 and 1500 GHz</option> -<option value="em.mm.400-750GHz">Millimetric between 400 and 750 GHz</option> -<option value="em.mm.200-400GHz">Millimetric between 200 and 400 GHz</option> -<option value="em.mm.100-200GHz">Millimetric between 100 and 200 GHz</option> -<option value="em.mm.50-100GHz">Millimetric between 50 and 100 GHz</option> -<option value="em.mm.30-50GHz">Millimetric between 30 and 50 GHz</option> -<option value="em.radio">Radio part of the spectrum</option> -<option value="em.radio.12-30GHz">Radio between 12 and 30 GHz</option> -<option value="em.radio.6-12GHz">Radio between 6 and 12 GHz</option> -<option value="em.radio.3-6GHz">Radio between 3 and 6 GHz</option> -<option value="em.radio.1500-3000MHz">Radio between 1500 and 3000 MHz</option> -<option value="em.radio.750-1500MHz">Radio between 750 and 1500 MHz</option> -<option value="em.radio.400-750MHz">Radio between 400 and 750 MHz</option> -<option value="em.radio.200-400MHz">Radio between 200 and 400 MHz</option> -<option value="em.radio.100-200MHz">Radio between 100 and 200 MHz</option> -<option value="em.radio.20-100MHz">Radio between 20 and 100 MHz</option> - </select></td></tr> -</table> -<hr/> -<script> -function showRectanglesCheckbox(){ - var s = document.getElementById('showRectangles'); - showRectangles = s.checked; - if(showRectangles){ document.getElementById('rectanglesForm').style.display = "block" - } else { document.getElementById('rectanglesForm').style.display = "none" - } - redrawAll(); - return false; -} </script> -<input id="showRectangles" type="checkbox" onclick="showRectanglesCheckbox()" />add Sky Footprints<br/> -<div id=rectanglesForm style="display: none;"> -<table> -<tr><td><a href=# onclick="alert('RA and Dec specify a center point of a rectangle that is aligned equatorially. Or list of centers. They must be in decimal degrees 0<=RA<=360 -90<=Dec<=90, in the J2000 frame.');return false;"> -RA (decimal degrees)</a></td> <td><input type="text" name="raList" value="" size=80/></td></tr> - -<tr><td><a href=# onclick="alert('RA and Dec specify a center point of a rectangle that is aligned equatorially. Or list of centers. They must be in decimal degrees 0<=RA<=360 -90<=Dec<=90, in the J2000 frame.');return false;"> -Dec (decimal degrees)</a></td> <td><input type="text" name="decList" value="" size=80/></td></tr> - -<!-- -<script language="javascript" type="text/javascript" src="https://losc.ligo.org/s/js/gpstimeutil.js"></script> -<script language="javascript" type="text/javascript" src="https://losc.ligo.org/s/js/sexg.js"></script> ---> - - -<tr><td><a href=# onclick="alert('The time at the center of a time interval during which the observation was taken. Or list of times.');return false;"> -GPStime</a></td> -<td><input type="text" name="gpstimeList" value="" size=80/></td> -<!-- -<td><input onKeyUp="return TIMEcopy(2,1);" id="TIMEgps1" name="gpstimeList" maxlength="12" size="20" value="1000000000"/></td> -<td><input onKeyUp="return TIMEcopy(1,1);" id="TIMEiso1" name="TIMEiso1" maxlength="25" size="25" value="2011-09-14T01:46:25"/></td> -<td><span id="TIMEerr1" STYLE="font: 12px Arial; color:red">OK</span><br/></td> ---> -</tr> - -<tr><td><a href=# onclick="alert('RAWidth and DecWidth specify the size of a a rectangle that is aligned equatorially. Thus the edge of the box is distant from the center by half of the width.');return false;"> -RAwidth (decimal degrees)</a></td> <td><input type="text" name="raWidthList" value=""/></td></tr> - -<tr><td><a href=# onclick="alert('RAWidth and DecWidth specify the size of a a rectangle that is aligned equatorially. Thus the edge of the box is distant from the center by half of the width.');return false;"> -Decwidth (decimal degrees)</a></td> <td><input type="text" name="decWidthList" value=""/></td></tr> - -<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> - -</table> -</div> -<hr/> -<script> -function showJsonCheckbox(){ - var s = document.getElementById('showJson'); - showJson = s.checked; - if(showJson){ document.getElementById('jsonForm').style.display = "block" - } else { document.getElementById('jsonForm').style.display = "none" - } - redrawAll(); - return false; -} -</script> -<input id="showJson" type="checkbox" onclick="showJsonCheckbox()" />add JSON data<br/> -<div id=jsonForm style="display: none;"> -<table> -<tr><td><a href=# onclick="alert('This section allows for machine-readable data to be included in a flexible way. any key can be any value in a JSON dictionary. There is a useful validator at http://jsonlint.com.');return false;"> -JSON data</a></td> <td colspan=2><textarea name="extra_info_dict" rows="20" cols="50"></textarea></td></tr> -</table> -</div> -<hr/> -<input type="submit" value="Submit EMBB Report"/> -</form> - - - </div> - - <!-- Bulletin Board Log --> - <div data-dojo-type="dijit/TitlePane" - {% if object.embbeventlog_set.count > 0 %} - data-dojo-props="title: 'Bulletin Board Log', open: true"> - {% else %} - data-dojo-props="title: 'Bulletin Board Log', open: false"> - {% endif %} -This data in <a href="{% url "api:embbeventlog-list" object.graceid %}">JSON</a><br/> -<table id="bb_2" class="event" border="1" -style="table-layout:fixed; width:100%;word-wrap:break-word;"> - <thead> - <tr> - <th>Date</th> - <th>Submitter</th> - <th>MOU Group</th> - <th>Instrument</th> - <th>Footprint ID</th> - <th>Waveband</th> - <th>RA</th> - <th>DEC</th> - <th>RAwidth</th> - <th>Decwidth</th> - <th>GPS Time</th> - <th>Duration</th> - <th>EEL Type</th> - <th>Obs status</th> - <th>Comment</th> - <th>JSON Data</th> - </tr> - </thead> - - <tbody id="bb_log" > - {% for eel in object.embbeventlog_set.iterator %} -<tr> -<td>{{ eel.created }}</td> -<td>{{ eel.submitter }}</td> -<td>{{ eel.group }}</td> -<td>{{ eel.instrument }}</td> -<td>{{ eel.footprintID }}</td> -<td>{{ eel.waveband }}</td> -<td>{{ eel.ra }}</td> -<td>{{ eel.dec }}</td> -<td>{{ eel.raWidth }}</td> -<td>{{ eel.decWidth }}</td> -<td>{{ eel.gpstime }}</td> -<td>{{ eel.duration }}</td> -<td>{{ eel.eel_status }}</td> -<td>{{ eel.obs_status }}</td> -<td>{{ eel.comment|truncatechars:40 }}</td> -<td>{{ eel.extra_info_dict|truncatechars:40 }}</td> -</tr> - {% endfor %} - </tbody> - - </table> - </div> -</div> - - -<p> </p> -<div id="annotations" class="content-area"> - <div id="new_log_entry_form"> - <noscript> - <h3>Create a new log entry</h3> - <form id="newlog" action="{% url "logentry" object.graceid "" %}" method="POST"> - <p> - <textarea id="newlogtext" name="comment" style="width:300px;display:block"></textarea> - <input type="submit" value="Submit"/> - </p> - </form> - </noscript> - </div> +{% include "gracedb/neighbors_frag.html" %} - <h3 id="logmessagetitle">Event Log Messages</h3> - <div id="previewer"></div> - <div id="editor"></div> +{% include "gracedb/eel_form_frag.html" %} -</div> +</div> <!-- end event_detail_content div --> {% endblock %} -<!-- Single Inspiral Data --> diff --git a/templates/gracedb/event_detail_CWB.html b/templates/gracedb/event_detail_CWB.html index de184254918e290919a0d69ea6fdde66fff6ed64..fc2f6782b7476c7fa32118462fc5ca32ed4bcacd 100644 --- a/templates/gracedb/event_detail_CWB.html +++ b/templates/gracedb/event_detail_CWB.html @@ -4,7 +4,7 @@ {# Analysis-specific attributes for a cWB event#} {% block analysis_specific %} -<h3>Analysis-Specific Attributes</h3> +<h2>Analysis-Specific Attributes</h2> <table class="analysis_specific"> <tbody> <tr> diff --git a/templates/gracedb/event_detail_GRB.html b/templates/gracedb/event_detail_GRB.html index 142035ac19af04c4e689b55c9412bdd0fb19a2ad..d2a69cb3df042b0224b19882a7131a7d14e321b4 100644 --- a/templates/gracedb/event_detail_GRB.html +++ b/templates/gracedb/event_detail_GRB.html @@ -2,7 +2,7 @@ {% load timeutil %} {% block basic_info %} -<h3> Basic Info </h3> +<h2> Basic Info </h2> <table class="event"> {% if skyalert_authorized %} @@ -19,13 +19,13 @@ <th>Search</th> <th>Instruments</th> <th> - {{ "gps"|timeselect:"gps" }} - Event Time + <div id="basic_info_event_ts"></div> + <div> Event Time </div> </th> <th>Links</th> <th> - {{"created"|timeselect:"utc" }} - Submitted + <div id="basic_info_created_ts"></div> + <div> Submitted </div> </th> </tr> diff --git a/templates/gracedb/event_detail_coinc.html b/templates/gracedb/event_detail_coinc.html index 11d81da57d377ee221b5bce8d74f3db29af900cb..ed8bddbfb7b1c531bb4e83e43b763660e59ad5c7 100644 --- a/templates/gracedb/event_detail_coinc.html +++ b/templates/gracedb/event_detail_coinc.html @@ -12,10 +12,10 @@ <div style="display:table-row;width:100%"> <div style="display:table-cell;float:left;width:35%;"> -<h3>Coinc Tables</h3> +<h2>Coinc Tables</h2> <!-- <table class="analysis_specific"> <tbody> --> <!-- 5 rows here --> -<table style="height:320px"> +<table style="height:364px"> <!-- <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> --> @@ -33,7 +33,7 @@ <!-- Single Inspiral Data --> <!-- 13 rows here. --> <div style="display:table-cell;float:right;width:65%"> -<h3>Single Inspiral Tables</h3> +<h2>Single Inspiral Tables</h2> <!--<div id="single_inspiral_tables"> --> <!-- <div id="single_inspiral_tables" @@ -359,6 +359,18 @@ <td>{{ e.gamma9 }}</td> {% endfor %} </tr> --> + <tr> + <th>spin1z</th> + {% for e in single_inspiral_events %} + <td>{{ e.spin1z }}</td> + {% endfor %} + </tr> + <tr> + <th>spin2z</th> + {% for e in single_inspiral_events %} + <td>{{ e.spin2z }}</td> + {% endfor %} + </tr> </table> </div> diff --git a/templates/gracedb/event_detail_script.js b/templates/gracedb/event_detail_script.js new file mode 100644 index 0000000000000000000000000000000000000000..159c51d477c00e05534e1f292cf0330434d8773d --- /dev/null +++ b/templates/gracedb/event_detail_script.js @@ -0,0 +1,1001 @@ +// Ugh. Why do I have to pull the stuff in here? +// Tooltip pop-ups for labels. +var label_descriptions = { + cWB_s: "cWB_s", + cWB_r: "cWB_r", + EM_READY: "Has been processed by GDB Processor.<br/>Skymaps have been produced.", + SWIFT_NO: "Do not send notification to SWIFT telescope.", + SWIFT_GO: "Send notification to SWIFT telescope.", + LUMIN_NO: "LUMIN No", + LUMIN_GO: "LUMIN Go", + DQV: "Data quality veto.", + INJ: "Injection occured near this time." +} +function tooltiptext(name, creator, time) { + return ( creator + " " + time + "<br/>" + label_descriptions[name] ); +}; +var tooltip=function(){ + var id = 'tt'; + var top = 3; + var left = 3; + var maxw = 300; + var speed = 10; + var timer = 20; + var endalpha = 95; + var alpha = 0; + var tt,t,c,b,h; + var ie = document.all ? true : false; + return{ + show:function(v,w){ + if(tt == null){ + tt = document.createElement('div'); + tt.setAttribute('id',id); + t = document.createElement('div'); + t.setAttribute('id',id + 'top'); + c = document.createElement('div'); + c.setAttribute('id',id + 'cont'); + b = document.createElement('div'); + b.setAttribute('id',id + 'bot'); + tt.appendChild(t); + tt.appendChild(c); + tt.appendChild(b); + document.body.appendChild(tt); + tt.style.opacity = 0; + tt.style.filter = 'alpha(opacity=0)'; + document.onmousemove = this.pos; + } + tt.style.display = 'block'; + c.innerHTML = v; + tt.style.width = w ? w + 'px' : 'auto'; + if(!w && ie){ + t.style.display = 'none'; + b.style.display = 'none'; + tt.style.width = tt.offsetWidth; + t.style.display = 'block'; + b.style.display = 'block'; + } + if(tt.offsetWidth > maxw){tt.style.width = maxw + 'px'} + h = parseInt(tt.offsetHeight) + top; + clearInterval(tt.timer); + tt.timer = setInterval(function(){tooltip.fade(1)},timer); + }, + pos:function(e){ + var u = ie ? event.clientY + document.documentElement.scrollTop : e.pageY; + var l = ie ? event.clientX + document.documentElement.scrollLeft : e.pageX; + tt.style.top = (u - h) + 'px'; + tt.style.left = (l + left) + 'px'; + }, + fade:function(d){ + var a = alpha; + if((a != endalpha && d == 1) || (a != 0 && d == -1)){ + var i = speed; + if(endalpha - a < speed && d == 1){ + i = endalpha - a; + }else if(alpha < speed && d == -1){ + i = a; + } + alpha = a + (i * d); + tt.style.opacity = alpha * .01; + tt.style.filter = 'alpha(opacity=' + alpha + ')'; + }else{ + clearInterval(tt.timer); + if(d == -1){tt.style.display = 'none'} + } + }, + hide:function(){ + clearInterval(tt.timer); + tt.timer = setInterval(function(){tooltip.fade(-1)},timer); + } + }; +}(); + + + + + +// A utility +var getKeys = function(obj){ + var keys = []; + for(var key in obj){ + keys.push(key); + } + return keys; +} + +var image_extensions = ['png', 'gif', 'jpg']; + +// A utility function to determine whether a log message has an image. +// This would not be necessary if we were using django template language +var hasImage = function(object) { + if (!object.filename) return false; + var file_extension = object.filename.slice(object.filename.length - 3); + return image_extensions.indexOf(file_extension) >= 0; +} + +// some URLs. Usage of Django template syntax should be limited to here +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 %}'; +var skymapJsonUrl = '{% url "file" object.graceid "skymap.json" %}'; +var skymapViewerUrl = '{{ SKYMAP_VIEWER_SERVICE_URL }}'; + +// This little list determines the priority ordering of the digest sections. +var blessed_tag_priority_order = [ + 'analyst_comments', + 'psd', + 'data_quality', + 'sky_loc', + 'background', + 'ext_coinc', + 'strain', + 'tfplots', + 'sig_info', + 'audio', +]; + +require([ + 'dojo/_base/declare', + 'dojo/query', + 'dojo/on', + 'dojo/parser', + 'dojo/dom', + 'dojo/dom-construct', + 'dojo/dom-style', + 'dojo/request', + 'dojo/store/Memory', + 'dojo/data/ObjectStore', + 'dstore/Rest', + 'dstore/RequestMemory', + 'dgrid/Grid', + 'dgrid/extensions/DijitRegistry', + 'put-selector/put', + 'dijit/TitlePane', + 'dijit/form/Form', + 'dijit/form/Button', + 'dijit/form/TextBox', + 'dijit/form/ComboBox', + 'dijit/form/Select', + 'dijit/Tooltip', + 'dijit/Dialog', + 'dijit/Editor', + 'dojox/editor/plugins/Save', + 'dojox/editor/plugins/Preview', + 'dojox/layout/ScrollPane', + 'dojox/image/LightboxNano', + 'dijit/_editor/plugins/TextColor', + 'dijit/_editor/plugins/LinkDialog', + 'dijit/_editor/plugins/ViewSource', + 'dijit/_editor/plugins/NewPage', + 'dijit/_editor/plugins/FullScreen', + 'dojo/domReady!', +], function(declare, query, on, parser, dom, domConstruct, domStyle, request, Memory, ObjectStore, + Rest, RequestMemory, Grid, DijitRegistry, + put, + TitlePane, Form, Button, TextBox, ComboBox, Select, Tooltip, Dialog, Editor, + Save, Preview, ScrollPane) { + + parser.parse(); + //---------------------------------------------------------------------------------------- + // Some utility functions + //---------------------------------------------------------------------------------------- + var createExpandingSection = function (titleNode, contentNode, formNode, 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); + var addButtonNode = put(titleTableRow, "td.title div.expandFormButton", '(add)'); + + if (!(initiallyOpen && initiallyOpen==true)) { + put(expandGlyphNode, '.closed'); + domStyle.set(contentNode, 'display', 'none'); + domStyle.set(addButtonNode, 'display', 'none'); + } + // This one is always closed initially + domStyle.set(formNode, 'display', 'none'); + + on(expandGlyphNode, "click", function() { + if (domStyle.get(contentNode, 'display') == 'none') { + domStyle.set(contentNode, 'display', 'block'); + domStyle.set(addButtonNode, 'display', 'block'); + put(expandGlyphNode, '!closed'); + } else { + domStyle.set(contentNode, 'display', 'none'); + domStyle.set(addButtonNode, 'display', 'none'); + put(expandGlyphNode, '.closed'); + } + }); + + on(titleTextNode, "click", function() { + if (domStyle.get(contentNode, 'display') == 'none') { + domStyle.set(contentNode, 'display', 'block'); + domStyle.set(addButtonNode, 'display', 'block'); + put(expandGlyphNode, '!closed'); + } else { + domStyle.set(contentNode, 'display', 'none'); + domStyle.set(addButtonNode, 'display', 'none'); + put(expandGlyphNode, '.closed'); + } + }); + + on(addButtonNode, "click", function() { + if (domStyle.get(formNode, 'display') == 'none') { + domStyle.set(formNode, 'display', 'block'); + addButtonNode.innerHTML = '(cancel)'; + } else { + domStyle.set(formNode, 'display', 'none'); + addButtonNode.innerHTML = '(add)'; + } + }); + } + + var timeChoicesData = [ + {"id": "llo", "label": "LLO Local"}, + {"id": "lho", "label": "LHO Local"}, + {"id": "virgo", "label": "Virgo Local"}, + {"id": "utc", "label": "UTC"}, + ]; + // XXX Fixme. So. Bad. + var timeChoicesDataWithGps = [ + {"id": "gps", "label": "GPS Time"}, + {"id": "llo", "label": "LLO Local"}, + {"id": "lho", "label": "LHO Local"}, + {"id": "virgo", "label": "Virgo Local"}, + {"id": "utc", "label": "UTC"}, + ]; + + var timeChoices = new Memory ({ data: timeChoicesData }); + var timeChoicesStore = new ObjectStore({ objectStore: timeChoices}); + var timeChoicesWithGps = new Memory ({ data: timeChoicesDataWithGps }); + var timeChoicesWithGpsStore = new ObjectStore({ objectStore: timeChoicesWithGps}); + + var createTimeSelect = function(node, label, defaultName, useGps) { + var myStore = (useGps) ? timeChoicesWithGpsStore : timeChoicesStore; + var s = new Select({ store: myStore }, node); + s.attr("value", defaultName); + s.on("change", function () { changeTime(this, label); }); + return s; + } + + //---------------------------------------------------------------------------------------- + // Take care of stray time selects + //---------------------------------------------------------------------------------------- + createTimeSelect(dom.byId('basic_info_event_ts'), 'gps', 'gps', true); + createTimeSelect(dom.byId('basic_info_created_ts'), 'created', 'utc', true); + createTimeSelect(dom.byId('neighbors_event_ts'), 'ngps', 'gps', true); + createTimeSelect(dom.byId('neighbors_created_ts'), 'ncreated', 'utc', true); + + //---------------------------------------------------------------------------------------- + // 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'); + + // 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; + + createExpandingSection(embbTitleDiv, embbContentDiv, embbAddDiv, 'Electromagnetic Bulletin Board'); + + // Append the div that will hold our dgrid + put(embbContentDiv, 'div#eel-grid'); + + embbStore = new declare([Rest, RequestMemory])({target: embbEventLogListUrl}); + embbStore.get('').then(function(content) { + // Pull the EELs out of the rest content and create a new simple store from them. + var eels = content.embblog; + + if (eels.length == 0) { + eelDiv = dom.byId('eel-grid'); + eelDiv.innerHTML = '<p> (No EMBB log 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: '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; + 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' }, + ]; + + // 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]); + // Add the text comment + put(t, 'tr td[style="width: 5%"]+td div.subrid-text', object.comment); + + return div; + } + }, 'eel-grid'); + grid.renderArray(eels); + 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 eels or not. + }); + + + //---------------------------------------------------------------------------------------- + // Section for log entries + //---------------------------------------------------------------------------------------- + var annotationsDiv = put(eventDetailContainer, 'div.content-area'); + var logTitleDiv = put(annotationsDiv, 'div#log_title_expander'); + var logContentDiv = put(annotationsDiv, 'div#log_content'); + + // Create the form for adding a new log entry. + var logAddDiv = put(logContentDiv, 'div#new_log_entry_form'); + put(logAddDiv, 'div#previewer'); + put(logAddDiv, 'div#editor'); + createExpandingSection(logTitleDiv, logContentDiv, logAddDiv, 'Event Log Messages', true); + + //---------------------------------------------------------------------------------------- + //---------------------------------------------------------------------------------------- + // Get the tag properties. Sorta hate it that this is so complicated. + tagStore = new declare([Rest, RequestMemory])({target: tagListUrl}); + tagStore.get('').then(function(content) { + var tags = content.tags; + + var tag_display_names = new Object(); + var blessed_tags = new Array(); + for (var tag_name in tags) { + var tag = tags[tag_name]; + tag_display_names[tag_name] = tag.displayName; + if (tag.blessed) blessed_tags.push({ name: tag_name }); + } + // Reorder the blessed tags according to the priority order above. + var new_blessed_tags = new Array(); + for (var i=0; i<blessed_tag_priority_order.length; i++) { + var tag_name = blessed_tag_priority_order[i]; + for (var j=0; j<blessed_tags.length; j++) { + if (blessed_tags[j].name == tag_name) { + new_blessed_tags.push(blessed_tags[j]); + break; + } + } + } + // Add the rest of them. + for (var i=0; i<blessed_tags.length; i++) { + if (new_blessed_tags.indexOf(blessed_tags[i]) < 0) { + new_blessed_tags.push(blessed_tags[i]); + } + + } + blessed_tags = new_blessed_tags; + var blessed_tag_names = blessed_tags.map(function (obj) { return obj.name; }); + var blessedTagStore = new Memory({ data: blessed_tags }); + + // Create the tag callback generators. These don't depend on the log message contents + // so we should be able to define them here. + function getTagDelCallback(tag_name, N) { + return function() { + tagUrl = tagUrlPattern.replace("000", N).replace("temp",tag_name); + var tagResultDialog = new Dialog({ style: "width: 300px" }); + var actionBar = domConstruct.create("div", { "class": "dijitDialogPaneActionBar" }); + var tbnode = domConstruct.create("div", { + style: "margin: 0px auto 0px auto; text-align: center;" + }, actionBar); + var tagButton = new Button({ + label: "Ok", + onClick: function(){ + tagResultDialog.hide(); + }}).placeAt(tbnode); + request.del(tagUrl).then( + function(text){ + tagResultDialog.set("content", text); + domConstruct.place(actionBar, tagResultDialog.containerNode); + tagResultDialog.show(); + location.reload(true); + }, + function(error){ + tagResultDialog.set("content", "Error: " + error); + domConstruct.place(actionBar, tagResultDialog.containerNode); + tagResultDialog.show(); + }); + } + } + + function getTagAddCallback(N) { + return function() { + // Create the tag result dialog. + var tagResultDialog = new Dialog({ style: "width: 300px" }); + var actionBar = domConstruct.create("div", { "class": "dijitDialogPaneActionBar" }); + var tbnode = domConstruct.create("div", { + style: "margin: 0px auto 0px auto; text-align: center;" + }, actionBar); + var tagButton = new Button({ + label: "Ok", + onClick: function(){ + tagResultDialog.hide(); + } + }).placeAt(tbnode); + + // Create the form + addTagForm = new Form(); + var msg = "<p> Choose a tag \ + name from the dropdown menu or enter a new one. If you are \ + creating a new tag, please also provide a display name. </p>"; + domConstruct.create("div", {innerHTML: msg} , addTagForm.containerNode); + + // Form for tagging existing log messages. + new ComboBox({ + name: "existingTagSelect", + value: "", + store: blessedTagStore, + searchAttr: "name" + }).placeAt(addTagForm.containerNode); + + new TextBox({ + name: "tagDispName", + }).placeAt(addTagForm.containerNode); + + new Button({ + type: "submit", + label: "OK", + }).placeAt(addTagForm.containerNode); + + // Create the dialoge + addTagDialog = new Dialog({ + title: "Add Tag", + content: addTagForm, + style: "width: 300px" + }); + + // Define the form on submit handler + on(addTagForm, "submit", function(evt) { + evt.stopPropagation(); + evt.preventDefault(); + formData = addTagForm.getValues(); + var tagName = formData.existingTagSelect; + var tagDispName = formData.tagDispName; + var tagUrl = tagUrlPattern.replace("000", N).replace("temp",tagName); + + request.post(tagUrl, { + data: {displayName: tagDispName} + }).then( + function(text){ + tagResultDialog.set("content", text); + domConstruct.place(actionBar, tagResultDialog.containerNode); + tagResultDialog.show(); + location.reload(true); + }, + function(error){ + tagResultDialog.set("content", "Error: " + error); + domConstruct.place(actionBar, tagResultDialog.containerNode); + tagResultDialog.show(); + } + ); + addTagDialog.hide(); + }); + + // show the dialog + addTagDialog.show(); + } + } + + //---------------------------------------------------------------------------------------- + //---------------------------------------------------------------------------------------- + // Now that we've got the tag info, let's get the event log objects. + logStore = new declare([Rest, RequestMemory])({target: eventLogListUrl}); + logStore.get('').then(function(content) { + + // Pull the logs out of the JSON returned by the server. + var logs = content.log; + var Nlogs = logs.length; + + // Convert the 'created' times to UTC. + logs = logs.map( function(obj) { + var server_t = moment.tz(obj.created, 'America/Chicago'); + obj.created = server_t.clone().tz('UTC').format('LLL'); + return obj; + }); + + // Total up the tags present. This list will have duplicates. + var total_tags = new Array(); + logs.forEach( function(log) { + log.tag_names.forEach( function (tag_name) { + total_tags.push(tag_name); + }); + }); + + // Figure out what blessed tags are present. + var our_blessed_tags = blessed_tag_names.filter( function(value) { + return total_tags.indexOf(value) >= 0; + }); + + // If there are any blessed tags here, we'll do TitlePanes + if (our_blessed_tags.length > 0) { + // define our columns for the topical digest panes + var columns = [ + { + field: 'created', + renderHeaderCell: function(node) { + timeHeaderContainer = put(node, 'div'); + createTimeSelect(timeHeaderContainer, 'log', 'llo'); + put(timeHeaderContainer, 'div', 'Log Entry Created'); + return timeHeaderContainer; + }, + renderCell: function(object, value, node, options) { + var server_t = moment.tz(object.created, 'America/Chicago'); + var t = put(node, 'time[name="time-log"]', server_t.format('LLL')); + put(t, '[utc="$"]', server_t.clone().tz('UTC').format('LLL')); + put(t, '[llo="$"]', server_t.format('LLL')); + put(t, '[lho="$"]', server_t.clone().tz('America/Los_Angeles').format('LLL')); + put(t, '[virgo="$"]', server_t.clone().tz('Europe/Rome').format('LLL')); + return t; + } + }, + { field: 'issuer', label: 'Submitter', get: function(obj) { return obj.issuer.display_name; } }, + // Sometimes the comment contains HTML, so we just want to return whatever it has. + // This is where the link with the filename goes. Also the view in skymapViewer button + { + field: 'comment', + label: 'Comment', + renderCell: function(object, value, node, options) { + var commentDiv = put(node, 'div'); + // Putting this in the innerHTML allows users to create comments in HTML. + // Whereas, inserting the comment with the put selector escapes it. + commentDiv.innerHTML += value + ' '; + if (object.filename) put(commentDiv, 'a[href=$]', object.file, object.filename); + if (object.filename == 'skymap.json') { + var svButton = put(commentDiv, 'button.modButtonClass#sV_button', 'View in SkymapViewer!'); + put(svButton, '[type="button"][data-dojo-type="dijit/form/Button"]'); + put(svButton, '[style="float: right"]'); + } + return commentDiv; + } + }, + + ]; + + // Create the topical digest title panes + for (i=0; i<our_blessed_tags.length; i++) { + var tag_name = our_blessed_tags[i]; + // First filter the log messages based on whether they have this blessed tag. + var tagLogs = logs.filter( function(obj) { + // XXX Not sure why this simpler filter didn't work. + // return obj.tag_names.indexOf(tag_name) > 0; + for (var k=0; k<obj.tag_names.length; k++) { + if (obj.tag_names[k]==tag_name) return true; + } + return false; + }); + + // Next filter the remaining log messages based on whether or not images are present. + var imgLogs = tagLogs.filter( function(obj) { return hasImage(obj); }); + var noImgLogs = tagLogs.filter ( function(obj) { return !hasImage(obj); }); + + // Create the title pane with a placeholder div + var pane_contents_id = tag_name + '_pane'; + var tp = new TitlePane({ + title: tag_display_names[tag_name], + content: '<div id="' + pane_contents_id + '"></div>', + open: true + }); + logContentDiv.appendChild(tp.domNode); + paneContentsNode = dom.byId(pane_contents_id); + + // Handle the log messages with images by putting them in little box. + if (imgLogs.length) { + var figContainerId = tag_name + '_figure_container'; + var figDiv = put(paneContentsNode, 'div#' + figContainerId); + var figRow = put(figDiv, 'table.figure_container tr'); + for (j=0; j<imgLogs.length; j++) { + var log = imgLogs[j]; + var figTabInner = put(figRow, 'td table.figures'); + var figA = put(figTabInner, 'tr.figrow img[height="180"][src=$]', log.file); + new dojox.image.LightboxNano({href: log.file}, figA); + var figComment = put(figTabInner, 'tr td'); + figComment.innerHTML = log.comment; + figComment.innerHTML += ' <a href="' + log.file + '">' + log.filename + '.</a> '; + figComment.innerHTML += 'Submitted by ' + log.issuer.display_name + ' on ' + log.created; + } + // Put the figures in a scrolling pane in case there are too many to display horizontally + var sp = new dojox.layout.ScrollPane({ orientation: "horizontal", style: "overflow: hidden;" }, + figContainerId); + + } + + // Handle the log messages without images by putting them in a grid. + if (noImgLogs.length) { + var gridNode = put(paneContentsNode, 'div#' + pane_contents_id + '-grid') + var grid = new declare([Grid, DijitRegistry])({ + columns: columns, + className: 'dgrid-autoheight', + // Overriding renderRow here to add an extra class to the row. This is for styling. + renderRow: function(object,options) { + return put('div.supergrid-row', Grid.prototype.renderRow.call(this,object,options)); + } + }, gridNode); + grid.renderArray(noImgLogs); + grid.set("sort", 'N', descending=true); + } + + } + + // Create the full event-log title pane + var columns = [ + { field: 'N', label: 'No.' }, + { + field: 'created', + renderHeaderCell: function(node) { + timeHeaderContainer = put(node, 'div'); + var ts = createTimeSelect(timeHeaderContainer, 'audit-log', 'llo'); + put(timeHeaderContainer, 'div', 'Log Entry Created'); + // XXX Not sure how to get this to do the right thing. + return timeHeaderContainer; + //return ts; + }, + renderCell: function(object, value, node, options) { + var server_t = moment.tz(object.created, 'America/Chicago'); + var t = put(node, 'time[name="time-audit-log"]', server_t.format('LLL')); + put(t, '[utc="$"]', server_t.clone().tz('UTC').format('LLL')); + put(t, '[llo="$"]', server_t.format('LLL')); + put(t, '[lho="$"]', server_t.clone().tz('America/Los_Angeles').format('LLL')); + put(t, '[virgo="$"]', server_t.clone().tz('Europe/Rome').format('LLL')); + return t; + } + }, +{ field: 'issuer', label: 'Submitter', get: function(obj) { return obj.issuer.display_name; } }, + // Sometimes the comment contains HTML, so we just want to return whatever it has. + // This is where the link with the filename goes. Also the view in skymapViewer button + { + field: 'comment', + label: 'Comment', + renderCell: function(object, value, node, options) { + commentDiv = put(node, 'div'); + // Putting this in the innerHTML allows users to create comments in HTML. + // Whereas, inserting the comment with the put selector escapes it. + commentDiv.innerHTML += value + ' '; + if (object.filename) put(commentDiv, 'a[href=$]', object.file, object.filename); + // Create tag-related features + var tagButtonContainer = put(commentDiv, 'div.tagButtonContainerClass'); + // For each existing tag on a log message, we will make a little widget + // to delete it. + object.tag_names.forEach( function(tag_name) { + var delDiv = put(tagButtonContainer, 'div.tagDelButtonDivClass'); + var del_button_id = "del_button_" + object.N + '_' + tag_name; + var delButton = put(delDiv, 'button.modButtonClass.left#' + del_button_id); + put(delButton, '[data-dojo-type="dijit/form/Button"]'); + // It looks like an 'x', so people will know that this means 'delete' + delButton.innerHTML = '×'; + var labButton = put(delDiv, 'button.modButtonClass.right', tag_name); + }); + // Create a button for adding a new tag. + var add_button_id = 'addtag_' + object.N; + var addButton = put(tagButtonContainer, 'button.modButtonClass#' + add_button_id); + put(addButton, '[data-dojo-type="dijit/form/Button"]'); + // Put a plus sign in there. + addButton.innerHTML = '+'; + + // The div is finally ready. Return it. + return commentDiv; + } + }, + { + field: 'image', + label: ' ', + renderCell: function(object, value, node, options) { + if (value) { + imgNode = put(node, 'img[height="60"][src="$"]', value); + return new dojox.image.LightboxNano({ href: value }, imgNode); + } + }, + get: function(object) { + if (hasImage(object)) { + return object.file; + } else { + return null; + } + }, + } + ]; + + var pane_contents_id = 'full_log_pane_div'; + + // Create the title pane with a placeholder div + var tp = new TitlePane({ + title: 'Full Event Log', + content: '<div id="' + pane_contents_id + '"></div>', + open: false + }); + logContentDiv.appendChild(tp.domNode); + + var grid = new declare([Grid, DijitRegistry])({ + minRowsPerPage: Nlogs, + columns: columns, + className: 'dgrid-autoheight', + renderRow: function(object,options) { + return put('div.supergrid-row', Grid.prototype.renderRow.call(this,object,options)); + } + }, pane_contents_id); + grid.renderArray(logs); + grid.set("sort", 'N', descending=true); + + } 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. + + var columns = [ + { field: 'N', label: 'No.' }, + { field: 'created', label: 'Log Entry Created (UTC)' }, + { field: 'issuer', label: 'Submitter', get: function(obj) { return obj.issuer.display_name; } }, + // Sometimes the comment contains HTML, so we just want to return whatever it has. + // This is where the link with the filename goes. Also the view in skymapViewer button + { + field: 'comment', + label: 'Comment', + renderCell: function(object, value, node, options) { + commentDiv = put(node, 'div'); + // Putting this in the innerHTML allows users to create comments in HTML. + // Whereas, inserting the comment with the put selector escapes it. + commentDiv.innerHTML += value + ' '; + if (object.filename) put(commentDiv, 'a[href=$]', object.file, object.filename); + // Create tag-related features + var tagButtonContainer = put(commentDiv, 'div.tagButtonContainerClass'); + // For each existing tag on a log message, we will make a little widget + // to delete it. + object.tag_names.forEach( function(tag_name) { + var delDiv = put(tagButtonContainer, 'div.tagDelButtonDivClass'); + var del_button_id = "del_button_" + object.N + '_' + tag_name; + var delButton = put(delDiv, 'button.modButtonClass.left#' + del_button_id); + put(delButton, '[data-dojo-type="dijit/form/Button"]'); + // It looks like an 'x', so people will know that this means 'delete' + delButton.innerHTML = '×'; + var labButton = put(delDiv, 'button.modButtonClass.right', tag_name); + }); + // Create a button for adding a new tag. + var add_button_id = 'addtag_' + object.N; + var addButton = put(tagButtonContainer, 'button.modButtonClass#' + add_button_id); + put(addButton, '[data-dojo-type="dijit/form/Button"]'); + // Put a plus sign in there. + addButton.innerHTML = '+'; + + // The div is finally ready. Return it. + return commentDiv; + } + }, + { + field: 'image', + label: ' ', + renderCell: function(object, value, node, options) { + if (value) { + imgNode = put(node, 'a[href="$"]', value); + put(imgNode, '[dojoType="dojox.image.LightboxNano"]'); + put(imgNode, 'img[height="60"][src="$"]', value); + return imgNode; + } + }, + get: function(object) { + if (hasImage(object)) { + return object.file; + } else { + return null; + } + }, + } + ]; + + var grid = new declare([Grid, DijitRegistry])({ + minRowsPerPage: Nlogs, + columns: columns, + className: 'dgrid-autoheight', + renderRow: function(object,options) { + return put('div.supergrid-row', Grid.prototype.renderRow.call(this,object,options)); + } + }, logContentDiv); + grid.renderArray(logs); + grid.set("sort", 'N', descending=true); + + } + + //------------------------------------------------------------------- + // Now that the annotations section has been added to the dom, we + // can work on its functionality. + //------------------------------------------------------------------- + var logtitle = dom.byId("logmessagetitle"); + var logtext = dom.byId("newlogtext"); + + var editor_div = dom.byId("editor"); + var preview_div = dom.byId("previewer"); + + // A pane holder for the form that will tag new log messages. + // I need it up here because we're going to integrate it with the + // editor components. + /* + dojo.style(preview_div, { 'display':'none'}); + dojo.style(editor_div, { 'display':'none'}); + + var button_element = dojo.create('button'); + dojo.place(button_element, logtitle, "right"); + var button = new Button({ + label: "Add Log Entry", + state: "add", + onClick: function(){ + if (this.state == 'add') { + dojo.style(editor_div, {'display':'block'}); + button.set('label', "Cancel Log Entry"); + button.set('state', 'cancel'); + editor.focus(); + } + else { + dojo.style(editor_div, {'display':'none'}); + dojo.style(preview_div, {'display':'none'}); + button.set('label', "Add Log Entry"); + button.set('state', 'add'); + editor.set('value',''); + } + }, + }, button_element); */ + + var savebutton = new Save({ + url: eventLogSaveUrl, + onSuccess: function (resp, ioargs) { + //this.inherited(resp, ioargs); + this.button.set("disabled", false); + location.reload(true); + }, + onError: function (error, ioargs) { + //this.inherited(error, ioargs); + this.button.set("disabled", false); + alert("o hai " + error); + }, + save: function(postdata) { + var newTagName = "analyst_comments"; + var postArgs = { + url: this.url, + content: { comment: postdata, tagname: newTagName }, + handleAs: "json" + }; + this.button.set("disabled", true); + var deferred = dojo.xhrPost(postArgs); + deferred.addCallback(dojo.hitch(this, this.onSuccess)); + deferred.addErrback(dojo.hitch(this, this.onError)); + // Call whatever function is necessary to attach the tag + // or add to the postdata and handle in the django view? + } + }); + + var previewbutton = new Preview({ + _preview: function(){ + var content = this.editor.get("value"); + preview_div.innerHTML = editor.get('value'); + dojo.style(preview_div, { + 'display':'block', + 'border': ".2em solid #900", + 'padding': '10px' + }); + MathJax.Hub.Queue(["Typeset",MathJax.Hub, preview_div]); + } + }); + + var editor = new Editor({ + extraPlugins : ['hiliteColor','|','createLink', + 'insertImage','fullscreen','viewsource','newpage', '|', previewbutton, savebutton] + }, editor_div); + editor.startup(); + + // For each log, attach callbacks for the tag delete and add buttons. + logs.forEach( function(log) { + // Attach a delete callback for each tag. + log.tag_names.forEach( function(tag_name) { + var del_button_id = "del_button_" + log.N + '_' + tag_name; + on(dom.byId(del_button_id), "click", getTagDelCallback(tag_name, log.N)); + new Tooltip({ connectId: del_button_id, label: "delete this tag" }); + }); + + // Attach an add tag callback for each log. + var add_button_id = 'addtag_' + log.N; + on(dom.byId(add_button_id), "click", getTagAddCallback(log.N)); + new Tooltip({ connectId: add_button_id, label: "tag this log message" }); + + }); + + // Handle the post to skymapViewer button. + // Tacking on an invisible div with a form inside. + sVdiv = put(annotationsDiv, 'div#sV_form_div[style="display: none"]'); + sVform = put(sVdiv, 'form#sV_form[method="post"][action="$"]', + encodeURI(skymapViewerUrl)); + put(sVform, 'input[type="hidden"][name="json"]'); + put(sVform, 'input[type="hidden"][name="embb"]'); + put(sVform, 'input[type="submit"][value="View in skymapViewer!"]'); + + var sV_button = dom.byId("sV_button"); + if (sV_button) { + on(sV_button, "click", function() { + console.log("You clicked the button!"); + var embblog_json_url = embbEventLogListUrl; + var embblog_json; + dojo.xhrGet({ + url: embblog_json_url + "?format=json", + async: true, + load: function(embblog_json) { + + // fetch JSON content. + dojo.xhrGet({ + url: skymapJsonUrl, + load: function(result) { + // Find the form and set its value to the appropriate JSON + sV_form = dom.byId("sV_form"); + // Shove the skymap.json contents into the value for the second form field. + sV_form.elements[1].value = result; + sV_form.elements[2].value = embblog_json; + // Submit the form, which takes the user to the skymapViewer server. + sV_form.submit(); + } + }); // end of inside ajax + } + }); // end of outside ajax + }); + } + + + }); + }); + + +}); + diff --git a/templates/gracedb/neighbors_frag.html b/templates/gracedb/neighbors_frag.html index d27c77080c187b7e3ac37323a8ec4582b985bd9f..7b2df91b3706aab404958d9d29c927998fc79992 100644 --- a/templates/gracedb/neighbors_frag.html +++ b/templates/gracedb/neighbors_frag.html @@ -1,11 +1,11 @@ {% load timeutil %} {% load scientific %} <div id="neighbors" class="content-area"> -<h3>Neighbors +<h2>Neighbors <span data-dojo-type="dijit/InlineEditBox" data-dojo-props="editor:'dijit/form/TextBox', editorParams:{constraints:{places:0} }" width="100px" title="window" - onchange="refresh_neighbors(this.value)">{{neighbor_delta}}</span></h3> + onchange="refresh_neighbors(this.value)">{{neighbor_delta}}</span></h2> {% if nearby %} @@ -18,15 +18,15 @@ <th>Search</th> <th>Instruments</th> <th> - {{ "ngps"|timeselect:"gps" }} - Event Time + <div id="neighbors_event_ts"></div> + <div> Event Time </div> </th> <th>Δgpstime</th> <th>FAR (Hz)</th> <th>Links</th> <th> - {{"ncreated"|timeselect:"utc" }} - Submitted + <div id="neighbors_created_ts"></div> + <div> Submitted </div> </th> </tr> {% for delta, object in nearby %}