Skip to content
Snippets Groups Projects
Commit 4f28c47a authored by Branson Stephens's avatar Branson Stephens
Browse files

Merge branch 'tag_dev'

parents 75b7cfd5 7678c132
No related branches found
No related tags found
No related merge requests found
Showing
with 1160 additions and 218 deletions
from models import Event, EventLog, User, Group
from models import Label, Labelling, Slot
from models import Label, Labelling, Tag
from django.contrib import admin
class EventAdmin(admin.ModelAdmin):
......@@ -30,8 +30,9 @@ class LabellingAdmin(admin.ModelAdmin):
list_display = [ 'event', 'label', 'creator' ]
search_fields = [ 'event__id', 'label__name', 'creator__name' ]
class SlotAdmin(admin.ModelAdmin):
list_display = [ 'event', 'name', 'value' ]
class TagAdmin(admin.ModelAdmin):
list_display = [ 'name', 'displayName' ]
filter_vertical = [ 'eventlogs' ]
admin.site.register(Event, EventAdmin)
admin.site.register(EventLog, EventLogAdmin)
......@@ -39,4 +40,4 @@ admin.site.register(User, UserAdmin)
admin.site.register(Group)
admin.site.register(Label, LabelAdmin)
admin.site.register(Labelling, LabellingAdmin)
admin.site.register(Slot, SlotAdmin)
admin.site.register(Tag, TagAdmin)
......@@ -8,7 +8,7 @@ from django.conf import settings
import json
from gracedb.models import Event, Group, EventLog, Slot
from gracedb.models import Event, Group, EventLog, Tag
from gracedb.views import create_label
from translator import handle_uploaded_data
......@@ -115,12 +115,8 @@ def eventToDict(event, columns=None, request=None):
"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),
}
# XXX Jam the slots in here? Could just have a list of slot names instead of
# all these links. But the links might be useful??
rv['slots'] = {}
for slot in Slot.objects.filter(event=event).order_by('name'):
rv['slots'][slot.name] = reverse("slot", args=[graceid, slot.name], request=request)
return rv
......@@ -472,12 +468,16 @@ class EventLabel(APIView):
# Janky serialization
def eventLogToDict(log, n=None, request=None):
# XXX Messy. n should not be here but in the model.
taglist_uri = None
if (n is None) and request:
uri = request.build_absolute_uri()
elif n is not None and request:
uri = reverse("eventlog-detail",
args=[log.event.graceid(), n],
request=request)
taglist_uri = reverse("eventlogtag-list",
args=[log.event.graceid(), n],
request=request)
else:
uri = None
return {
......@@ -485,6 +485,7 @@ def eventLogToDict(log, n=None, request=None):
"created" : log.created,
"issuer" : log.issuer.name,
"self" : uri,
"tags" : taglist_uri,
}
class EventLogList(APIView):
......@@ -521,8 +522,10 @@ class EventLogList(APIView):
return Response(rv)
def post(self, request, graceid):
logger = logging.getLogger(__name__)
event = Event.getByGraceid(graceid)
message = request.DATA.get('message')
tagname = request.DATA.get('tagname')
logentry = EventLog(
event=event,
issuer=request.ligouser,
......@@ -533,6 +536,16 @@ class EventLogList(APIView):
rv = eventLogToDict(logentry, n, request=request)
response = Response(rv, status=status.HTTP_201_CREATED)
response['Location'] = rv['self']
if tagname:
n = logentry.getN()
# XXX This is not what these API views are really meant for, but...
tmp = EventLogTagDetail()
retval = tmp.put(request, graceid, n, tagname)
# XXX This seems like a bizarre way of getting an error message out.
if retval.status_code != 201:
response['tagWarning'] = 'Error creating tag.'
return response
class EventLogDetail(APIView):
......@@ -547,7 +560,264 @@ class EventLogDetail(APIView):
return Response("Log Entry Not Found",
status=status.HTTP_404_NOT_FOUND)
rv = event.eventlog_set.order_by("created").all()[int(n)]
return Response(eventLogToDict(rv, request=request))
# XXX I (Branson) put the n argument here. Why not?
# We might as well since we have it, right?
return Response(eventLogToDict(rv, n, request=request))
#==================================================================
# Tags
def tagToDict(tag, columns=None, request=None, event=None, n=None):
"""Convert a tag to a dictionary.
Output depends on the level of specificity.
"""
rv = {}
rv['name'] = tag.name
rv['displayName'] = tag.displayName
if event:
if n:
# We want a link to the self only. End of the line.
rv['links'] = {
"self" : reverse("eventlogtag-detail",
args=[event.graceid(),n,tag.name],
request=request)
}
else:
# Links to all log messages of the event with this tag.
rv['links'] = {
"logs" : [reverse("eventlog-detail",
args=[event.graceid(),log.getN()],
request=request)
for log in event.getLogsForTag(tag.name)],
"self" : reverse("eventtag-detail",
args=[event.graceid(),tag.name],
request=request)
}
else:
# XXX Unclear what the tag detail resource should be at this level.
# For now, return an empty list.
pass
# rv['links'] = {
# "events" : [reverse("event-detail",
# args=[event.graceid()],
# request=request)
# for event in tag.getEvents()],
# "self" : reverse("tag-detail",
# args=[tag.name],
# request=request)
# }
return rv
class TagList(APIView):
"""Tag List Resource
"""
authentication_classes = (LigoAuthentication,)
permission_classes = (IsAuthenticated,)
def get(self, request):
# Return a list of links to all tag objects.
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() ]
}
return Response(rv)
# XXX Unclear what the tag detail resource should be.
# class TagDetail(APIView):
# """Tag Detail Resource
# """
# authentication_classes = (LigoAuthentication,)
# permission_classes = (IsAuthenticated,)
#
# def get(self, request, tagname):
# try:
# tag = Tag.objects.filter(name=tagname)[0]
# except Tag.DoesNotExist:
# return Response("Tag not found.",
# status=status.HTTP_404_NOT_FOUND)
# return Response(tagToDict(tag,request=request))
class EventTagList(APIView):
"""Event Tag List Resource
"""
authentication_classes = (LigoAuthentication,)
permission_classes = (IsAuthenticated,)
def get(self, request, graceid):
# Return a list of links to all tags for this event.
try:
event = Event.getByGraceid(graceid)
except Event.DoesNotExist:
# XXX Real error message.
return Response("Event does not exist.",
status=status.HTTP_404_NOT_FOUND)
rv = {
'tags' : [ reverse("eventtag-detail",args=[graceid,
tag.name],
request=request)
for tag in event.getAvailableTags()]
}
return Response(rv)
class EventTagDetail(APIView):
"""Event Tag List Resource
"""
authentication_classes = (LigoAuthentication,)
permission_classes = (IsAuthenticated,)
def get(self, request, graceid, tagname):
try:
event = Event.getByGraceid(graceid)
except Event.DoesNotExist:
# XXX Real error message.
return Response("Event does not exist.",
status=status.HTTP_404_NOT_FOUND)
try:
tag = Tag.objects.filter(name=tagname)[0]
rv = tagToDict(tag,event=event,request=request)
return Response(rv)
except Tag.DoesNotExist:
return Response("No such tag for event.",
status=status.HTTP_404_NOT_FOUND)
class EventLogTagList(APIView):
"""Event Log Tag List Resource
"""
authentication_classes = (LigoAuthentication,)
permission_classes = (IsAuthenticated,)
def get(self, request, graceid, n):
# Return a list of links to tags associated with a given log message
try:
event = Event.getByGraceid(graceid)
eventlog = event.eventlog_set.order_by("created").all()[int(n)]
except Event.DoesNotExist:
# XXX Real error message.
return Response("Event does not exist.",
status=status.HTTP_404_NOT_FOUND)
except:
# XXX Real error message.
return Response("Log does not exist.",
status=status.HTTP_404_NOT_FOUND)
rv = {
'tags' : [ reverse("eventlogtag-detail",
args=[graceid,
n, tag.name],
request=request)
for tag in eventlog.tag_set.all()]
}
return Response(rv)
class EventLogTagDetail(APIView):
"""Event Log Tag Detail Resource
"""
authentication_classes = (LigoAuthentication,)
permission_classes = (IsAuthenticated,)
def get(self, request, graceid, n, tagname):
try:
event = Event.getByGraceid(graceid)
eventlog = event.eventlog_set.order_by("created").all()[int(n)]
except Event.DoesNotExist:
# XXX Real error message.
return Response("Event does not exist.",
status=status.HTTP_404_NOT_FOUND)
except:
# XXX Real error message.
return Response("Log does not exist.",
status=status.HTTP_404_NOT_FOUND)
try:
tag = eventlog.tag_set.filter(name=tagname)[0]
# Serialize
return Response(tagToDict(tag,event=event,n=n,request=request))
except:
return Response("Tag not found.",status=status.HTTP_404_NOT_FOUND)
def put(self, request, graceid, n, tagname):
logger = logging.getLogger(__name__)
try:
event = Event.getByGraceid(graceid)
eventlog = event.eventlog_set.order_by("created").all()[int(n)]
except Event.DoesNotExist:
# XXX Real error message.
return Response("Event does not exist.",
status=status.HTTP_404_NOT_FOUND)
except:
# XXX Real error message.
return Response("Log does not exist.",
status=status.HTTP_404_NOT_FOUND)
try:
# Has this tag-eventlog relationship already been created? If so, kick out.
# Actually, adding the eventlog to the tag would not hurt anything--no
# duplicate entry would be made in the database. However, we don't want
# an extra log entry, or a deceptive HTTP response (i.e., one telling the
# client that the creation was sucessful when, in fact, the database
# was unchanged.
tag = eventlog.tag_set.filter(name=tagname)[0]
msg = "Log already has tag %s" % unicode(tag)
return Response(msg,status=status.HTTP_409_CONFLICT)
except:
# Look for the tag. If it doesn't already exist, create it.
try:
tag = Tag.objects.filter(name=tagname)[0]
except:
displayName = request.DATA.get('displayName')
tag = Tag(name=tagname, displayName=displayName)
tag.save()
# Now add the log message to this tag.
tag.eventlogs.add(eventlog)
# Create a log entry to document the tag creation.
msg = "Tagged message %s: %s " % (n, tagname)
logentry = EventLog(event=event,
issuer=request.ligouser,
comment=msg)
logentry.save()
return Response("Tag created.",status=status.HTTP_201_CREATED)
def delete(self, request, graceid, n, tagname):
try:
event = Event.getByGraceid(graceid)
eventlog = event.eventlog_set.order_by("created").all()[int(n)]
except Event.DoesNotExist:
# XXX Real error message.
return Response("Event does not exist.",
status=status.HTTP_404_NOT_FOUND)
except:
# XXX Real error message.
return Response("Log does not exist.",
status=status.HTTP_404_NOT_FOUND)
try:
tag = eventlog.tag_set.filter(name=tagname)[0]
tag.eventlogs.remove(eventlog)
# Is the tag empty now? If so we can delete it.
if not tag.eventlogs.all():
tag.delete()
# Create a log entry to document the tag creation.
msg = "Removed tag %s for message %s. " % (tagname, n)
logentry = EventLog(event=event,
issuer=request.ligouser,
comment=msg)
logentry.save()
return Response("Tag deleted.",status=status.HTTP_200_OK)
except:
return Response("Tag not found.",status=status.HTTP_404_NOT_FOUND)
#==================================================================
# Root Resource
......@@ -579,9 +849,16 @@ class GracedbRoot(APIView):
labels = labels.replace("G1200", "{graceid}")
labels = labels.replace("thelabel", "{label}")
slot = reverse("slot", args=["G1200", "slotname"], request=request)
slot = slot.replace("G1200", "{graceid}")
slot = slot.replace("slotname", "{slotname}")
taglist = reverse("eventlogtag-list", args=["G1200", "0"], request=request)
taglist = taglist.replace("G1200", "{graceid}")
taglist = taglist.replace("0", "{n}")
tag = reverse("eventlogtag-detail", args=["G1200", "0", "tagname"], request=request)
tag = tag.replace("G1200", "{graceid}")
tag = tag.replace("0", "{n}")
tag = tag.replace("tagname", "{tagname}")
# XXX Need a template for the tag list?
templates = {
"event-detail-template" : detail,
......@@ -589,7 +866,8 @@ class GracedbRoot(APIView):
"event-label-template" : labels,
"files-template" : files,
"filemeta-template" : filemeta,
"slot-template" : slot,
"tag-template" : tag,
"taglist-template" : taglist,
}
return Response({
......@@ -816,98 +1094,3 @@ class FileMeta(APIView):
permission_classes = (IsAuthenticated,)
pass
#==================================================================
# Slots
class EventSlot(APIView):
"""A slot associated with an event.
"""
# Get the value of a slot. This will be a filename.
def get(self, request, graceid, slotname):
try:
event = Event.getByGraceid(graceid)
except Event.DoesNotExist:
# XXX Real error message.
return Response("Event does not exist.",
status=status.HTTP_404_NOT_FOUND)
if not slotname:
return Response(dict([(s.name, s.value)
for s in Slot.objects.filter(event=event)]))
try:
slot = Slot.objects.filter(event=event).filter(name=slotname)[0]
except:
# Okay, no slot yet. Probably want an error message.
# Try looking for files that contain the slot name.
return Response("No slot. Search based on slotname not implemented yet.",
status=status.HTTP_404_NOT_FOUND)
filename = slot.value
rv = {}
rv['value'] = filename
return Response(rv)
# Create a slot. The slot's value will be a filename.
# This file is assumed to have already been uploaded.
def put(self, request, graceid, slotname):
try:
event = Event.getByGraceid(graceid)
except Event.DoesNotExist:
# XXX Real error message.
return Response("Event does not exist.",
status=status.HTTP_404_NOT_FOUND)
filename = request.DATA.get('filename')
# Interestingly, the None object seems to be converted to a string
# when encoded in the HTTP request body. Hence the 'None' string
# below. If somebody intentionally named a file 'None', then
# they deserve to get this error message.
if filename=='' or filename=='None' or filename==None:
return Response("Please submit a filename or upload a file.",
status=status.HTTP_400_BAD_REQUEST)
# UGLY hack to deal with /private vs /general dirs
general = False
tmpFilename = filename
if filename.startswith("general/"):
tmpFilename = filename[len("general/"):]
general = True
filepath = os.path.join(event.datadir(general), tmpFilename)
# Check for existence of the file.
if not os.path.exists(filepath):
return Response("No slot created because file does not exist",
status=status.HTTP_404_NOT_FOUND)
# Check for existence of the slot. If it exists, simply update the
# existing slot.
try:
slot = Slot.objects.filter(event=event).filter(name=slotname)[0]
slot.value = filename
slot.save()
except:
# Create the slot.
slot = Slot(event=event,name=slotname,value=filename)
slot.save()
return Response("Slot created or updated.",status=status.HTTP_201_CREATED)
# Delete a slot.
def delete(self, request, graceid, slotname):
try:
event = Event.getByGraceid(graceid)
except Event.DoesNotExist:
# XXX Real error message.
return Response("Event does not exist.",
status=status.HTTP_404_NOT_FOUND)
# Gotta find the poor devil before we can delete him.
try:
slot = Slot.objects.filter(event=event).filter(name=slotname)[0]
except:
# Okay, no slot yet. Probably want an error message.
# Try looking for files that contain the slot name.
return Response("No such slot.",
status=status.HTTP_404_NOT_FOUND)
slot.delete()
return Response("Slot deleted.",status=status.HTTP_200_OK)
# -*- coding: utf-8 -*-
import datetime
from south.db import db
from south.v2 import SchemaMigration
from django.db import models
class Migration(SchemaMigration):
def forwards(self, orm):
# Adding model 'Tag'
db.create_table('gracedb_tag', (
('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
('name', self.gf('django.db.models.fields.CharField')(max_length=100)),
('displayName', self.gf('django.db.models.fields.CharField')(max_length=200, null=True)),
))
db.send_create_signal('gracedb', ['Tag'])
# Adding M2M table for field eventlogs on 'Tag'
db.create_table('gracedb_tag_eventlogs', (
('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
('tag', models.ForeignKey(orm['gracedb.tag'], null=False)),
('eventlog', models.ForeignKey(orm['gracedb.eventlog'], null=False))
))
db.create_unique('gracedb_tag_eventlogs', ['tag_id', 'eventlog_id'])
def backwards(self, orm):
# Deleting model 'Tag'
db.delete_table('gracedb_tag')
# Removing M2M table for field eventlogs on 'Tag'
db.delete_table('gracedb_tag_eventlogs')
models = {
'gracedb.approval': {
'Meta': {'object_name': 'Approval'},
'approvedEvent': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['gracedb.Event']"}),
'approver': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['gracedb.User']"}),
'approvingCollaboration': ('django.db.models.fields.CharField', [], {'max_length': '1'}),
'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
},
'gracedb.coincinspiralevent': {
'Meta': {'ordering': "['-id']", 'object_name': 'CoincInspiralEvent', '_ormbases': ['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'}),
'event_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "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'})
},
'gracedb.event': {
'Meta': {'ordering': "['-id']", 'object_name': 'Event'},
'analysisType': ('django.db.models.fields.CharField', [], {'max_length': '20'}),
'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': "orm['gracedb.Group']"}),
'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': "orm['gracedb.Label']", 'through': "orm['gracedb.Labelling']", 'symmetrical': 'False'}),
'likelihood': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
'nevents': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True'}),
'submitter': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['gracedb.User']"}),
'uid': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '20'})
},
'gracedb.eventlog': {
'Meta': {'ordering': "['-created']", 'object_name': 'EventLog'},
'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': "orm['gracedb.Event']"}),
'filename': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '100'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'issuer': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['gracedb.User']"})
},
'gracedb.group': {
'Meta': {'object_name': 'Group'},
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'managers': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['gracedb.User']", 'symmetrical': 'False'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '20'})
},
'gracedb.label': {
'Meta': {'object_name': 'Label'},
'defaultColor': ('django.db.models.fields.CharField', [], {'default': "'black'", 'max_length': '20'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '20'})
},
'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': "orm['gracedb.User']"}),
'event': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['gracedb.Event']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'label': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['gracedb.Label']"})
},
'gracedb.multiburstevent': {
'Meta': {'ordering': "['-id']", 'object_name': 'MultiBurstEvent', '_ormbases': ['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'}),
'event_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "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'})
},
'gracedb.slot': {
'Meta': {'unique_together': "(('event', 'name'),)", 'object_name': 'Slot'},
'event': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['gracedb.Event']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'value': ('django.db.models.fields.CharField', [], {'max_length': '100'})
},
'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': "orm['gracedb.EventLog']", 'symmetrical': 'False'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
},
'gracedb.user': {
'Meta': {'ordering': "['name']", 'object_name': 'User'},
'dn': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'email': ('django.db.models.fields.EmailField', [], {'max_length': '75'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'principal': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'unixid': ('django.db.models.fields.CharField', [], {'max_length': '25'})
}
}
complete_apps = ['gracedb']
\ No newline at end of file
# -*- coding: utf-8 -*-
import datetime
from south.db import db
from south.v2 import SchemaMigration
from django.db import models
class Migration(SchemaMigration):
def forwards(self, orm):
# Removing unique constraint on 'Slot', fields ['event', 'name']
db.delete_unique('gracedb_slot', ['event_id', 'name'])
# Deleting model 'Slot'
db.delete_table('gracedb_slot')
def backwards(self, orm):
# Adding model 'Slot'
db.create_table('gracedb_slot', (
('value', self.gf('django.db.models.fields.CharField')(max_length=100)),
('event', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['gracedb.Event'])),
('name', self.gf('django.db.models.fields.CharField')(max_length=100)),
('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
))
db.send_create_signal('gracedb', ['Slot'])
# Adding unique constraint on 'Slot', fields ['event', 'name']
db.create_unique('gracedb_slot', ['event_id', 'name'])
models = {
'gracedb.approval': {
'Meta': {'object_name': 'Approval'},
'approvedEvent': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['gracedb.Event']"}),
'approver': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['gracedb.User']"}),
'approvingCollaboration': ('django.db.models.fields.CharField', [], {'max_length': '1'}),
'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
},
'gracedb.coincinspiralevent': {
'Meta': {'ordering': "['-id']", 'object_name': 'CoincInspiralEvent', '_ormbases': ['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'}),
'event_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "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'})
},
'gracedb.event': {
'Meta': {'ordering': "['-id']", 'object_name': 'Event'},
'analysisType': ('django.db.models.fields.CharField', [], {'max_length': '20'}),
'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': "orm['gracedb.Group']"}),
'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': "orm['gracedb.Label']", 'through': "orm['gracedb.Labelling']", 'symmetrical': 'False'}),
'likelihood': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
'nevents': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True'}),
'submitter': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['gracedb.User']"}),
'uid': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '20'})
},
'gracedb.eventlog': {
'Meta': {'ordering': "['-created']", 'object_name': 'EventLog'},
'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': "orm['gracedb.Event']"}),
'filename': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '100'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'issuer': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['gracedb.User']"})
},
'gracedb.group': {
'Meta': {'object_name': 'Group'},
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'managers': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['gracedb.User']", 'symmetrical': 'False'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '20'})
},
'gracedb.label': {
'Meta': {'object_name': 'Label'},
'defaultColor': ('django.db.models.fields.CharField', [], {'default': "'black'", 'max_length': '20'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '20'})
},
'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': "orm['gracedb.User']"}),
'event': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['gracedb.Event']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'label': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['gracedb.Label']"})
},
'gracedb.multiburstevent': {
'Meta': {'ordering': "['-id']", 'object_name': 'MultiBurstEvent', '_ormbases': ['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'}),
'event_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "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'})
},
'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': "orm['gracedb.EventLog']", 'symmetrical': 'False'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
},
'gracedb.user': {
'Meta': {'ordering': "['name']", 'object_name': 'User'},
'dn': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'email': ('django.db.models.fields.EmailField', [], {'max_length': '75'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'principal': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'unixid': ('django.db.models.fields.CharField', [], {'max_length': '25'})
}
}
complete_apps = ['gracedb']
\ No newline at end of file
......@@ -173,6 +173,25 @@ class Event(models.Model):
def __unicode__(self):
return self.graceid()
# Return a list of distinct tags associated with the log messages of this
# event.
def getAvailableTags(self):
tagset_list = [log.tag_set.all() for log in self.eventlog_set.all()]
taglist = []
for tagset in tagset_list:
for tag in tagset:
taglist.append(tag)
# Eliminate duplicates
return list(set(taglist))
def getLogsForTag(self,tagname):
loglist = []
for log in self.eventlog_set.all():
for tag in log.tag_set.all():
if tag.name==tagname:
loglist.append(log)
return loglist
class EventLog(models.Model):
class Meta:
ordering = ["-created"]
......@@ -193,6 +212,16 @@ class EventLog(models.Model):
# XXX hacky
return self.filename and self.filename[-3:].lower() in ['png','gif','jpg']
def getN(self):
# XXX also hacky?
# I think it would still work if some logs were removed from the database.
logset = self.event.eventlog_set.order_by("created")
# XXX This actually evaluates the queryset. This may be a problem if
# there are a huge number of log messages for this event and they
# take up a lot of memory
logset = list(logset)
return logset.index(self)
class Labelling(models.Model):
event = models.ForeignKey(Event)
label = models.ForeignKey(Label)
......@@ -240,21 +269,26 @@ class MultiBurstEvent(Event):
ligo_angle = models.FloatField(null=True)
ligo_angle_sig = models.FloatField(null=True)
## Slots (user-defined event attributes)
class Slot(models.Model):
"""Slot Model"""
# Does the slot need to have a submitter column?
class Meta:
unique_together = (('event', 'name'))
event = models.ForeignKey(Event)
name = models.CharField(max_length=100)
value = models.CharField(max_length=100)
## Tags (user-defined log message attributes)
class Tag(models.Model):
"""Tag Model"""
# XXX Does the tag need to have a submitter column?
# No, because creating a tag will generate a log message.
# For the same reason, a timstamp is not necessary.
eventlogs = models.ManyToManyField(EventLog)
name = models.CharField(max_length=100)
displayName = models.CharField(max_length=200,null=True)
# In case the slot value is not a filename, this will just return None.
def fileurl(self):
if self.value:
return reverse('file', args=[self.event.graceid(), self.value])
def __unicode__(self):
if self.displayName:
return self.displayName
else:
return None
return self.name
# def getEvents(self):
# # XXX Any way of doing this with filters?
# # We would need to filter for a non-null intersection of the
# # set of log messages in the event with the set of log
# # messages in the tag.
# eventlist = [log.event for log in self.eventlogs.all()]
from django import template
from django.utils.encoding import force_unicode
from django.utils.safestring import mark_safe
from ..models import Tag, EventLog
register = template.Library()
@register.filter("getLogsForTag")
def getLogsForTag(event,name=None):
if event is None:
return None
try:
if name:
return event.getLogsForTag(name)
else:
return None
except:
# Either there is no such tag or something went wrong.
# In either case, we want the template to just ignore it.
return None
@register.filter("tagUnicode")
def tagUnicode(tag):
return unicode(tag);
@register.filter("logsForTagHaveImage")
def logsForTagAllHaveImages(event,name=None):
if event is None:
return None
try:
if name:
loglist = event.getLogsForTag(name)
# Start by assuming no images
bool_rv = False
for log in loglist:
# If any of the log messages do have an image
# attached, we flip the return value to true.
if log.hasImage():
bool_rv = True
break
return bool_rv
else:
return None
except:
# Either there is no such tag or something went wrong.
# In either case, we want the template to just ignore it.
return None
@register.filter("logsForTagHaveText")
def logsForTagHaveText(event,name=None):
if event is None:
return None
try:
if name:
loglist = event.getLogsForTag(name)
bool_rv = False
for log in loglist:
if not log.hasImage():
bool_rv = True
break
return bool_rv
else:
return None
except:
# Either there is no such tag or something went wrong.
# In either case, we want the template to just ignore it.
return None
from django import template
from django.utils.encoding import force_unicode
from django.utils.safestring import mark_safe
from ..models import Slot, EventLog
register = template.Library()
@register.filter("slot")
def slot(event,name=None):
if event is None:
return None
try:
if name:
return Slot.objects.filter(event=event).filter(name__exact=name)[0]
else:
return Slot.objects.filter(event=event)
except:
# Either there is no such slot or something went wrong.
# In either case, we want the template to just ignore it.
return None
......@@ -15,6 +15,7 @@ urlpatterns = patterns('gracedb.views',
url (r'^(?P<graceid>[GEHT]\d+)$', 'view', name="view2"),
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+)/log/(?P<num>\d+)/tag/(?P<tagname>\w+)$', 'taglogentry', name="taglogentry"),
# (r'^view/(?P<uid>[\w\d]+)', 'view'),
......
......@@ -5,7 +5,10 @@ from django.conf.urls.defaults import patterns, url
from gracedb.api import GracedbRoot
from gracedb.api import EventList, EventDetail
from gracedb.api import EventLogList, EventLogDetail
from gracedb.api import EventSlot
from gracedb.api import TagList
# from gracedb.api import TagDetail
from gracedb.api import EventTagList, EventTagDetail
from gracedb.api import EventLogTagList, EventLogTagDetail
from gracedb.api import Files, FileMeta
from gracedb.api import EventNeighbors, EventLabel
......@@ -27,6 +30,21 @@ urlpatterns = patterns('gracedb.api',
url (r'events/(?P<graceid>[GEHT]\d+)/log/(?P<n>\d+)$',
EventLogDetail.as_view(), name='eventlog-detail'),
# Tag Resources
url (r'^tag/$',
TagList.as_view(), name='tag-list'),
# 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/$',
EventTagList.as_view(), name='eventtag-list'),
url (r'events/(?P<graceid>[GEHT]\d+)/tag/(?P<tagname>\w+)$',
EventTagDetail.as_view(), name='eventtag-detail'),
url (r'events/(?P<graceid>[GEHT]\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+)$',
EventLogTagDetail.as_view(), name='eventlogtag-detail'),
# Event File Resources
# events/{graceid}/files/[{filename}[/{version}]]
url (r'^events/(?P<graceid>\w[\d]+)/files/(?P<filename>.+)?$',
......@@ -40,11 +58,6 @@ urlpatterns = patterns('gracedb.api',
url (r'^events/(?P<graceid>\w[\d]+)/labels/(?P<label>.+)?$',
EventLabel.as_view(), name="labels"),
# Event Slots
# events/{graceid}/slot/[{slotname}]
url (r'^events/(?P<graceid>[GEHT]\d+)/slot/(?P<slotname>.+)?$',
EventSlot.as_view(), name="slot"),
# Event Neighbors
# events/{graceid}/neighbors/[?delta=(N|(N,N))]
url (r'^events/(?P<graceid>\w[\d]+)/neighbors/$',
......
......@@ -10,7 +10,7 @@ from django.utils.safestring import mark_safe
from django.views.generic.list_detail import object_detail, object_list
from models import Event, Group, EventLog, Labelling, Label, User
from models import Event, Group, EventLog, Labelling, Label, User, Tag
from models import CoincInspiralEvent
from models import MultiBurstEvent
from forms import CreateEventForm, EventSearchForm, SimpleSearchForm
......@@ -29,6 +29,7 @@ from django.conf import settings
from templatetags.scientific import scientific
from buildVOEvent import buildVOEvent, submitToSkyalert
import logging
# XXX This should be configurable / moddable or something
MAX_QUERY_RESULTS = 1000
......@@ -443,6 +444,7 @@ def sanitize_html(data):
def logentry(request, graceid, num=None):
logger = logging.getLogger(__name__)
try:
event = Event.getByGraceid(graceid)
except Event.DoesNotExist:
......@@ -452,6 +454,28 @@ def logentry(request, graceid, num=None):
elog = EventLog(event=event, issuer=request.ligouser)
elog.comment = request.POST.get('comment') or request.GET.get('comment')
elog.save()
logger.debug("just saved log entry")
tagname = request.POST.get('tagname')
logger.debug("tagname = %s" % tagname)
if tagname:
# Look for the tag. If it doesn't already exist, create it.
try:
tag = Tag.objects.filter(name=tagname)[0]
except:
displayName = request.POST.get('displayName')
logger.debug("disp name = %s" % displayName)
tag = Tag(name=tagname, displayName=displayName)
tag.save()
logger.debug("just saved tag")
tag.eventlogs.add(elog)
# Create a log entry to document the tag creation.
num = elog.getN()
msg = "Tagged message %s: %s " % (num, tagname)
tlog = EventLog(event=event,
issuer=request.ligouser,
comment=msg)
tlog.save()
else:
try:
elog = event.eventlog_set.order_by('created').all()[int(num)]
......@@ -466,6 +490,8 @@ def logentry(request, graceid, num=None):
rv['issuer'] = elog.issuer.name
rv['created'] = elog.created.isoformat()
rv['comment'] = elog.comment
if tagname:
rv['tagname'] = tagname
return HttpResponse(json.dumps(rv), content_type="application/json")
......@@ -537,6 +563,7 @@ def view(request, graceid):
context['nearby'] = [(event.gpstime - a.gpstime, event)
for event in a.neighbors()]
context['skyalert_authorized'] = skyalert_authorized(request)
context['blessed_tags'] = settings.BLESSED_TAGS
return render_to_response(
[ 'gracedb/event_detail_{0}.html'.format(a.analysisType),
'gracedb/event_detail.html'],
......@@ -993,3 +1020,53 @@ def latest(request):
context,
context_instance=RequestContext(request))
#-----------------------------------------------------------------------------------
# For tags. A new view function. We need this because the API one would want users
# to have certs stored in their browser.
#-----------------------------------------------------------------------------------
def taglogentry(request, graceid, num, tagname):
try:
event = Event.getByGraceid(graceid)
eventlog = event.eventlog_set.order_by("created").all()[int(num)]
except:
# Either the event or the log does not exist.
raise Http404
if request.method == "POST":
try:
# Has this tag-eventlog relationship already been created?
tag = eventlog.tag_set.filter(name=tagname)[0]
msg = "Log already has tag %s" % tagname
return HttpResponse(msg, content_type="text")
except:
# Look for the tag. If it doesn't already exist, create it.
try:
tag = Tag.objects.filter(name=tagname)[0]
except:
displayName = request.POST['displayName']
tag = Tag(name=tagname, displayName=displayName)
tag.save()
# Now add the log message to this tag.
tag.eventlogs.add(eventlog)
# Create a log entry to document the tag creation.
msg = "Tagged message %s: %s " % (num, tagname)
logentry = EventLog(event=event,
issuer=request.ligouser,
comment=msg)
logentry.save()
else:
# We will only allow PUT here. Anything else is a bad request: 400
return HttpResponseBadRequest
# Hopefully, this will only ever be called form inside a script. Just in case...
if not request.is_ajax():
return HttpResponseRedirect(reverse(view, args=[graceid]))
# no need for a JSON response.
msg = "Successfully applied tag %s to log message %s." % (tagname, num)
msg = msg + " Refresh to see chages (if any) to the presentation."
return HttpResponse(msg, content_type="text")
......@@ -37,6 +37,15 @@ XMPP_ALERT_CHANNELS = [
'test_grb',
]
BLESSED_TAGS = [
'psd',
'sky_loc',
'strain',
'tfplots',
'sig_info',
'audio',
]
DATABASES = {
'default' : {
'NAME' : 'gracedb',
......
......@@ -9,6 +9,45 @@ html, body {
table.event th {padding:3px;border:none;text-align:center;vertical-align:bottom;}
table.event {border-bottom:1px solid gray;}
/* Branson added */
table.event {width:100%}
table.analysis_specific {width:100%}
.content-area {
margin-top: 10px;
margin-bottom: 30px;
margin-left: 15px;
margin-right: 15px;
}
table.figures tr.figrow {text-align:center;}
table.figures {width:300px;height:270px;border:1px solid gray;}
.tundra.eventDetail .pmTitlePaneClass .dijitOpen .dijitArrowNode {
background-repeat: no-repeat;
height: 14px;
width: 14px;
}
.tundra.eventDetail .pmTitlePaneClass .dijitClosed .dijitArrowNode {
background-repeat: no-repeat;
height: 14px;
width: 14px;
}
.tundra.eventDetail .pmTitlePaneClass .dijitClosed .dijitArrowNode {
background-image: url('/branson-static/images/plusButton.gif');
background-position: 0px 0px;
}
.tundra.eventDetail .pmTitlePaneClass .dijitOpen .dijitArrowNode {
background-image: url('/branson-static/images/minusButton.gif');
background-position: 0px 0px;
}
/* End Branson */
td, th {padding:3px;border:none;}
tr th {text-align:left;background-color:#f0f0f0;color:#333;}
......
static/images/minusButton.gif

148 B

static/images/plusButton.gif

212 B

......@@ -2,7 +2,7 @@
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<link rel="stylesheet" href="/gracedb-static/css/style.css" />
<link rel="stylesheet" href="/branson-static/css/style.css" />
<title>GraceDb | {% block title %}{% endblock %}</title>
<!-- START TESTING -->
<script type="text/javascript">
......
......@@ -2,27 +2,32 @@
{% load timeutil %}
{% load scientific %}
{% load sanitize_html %}
{% load slot %}
{% load logtags %}
{% block title %}View {{ object.graceid }}{% endblock %}
{% block heading %}{% endblock %}
{% block bodyattrs %}class="tundra"{% 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.7.1/dojo/dojo.js" type="text/javascript"></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="{{MEDIA_URL}}js/labeltips.js" type="text/javascript"></script>
<link rel="stylesheet" href="{{MEDIA_URL}}css/labeltips.css">
<script type="text/javascript">
var bb = null;
require(["dijit/Editor",
require(["dojo/dom",
"dojo/on",
"dojo/request",
"dojo/dom-form",
"dijit/Editor",
"dojox/editor/plugins/Save",
"dojox/editor/plugins/Preview",
"dijit/form/Button",
"dijit/TitlePane",
"dojox/layout/ScrollPane",
"dojo/store/Memory",
"dijit/form/ComboBox",
"dojox/image/LightboxNano",
"dijit/_editor/plugins/TextColor",
"dijit/_editor/plugins/LinkDialog",
......@@ -30,19 +35,26 @@
"dijit/_editor/plugins/NewPage",
"dijit/_editor/plugins/FullScreen",
"dojo/parser",
"dojo/domReady!"], function (Editor, Save, Preview, Button) {
"dojo/domReady!"], function (dom, on, request, domForm, Editor, Save, Preview,
Button, TitlePane, ScrollPane, Memory, ComboBox) {
dojo.parser.parse();
//var newlogdiv = dojo.byId("newlogdiv");
var logtitle = dojo.byId("logmessagetitle");
var logtext = dojo.byId("newlogtext");
//var newlogdiv = dom.byId("newlogdiv");
var logtitle = dom.byId("logmessagetitle");
var logtext = dom.byId("newlogtext");
var editor_div = dom.byId("editor");
var preview_div = dom.byId("previewer");
var editor_div = dojo.byId("editor");
var preview_div = dojo.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.
var newLogTag_pane_holder = dom.byId("newLogTag_pane_holder");
dojo.style(preview_div, { 'display':'none'});
dojo.style(editor_div, { 'display':'none'});
dojo.style(newLogTag_pane_holder, { 'display':'none'});
var button_element = dojo.create('button');
dojo.place(button_element, logtitle, "right");
......@@ -52,12 +64,14 @@
onClick: function(){
if (this.state == 'add') {
dojo.style(editor_div, {'display':'block'});
dojo.style(newLogTag_pane_holder, {'display':'block'});
button.set('label', "Cancel Log Entry");
button.set('state', 'cancel');
editor.focus();
}
else {
dojo.style(editor_div, {'display':'none'});
dojo.style(newLogTag_pane_holder, {'display':'none'});
dojo.style(preview_div, {'display':'none'});
button.set('label', "Add Log Entry");
button.set('state', 'add');
......@@ -66,9 +80,6 @@
},
}, button_element);
//dojo.style(newlogdiv, {'display':'none'});
var savebutton = new Save({
url: "{% url logentry object.graceid "" %}",
onSuccess: function (resp, ioargs) {
......@@ -82,15 +93,19 @@
alert("o hai " + error);
},
save: function(postdata) {
var postArgs = {
var newTagName = domForm.fieldToObject("newTagSelect");
var newTagDispName = domForm.fieldToObject("newTagDispName");
var postArgs = {
url: this.url,
content: { comment: postdata },
content: { comment: postdata, tagname: newTagName, displayName: newTagDispName },
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?
}
});
......@@ -107,11 +122,133 @@
}
});
var editor = new Editor({
extraPlugins : ['foreColor','hiliteColor','|','createLink',
'insertImage','fullscreen','viewsource','newpage', '|', previewbutton, savebutton]
}, editor_div);
// If we have blessed tags *and* some log messages have been tagged,
// we will want some TitlePanes.
{% if blessed_tags and object.getAvailableTags %}
var pane_holder = dom.byId("pane_holder");
var panes = new Array();
var titles = new Array();
{% for tag in object.getAvailableTags %}
{% if tag.name in blessed_tags %}
panes.push(dom.byId("{{tag.name}}"));
titles.push("{{tag|tagUnicode}}");
{% endif %}
{% endfor %}
for (var i = 0; i<panes.length; i++) {
tp = new dijit.TitlePane({title:titles[i],
content:panes[i].innerHTML,
open:true});
pane_holder.appendChild(tp.domNode);
dojo.destroy(panes[i]);
};
// Handle event log seperately. It will be closed by default.
var event_log = dom.byId("event_log");
tp = new dijit.TitlePane({title:"Full Event Log",
content:event_log.innerHTML,
open:false});
pane_holder.appendChild(tp.domNode);
dojo.destroy(event_log);
// Now, deal with the silly figure containers.
var figure_container_ids = new Array();
{% for tag in object.getAvailableTags %}
{% if tag.name in blessed_tags %}
if (dom.byId("{{tag.name}}_figure_container")) {
figure_container_ids.push("{{tag.name}}_figure_container");
}
{% endif %}
{% endfor %}
var figure_scrollpanes = new Array();
for (var i = 0; i<figure_container_ids.length; i++) {
figure_scrollpanes.push(new dojox.layout.ScrollPane({
orientation:"horizontal",
style:"overflow:hidden;"},
figure_container_ids[i]));
}
{% endif %} {# Endif for getAvailableTags and blessed_tags #}
// Set up infrastructure for tagging log messages.
{% if blessed_tags %}
var blessedTagStore = new Memory({
data: [
{% for bt in blessed_tags %}
{name:"{{bt}}"},
{% endfor %}
]
});
{% endif %}
// Panes for the tagging forms.
var newLogTag = dom.byId("newLogTag");
var existingLogTag = dom.byId("existingLogTag");
var existingLogTag_pane_holder = dom.byId("existingLogTag_pane_holder");
tp_new = new dijit.TitlePane({title:"Tag this message",
content:newLogTag.innerHTML,
open:false});
newLogTag_pane_holder.appendChild(tp_new.domNode);
dojo.destroy(newLogTag);
tp_existing = new dijit.TitlePane({title:"Add tag",
content:existingLogTag.innerHTML,
open:false});
existingLogTag_pane_holder.appendChild(tp_existing.domNode);
dojo.destroy(existingLogTag);
// Form for tagging new log messages.
var cb1 = new ComboBox({
id: "newTagSelect",
name: "newTagSelect",
value: "insert tag here",
{% if blessed_tags %}
store: blessedTagStore,
searchAttr: "name"
{% endif %}
}, "newTagSelect");
// Form for tagging existing log messages.
var cb2 = new ComboBox({
id: "existingTagSelect",
name: "existingTagSelect",
value: "None",
{% if blessed_tags %}
store: blessedTagStore,
searchAttr: "name"
{% endif %}
}, "existingTagSelect");
var form = dom.byId('existingTagForm');
on(form, "submit", function(evt){
evt.stopPropagation();
evt.preventDefault();
var n = domForm.fieldToObject("n");
var tagName = domForm.fieldToObject("existingTagSelect");
var tagDispName = domForm.fieldToObject("existingTagDispName");
var tagUrl = "{% url taglogentry object.graceid "000" "temp" %}"
tagUrl = tagUrl.replace("000",n);
tagUrl = tagUrl.replace("temp",tagName);
request.post(tagUrl, {
data: {displayName: tagDispName}
}).then(
function(text){
alert(text);
// You may want to force the page to reload.
//location.reload(true);
},
function(error){alert(error);}
);
});
});
</script>
......@@ -157,9 +294,7 @@
{% block content %}
<p>{{ message }}</p>
<table>
<tr><td valign="top">
<div class="content-area">
<h3> Basic Info </h3>
<table class="event">
......@@ -208,14 +343,17 @@
</tr>
</table>
</div>
<div class="content-area">
{# Analysis-specific attributes #}
{% block analysis_specific %}
{# This block is empty in the base event_detail template #}
{% endblock %}
</div>
{% if nearby %}
<p/>
<div id="neighbors" class="content-area">
<h3>Neighbors</h3>
<table class="event">
<tr>
......@@ -255,6 +393,99 @@
</tr>
{% endfor %}
</table>
</div>
{% endif %}
<div id="pane_holder" class="content-area">
{# XXX Hacky #}
{# With the title here, people will know that all of the things in the #}
{# panes are log messages #}
{% if object.getAvailableTags %}
<h3 id="logmessagetitle">Event Log Messages</h3>
<div id="newLogTag">
<p> Use this form to apply a tag to your new log entry. 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>
<form>
&nbsp; Tag name <input id="newTagSelect" name="newTagSelect" />
&nbsp; Display name <input type="text" id="newTagDispName" name="newTagDispName" />
</form>
</div>
<div id="newLogTag_pane_holder" class="pmTitlePaneClass"></div>
<div id="previewer"></div>
<div id="editor"></div>
{% endif %}
{# Loop over the available tags. #}
{% if object.getAvailableTags %}
{% for tag in object.getAvailableTags %}
{% if tag.name in blessed_tags %}
{# Create a div to hold the pane innerHTML for this tag #}
<div id="{{tag.name}}">
{# A section for the images #}
{% if object|logsForTagHaveImage:tag.name %}
<!-- figure container table is inside a scrolling div -->
<div id="{{tag.name}}_figure_container">
<table class="figure_container">
<tr>
{% for log in object|getLogsForTag:tag.name %}
{% if log.hasImage %}
<td>
<table class="figures">
<!-- A row for the images themselves -->
<tr class="figrow">
<td> <a href="{{ log.fileurl }}" dojoType="dojox.image.LightboxNano"><img height="180" src="{{ log.fileurl }}"></a>
</td>
</tr>
<!-- A row for the captions -->
<tr>
<td> {{log.comment|sanitize}}
{% if log.fileurl %}
<a href="{{log.fileurl}}">{{log.filename}}.</a>
{% endif %}
Submitted by {{log.issuer}} on {{log.created}}
</td>
</tr>
</table>
</td>
{% endif %}
{% endfor %}
</tr>
</table>
</div>
{% endif %}
{# A section for the text-only captions #}
{% if object|logsForTagHaveText:tag.name %}
{# Set up regular log message table #}
<table class="event">
<tr>
<th>No.</th>
<th>{{ "logtime"|timeselect:"utc" }} Log Entry Created</th>
<th>Submitter</th>
<th>Comment</th>
</tr>
{% for log in object|getLogsForTag:tag.name %}
{% if not log.hasImage %}
<tr class="{% cycle 'odd' 'even'%}">
<td>{{log.getN}}</td>
<td>{{log.created|multiTime:"logtime"}}</td>
<td>{{log.issuer}}</td>
<td>{{log.comment|sanitize}}
{% if log.fileurl %}
<a href="{{log.fileurl}}">{{log.filename}}</a>
{% endif %}
</td>
</tr>
{% endif %}
{% endfor %}
</table>
{% endif %}
</div>
{% endif %} {# endif the tag is blessed #}
{% endfor %}
{% endif %}
<noscript>
......@@ -266,15 +497,44 @@
</noscript>
<p/>
<h3 id="logmessagetitle">Event Log Messages</h3>
<div id="event_log">
{# XXX Hacky. We don't want the title here if we're doing panes. #}
{# And we only do panes if there are available tags. #}
{% if not object.getAvailableTags %}
<h3 id="logmessagetitle">Event Log Messages</h3>
<div id="newLogTag">
<p> Use this form to apply a tag to your new log entry. 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>
<form>
&nbsp; Tag name <input id="newTagSelect" name="newTagSelect" />
&nbsp; Display name <input type="text" id="newTagDispName" name="newTagDispName" />
</form>
</div>
<div id="newLogTag_pane_holder" class="pmTitlePaneClass"></div>
<div id="previewer"></div>
<div id="editor"></div>
{% endif %}
{% if object.eventlog_set.count %}
{% if blessed_tags %}
<div id="existingLogTag">
<p> Use this form to tag an existing log message. 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.
<form id="existingTagForm">
Log No. <input id="n" name="n" type="text" size="4" />
&nbsp;&nbsp; Tag name <input id="existingTagSelect" name="existingTagSelect" />
&nbsp;&nbsp; Display name <input type="text" id="existingTagDispName" name="existingTagDispName"/>
<input type="submit" name="submit" value="Submit" /> </p>
</form>
</div>
<div id="existingLogTag_pane_holder" class="pmTitlePaneClass"></div>
{% endif %}
<table class="event">
<tr>
<th>No.</th>
<th>{{ "logtime"|timeselect:"utc" }} Log Entry Created</th>
<th>Submitter</th>
<th>Comment</th>
......@@ -282,6 +542,7 @@
</tr>
{% for log in object.eventlog_set.iterator %}
<tr class="{% cycle 'odd' 'even'%}">
<td>{{log.getN}}
<td>{{log.created|multiTime:"logtime"}}</td>
<td>{{log.issuer}}</td>
<td>{{log.comment|sanitize}}
......@@ -298,32 +559,7 @@
{% endfor %}
</table>
{% endif %}
</div>
</td>
<td rowspan="2" valign="top">
{% if eventdesc %}
<h3>Event Log File</h3>
{{ eventdesc }}
<br/>
{% endif %}
{% if userdesc %}
<br/>
<h3>User Info</h3>
{{ userdesc }}
<br/>
{% endif %}
<p/>
<h3>Available Slots</h3>
{% if not object|slot %}
None.
{% else %}
{% for slot in object|slot %}
<a href="{{slot.fileurl}}">{{ slot.name }}</a> <br/>
{% endfor %}
{% endif %}
</td></tr></table>
</div> <!-- End of pane_holder div -->
{% endblock %}
......@@ -4,10 +4,9 @@
{# Analysis-specific attributes for a cWB event#}
{% block analysis_specific %}
<p/>
<h3>Analysis-Specific Attributes</h3>
<table> <tbody>
<table class="analysis_specific"> <tbody>
<tr>
<td> <table class="event"> <tbody>
<!-- Note: commenting out ifos so that we'll have a multiple of three -->
......
{% extends "gracedb/event_detail.html" %}
{# Analysis-specific attributes for a LowMass event#}
{% block analysis_specific %}
<p/>
<h3>Analysis-specific attributes</h3>
{% load scientific %}
{# Analysis-specific attributes for an LM event#}
{% block analysis_specific %}
<h3>Analysis-Specific Attributes</h3>
<table class="analysis_specific"> <tbody>
<tr>
<td> <table class="event"> <tbody>
<tr> <th> ifos </th> <td> {{object.ifos}} </td> </tr>
<tr> <th> end_time </th> <td> {{object.end_time}} </td> </tr>
<tr> <th> end_time_ns </th> <td> {{object.end_time_ns}} </td> </tr>
</tbody></table>
</td>
<td> <table class="event"> <tbody>
<tr> <th> mass </th> <td> {{object.mass|floatformat:"-4"}} </td> </tr>
<tr> <th> mchirp </th> <td> {{object.mchirp|floatformat:"-4"}} </td> </tr>
<tr> <th> minimum_duration </th> <td> {{object.minimum_duration|scientific}} </td> </tr>
</tbody></table>
</td>
<td> <table class="event"> <tbody>
<tr> <th> snr </th> <td> {{object.snr|floatformat:"-4"}} </td> </tr>
<tr> <th> false_alarm_rate </th> <td> {{object.false_alarm_rate|floatformat:"-4"}} </td> </tr>
<tr> <th> combined_far </th> <td> {{object.combined_far|scientific}} </td> </tr>
</tbody></table>
</td>
</tr>
</tbody> </table>
{% endblock %}
......@@ -4,10 +4,9 @@
{# Analysis-specific attributes for an LM event#}
{% block analysis_specific %}
<p/>
<h3>Analysis-Specific Attributes</h3>
<table> <tbody>
<table class="analysis_specific"> <tbody>
<tr>
<td> <table class="event"> <tbody>
<tr> <th> ifos </th> <td> {{object.ifos}} </td> </tr>
......@@ -30,5 +29,4 @@
</tr>
</tbody> </table>
{% endblock %}
......@@ -3,8 +3,7 @@
{# Analysis-specific attributes for a LowMass event#}
{% block analysis_specific %}
<p/>
<h3>Analysis-specific attributes</h3>
<h3>Event Log File</h3>
{{eventdesc}}
{% endblock %}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment