Skip to content
Snippets Groups Projects
Commit 9e7acff7 authored by Branson Craig Stephens's avatar Branson Craig Stephens
Browse files

Added new VOEvent model object, and exposed the VOEvents through

the REST API in a manner analogous to that of the EventLog objects.
Also updated the VOEvent such that IVORNs include an incrementing
serial number and the Citations section contains appropriate
references to earlier VOEvents.
parent cc440534
No related branches found
No related tags found
No related merge requests found
......@@ -14,12 +14,13 @@ from django.contrib.auth.models import Group as AuthGroup
from django.contrib.contenttypes.models import ContentType
from gracedb.models import Event, Group, Search, Pipeline, EventLog, Tag
from gracedb.models import EMGroup, EMBBEventLog, EMSPECTRUM
from gracedb.models import VOEvent
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 embbEventLogToDict, voeventToDict
from view_utils import reverse
from translator import handle_uploaded_data
......@@ -343,32 +344,6 @@ class TSVRenderer(BaseRenderer):
return outTable
# XXX this doesn't work because you don't have the request here. You could
# try stuffing it into the renderer context, but that's really ugly.
#class VOEventRenderer(BaseRenderer):
# media_type = 'application/xml'
# format = 'xml'
#
# def render(self, data, media_type=None, renderer_context=None):
# if 'error' in data.keys():
# return data['error']
#
# outDoc = ''
# for e in data['events']:
# graceid = e['graceid']
#
# try:
# # XXX If any part of this fails, the VOEvent will be empty.
# event = Event.getByGraceid(graceid)
# if not event.far or not event.gpstime:
# raise Exception
# voevent = buildVOEvent(event, request)
# except:
# voevent = ''
# outDoc += voevent + '\n'
#
# return outDoc
#==================================================================
# Events
......@@ -650,33 +625,6 @@ class EventDetail(APIView):
status=status.HTTP_400_BAD_REQUEST)
return Response(status=status.HTTP_202_ACCEPTED)
# FIXME or something.
# This should really be a renderer and not a view. But the problem
# is that the renderer needs the request in order to build up URLs.
# There must be a better way of doing this.
class EventVODetail(APIView):
authentication_classes = (LigoAuthentication,)
#parser_classes = (LigoLwParser, RawdataParser)
parser_classes = (parsers.MultiPartParser,)
#serializer_class = EventSerializer
permission_classes = (IsAuthenticated,IsAuthorizedForEvent,)
renderer_classes = (JSONRenderer, BrowsableAPIRenderer,)
@event_and_auth_required
def get(self, request, event):
voevent_type = request.QUERY_PARAMS.get('voevent_type', 'preliminary')
try:
voevent = buildVOEvent(event, request, voevent_type=voevent_type)
except VOEventBuilderException, e:
return Response(str(e), status=status.HTTP_400_BAD_REQUEST)
except Exception, e:
return Response("Problem building VOEvent: %s" % str(e),
status=status.HTTP_500_INTERNAL_SERVER_ERROR)
response = Response(voevent)
response["Cache-Control"] = "no-cache"
return response
#==================================================================
# Neighbors
......@@ -1336,10 +1284,10 @@ class GracedbRoot(APIView):
# Is there better?
detail = reverse("event-detail", args=["G1200"], request=request)
detail = detail.replace("G1200", "{graceid}")
vo_detail = reverse("event-vo-detail", args=["G1200"], request=request)
vo_detail = vo_detail.replace("G1200", "{graceid}")
log = reverse("eventlog-list", args=["G1200"], request=request)
log = log.replace("G1200", "{graceid}")
voevent = reverse("voevent-list", args=["G1200"], request=request)
voevent = voevent.replace("G1200", "{graceid}")
embb = reverse("embbeventlog-list", args=["G1200"], request=request)
embb = embb.replace("G1200", "{graceid}")
......@@ -1368,7 +1316,7 @@ class GracedbRoot(APIView):
templates = {
"event-detail-template" : detail,
"event-vo-detail-template" : vo_detail,
"voevent-list-template" : voevent,
"event-log-template" : log,
"embb-event-log-template" : embb,
"event-label-template" : labels,
......@@ -1408,6 +1356,7 @@ class GracedbRoot(APIView):
"wavebands" : dict(EMSPECTRUM),
"eel-statuses" : dict(EMBBEventLog.EEL_STATUS_CHOICES),
"obs-statuses" : dict(EMBBEventLog.OBS_STATUS_CHOICES),
"voevent-types" : dict(VOEvent.VOEVENT_TYPE_CHOICES),
})
##################################################################
......@@ -1612,3 +1561,122 @@ class PerformanceInfo(APIView):
return Response(performance_info,status=status.HTTP_200_OK)
#==================================================================
# VOEvent Resources
class VOEventList(APIView):
"""VOEvent List Resource
"""
authentication_classes = (LigoAuthentication,)
permission_classes = (IsAuthenticated,IsAuthorizedForEvent,)
@event_and_auth_required
def get(self, request, event):
voeventset = event.voevent_set.order_by("created","N")
count = voeventset.count()
voevents = [ voeventToDict(voevent, request)
for voevent in voeventset.iterator() ]
rv = {
'start': 0,
'numRows' : count,
'links' : {
'self' : request.build_absolute_uri(),
'first' : request.build_absolute_uri(),
'last' : request.build_absolute_uri(),
},
'voevents' : voevents,
}
return Response(rv)
@event_and_auth_required
def post(self, request, event):
voevent_type = request.DATA.get('voevent_type', None)
if not voevent_type:
msg = "You must provide a valid voevent_type."
return Response({'error': msg}, status = status.HTTP_400_BAD_REQUEST)
skymap_type = request.DATA.get('skymap_type', None)
skymap_filename = request.DATA.get('skymap_filename', None)
skymap_image_filename = request.DATA.get('skymap_image_filename', None)
if (skymap_filename and not skymap_type) or (skymap_type and not skymap_filename):
msg = "Both or neither of skymap_time and skymap_filename must be specified."
return Response({'error': msg}, status = status.HTTP_400_BAD_REQUEST)
# Instantiate the voevent and save in order to get the serial number
voevent = VOEvent(voevent_type=voevent_type, event=event, issuer=request.user)
try:
voevent.save()
except Exception as e:
return Response("Failed to create VOEvent: %s" % str(e),
status=status.HTTP_503_SERVICE_UNAVAILABLE)
# Now, you need to actually build the VOEvent.
try:
voevent_text, ivorn = buildVOEvent(event, voevent.N, voevent_type, request,
skymap_filename = skymap_filename, skymap_type = skymap_type,
skymap_image_filename = skymap_image_filename)
except VOEventBuilderException, e:
msg = "Problem building VOEvent: %s" % str(e)
return Response({'error': msg}, status = status.HTTP_400_BAD_REQUEST)
voevent_display_type = dict(VOEvent.VOEVENT_TYPE_CHOICES)[voevent_type].capitalize()
filename = "%s-%d-%s.xml" % (event.graceid(), voevent.N, voevent_display_type)
filepath = os.path.join(event.datadir(), filename)
fdest = VersionedFile(filepath, 'w')
fdest.write(voevent_text)
fdest.close()
file_version = fdest.version
voevent.filename = filename
voevent.file_version = file_version
voevent.ivorn = ivorn
voevent.save()
rv = voeventToDict(voevent, request=request)
# Create LogEntry to document the new VOEvent.
logentry = EventLog(event=event,
issuer=request.user,
comment='',
filename=filename,
file_version=file_version)
try:
logentry.save()
except:
rv['warnings'] = 'Problem saving log entry for VOEvent %s of %s' % (voevent.N,
event.graceid())
# Tag log entry as 'sky_loc'
tmp = EventLogTagDetail()
retval = tmp.put(request, event.graceid(), logentry.N, 'em_follow')
# XXX This seems like a bizarre way of getting an error message out.
if retval.status_code != 201:
rv['tagWarning'] = 'Error tagging VOEvent log message as em_follow.'
# Issue alert.
description = "VOEVENT: %s" % filename
issueAlertForUpdate(event, description, doxmpp=True,
filename=filename, serialized_object=rv)
response = Response(rv, status=status.HTTP_201_CREATED)
response['Location'] = rv['self']
return response
class VOEventDetail(APIView):
authentication_classes = (LigoAuthentication,)
permission_classes = (IsAuthenticated,IsAuthorizedForEvent,)
@event_and_auth_required
def get(self, request, event, n):
try:
voevent = event.voevent_set.filter(N=n)[0]
except:
return Response("VOEvent does not exist.",
status=status.HTTP_404_NOT_FOUND)
return Response(voeventToDict(voevent, request=request))
This diff is collapsed.
This diff is collapsed.
......@@ -822,3 +822,54 @@ class Tag(models.Model):
# # messages in the tag.
# eventlist = [log.event for log in self.eventlogs.all()]
class VOEvent(models.Model):
class Meta:
ordering = ['-created','-N']
unique_together = ("event","N")
# Now N will be the serial number.
event = models.ForeignKey(Event, null=False)
created = models.DateTimeField(auto_now_add=True)
issuer = models.ForeignKey(DjangoUser)
ivorn = models.CharField(max_length=200, default="")
filename = models.CharField(max_length=100, default="")
file_version = models.IntegerField(null=True)
N = models.IntegerField(null=False)
VOEVENT_TYPE_CHOICES = (('PR','preliminary'), ('IN','initial'), ('UP','update'), ('RE', 'retraction'),)
voevent_type = models.CharField(max_length=2, choices=VOEVENT_TYPE_CHOICES)
def fileurl(self):
if self.filename:
actual_filename = self.filename
if self.file_version >= 0:
actual_filename += ',%d' % self.file_version
return reverse('file', args=[self.event.graceid(), actual_filename])
else:
return None
def save(self, *args, **kwargs):
success = False
# XXX filename must not be 'None' because null=False for the filename
# field above.
self.filename = self.filename or ""
attempts = 0
while (not success and attempts < 5):
attempts = attempts + 1
if not self.N:
if self.event.voevent_set.count():
self.N = int(self.event.voevent_set.aggregate(models.Max('N'))['N__max']) + 1
else:
self.N = 1
try:
super(VOEvent, self).save(*args, **kwargs)
success = True
except IntegrityError:
# IntegrityError means an attempt to insert a duplicate
# key or to violate a foreignkey constraint.
# We are under race conditions. Let's try again.
pass
if not success:
# XXX Should this be a custom exception? That way we could catch it
# in the views that use it and give an informative error message.
raise Exception("Too many attempts to save log message. Something is wrong.")
......@@ -5,7 +5,7 @@ from django.conf.urls import patterns, url
# rest_framework
from gracedb.api import GracedbRoot
from gracedb.api import EventList, EventDetail, EventVODetail
from gracedb.api import EventList, EventDetail
from gracedb.api import EventLogList, EventLogDetail
from gracedb.api import EMBBEventLogList, EMBBEventLogDetail
from gracedb.api import TagList
......@@ -18,6 +18,7 @@ from gracedb.api import PerformanceInfo
from gracedb.api import EventPermissionList
from gracedb.api import GroupEventPermissionList
from gracedb.api import GroupEventPermissionDetail
from gracedb.api import VOEventList, VOEventDetail
urlpatterns = patterns('gracedb.api',
......@@ -28,8 +29,6 @@ urlpatterns = patterns('gracedb.api',
# events/[{graceid}[/{version}]]
url (r'events/$',
EventList.as_view(), name='event-list'),
url (r'events/voevent/(?P<graceid>[GEHMT]\d+)$',
EventVODetail.as_view(), name='event-vo-detail'),
url (r'events/(?P<graceid>[GEHMT]\d+)$',
EventDetail.as_view(), name='event-detail'),
......@@ -40,6 +39,13 @@ urlpatterns = patterns('gracedb.api',
url (r'events/(?P<graceid>[GEHMT]\d+)/log/(?P<n>\d+)$',
EventLogDetail.as_view(), name='eventlog-detail'),
# VOEvent Resources
# events/{graceid}/voevent/[{serial_number}]
url (r'events/(?P<graceid>[GEHMT]\d+)/voevent/$',
VOEventList.as_view(), name='voevent-list'),
url (r'events/(?P<graceid>[GEHMT]\d+)/voevent/(?P<n>\d+)$',
VOEventDetail.as_view(), name='voevent-detail'),
# EMBB Event Log Resources
# events/{graceid}/logs/[{logid}]
url (r'events/(?P<graceid>[GEHMT]\d+)/embb/$',
......
......@@ -284,6 +284,45 @@ def embbEventLogToDict(eel, request=None):
"extra_info_dict" : eel.extra_info_dict,
}
# VOEvent serializer
def voeventToDict(voevent, request=None):
filename = urlquote('%s,%d' % (voevent.filename, voevent.file_version))
uri = None
file_uri = None
if request:
uri = reverse("voevent-detail",
args=[voevent.event.graceid(), voevent.N],
request=request)
file_uri = reverse("files",
args=[voevent.event.graceid(), filename],
request=request)
issuer_info = {
"username": voevent.issuer.username,
"display_name": "%s %s" % (voevent.issuer.first_name, voevent.issuer.last_name),
}
# Read in the filecontents
filepath = os.path.join(voevent.event.datadir(), voevent.filename)
text = None
try:
text = open(filepath, 'r').read()
except:
pass
return {
"self" : uri,
"text" : text,
"file" : file_uri,
"N" : voevent.N,
"issuer" : issuer_info,
"ivorn" : voevent.ivorn,
"filename" : voevent.filename,
"file_version" : voevent.file_version,
"voevent_type" : voevent.voevent_type,
"created" : voevent.created.isoformat(),
}
#---------------------------------------------------------------------------------------
......
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