Skip to content
Snippets Groups Projects
Commit c9d56424 authored by Brian Moe's avatar Brian Moe
Browse files

Misc

parent d225e74b
No related branches found
No related tags found
No related merge requests found
......@@ -8,7 +8,8 @@ from django.conf import settings
import json
from gracedb.models import Event, Group
from gracedb.models import Event, Group, EventLog, Label
from translator import handle_uploaded_data
import os
import urllib
......@@ -24,6 +25,8 @@ PAGINATE_BY = REST_FRAMEWORK_SETTINGS.get('PAGINATE_BY', 10)
# rest_framework
from rest_framework import serializers, status
from rest_framework.response import Response
from rest_framework.parsers import BaseParser
#from rest_framework import generics
#from rest_framework.renderers import JSONRenderer, JSONPRenderer
#from rest_framework.renderers import YAMLRenderer, XMLRenderer
from forms import CreateEventForm
......@@ -51,15 +54,28 @@ class LigoAuthentication(authentication.BaseAuthentication):
return (user, None)
class EventSerializer(serializers.Serializer):
group = serializers.CharField(required=True, max_length=100)
analysisType = serializers.CharField(required=True, max_length=100)
class EventSerializer(serializers.ModelSerializer):
#group = serializers.CharField(required=True, max_length=100)
#analysisType = serializers.CharField(required=True, max_length=100)
group = serializers.CharField(source="group.name")
class Meta:
model = Event
fields = ('far', 'instruments', 'group')
class EventLogSerializer(serializers.ModelSerializer):
"""docstring for EventLogSerializer"""
comment = serializers.CharField(required=True, max_length=200)
class Meta:
model = EventLog
fields = ('comment', 'issuer', 'created')
#==================================================================
# Events
def eventToDict(event, columns=None, request=None):
"""Convert an Event to a dictionary so it can be serialized. (ugh)"""
# XXX Seems wrong. Need to understand serializers.
# XXX Need to understand serializers.
rv = {}
......@@ -69,20 +85,27 @@ def eventToDict(event, columns=None, request=None):
rv['group'] = event.group.name
rv['graceid'] = graceid
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'] = [labelling.label.name
for labelling in event.labelling_set.all()]
rv['labels'] = dict([
(labelling.label.name,
reverse("labels",
args=[graceid, labelling.label.name],
request=request))
for labelling in event.labelling_set.all()])
rv['links'] = {
"neighbors" : dict(
[(e.gpstime, reverse("event-detail", args=[e.graceid()]))
for e in event.neighbors()]),
# "neighbors" : dict(
# [(e.gpstime, reverse("event-detail", args=[e.graceid()], request=request))
# for e in event.neighbors()]),
"neighbors" : reverse("neighbors", args=[graceid], request=request),
"data" : event.weburl(),
"log" : reverse("eventlog-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),
}
return rv
......@@ -165,21 +188,28 @@ class EventList(APIView):
start = int(start)
count = int(count)
numRows = events.count()
last = max(0, (count / numRows) - 1)
last = max(0, (numRows / count)) * count
rv = {}
links = {}
rv['links'] = links
rv['events'] = [eventToDict(e, request=request)
for e in events[start:start+count]]
baseuri = reverse('event-list', request=request)
links['self'] = request.build_absolute_uri()
d = { 'start' : 0, "count": count, "sort": sort }
if query: d['query'] = query
rv['first'] = baseuri + "?" + urllib.urlencode(d)
links['first'] = baseuri + "?" + urllib.urlencode(d)
d['start'] = last
rv['last'] = baseuri + "?" + urllib.urlencode(d)
rv['self'] = request.build_absolute_uri()
links['last'] = baseuri + "?" + urllib.urlencode(d)
if start != last:
d['start'] = start+1
rv['next'] = baseuri + "?" + urllib.urlencode(d)
d['start'] = start+count
links['next'] = baseuri + "?" + urllib.urlencode(d)
rv['numRows'] = events.count()
d['links'] = links
return Response(rv)
def post(self, request, format=None):
......@@ -204,9 +234,42 @@ class EventList(APIView):
for key in form.errors]
return Response(rv, status=status.HTTP_400_BAD_REQUEST)
class LigoLwParser(parsers.MultiPartParser):
# XXX Revisit this.
# Doing it right involves refactoring translator.py
media_type = "multipart/form-data"
def parse(self, *args, **kwargs):
data = parsers.MultiPartParser.parse(self, *args, **kwargs)
return data
# eventData = data.files['eventFile'].read()
# dirPrefix = settings.GRACEDB_DATA_DIR
# eventDir = os.path.join(dirPrefix, event.graceid())
# # XXX handle duplicate file names.
# f = request.FILES['eventFile']
# uploadDestination = os.path.join(eventDir, "private", f.name)
# fdest = open(uploadDestination, 'w')
# # Save uploaded file into user private area.
# for chunk in f.chunks():
# fdest.write(chunk)
# fdest.close()
# # Extract Info from uploaded data
# try:
# handle_uploaded_data(event, uploadDestination)
# except:
# return Response("Bad Data",
# status=status.HTTP_400_BAD_REQUEST)
# return Response(status=status.HTTP_202_ACCEPTED)
class EventDetail(APIView):
authentication_classes = (LigoAuthentication,)
parser_classes = (parsers.MultiPartParser,)
parser_classes = (LigoLwParser,)
#parser_classes = (parsers.MultiPartParser,)
serializer_class = EventSerializer
form = CreateEventForm
def get(self, request, graceid):
......@@ -214,21 +277,151 @@ class EventDetail(APIView):
event = Event.getByGraceid(graceid)
except Event.DoesNotExist:
# XXX Real error message.
return Response("blah blah blah", status=status.HTTP_404_NOT_FOUND)
return Response(eventToDict(event, request=request))
return Response("Event Not Found",
status=status.HTTP_404_NOT_FOUND)
#response = Response(self.serializer_class(event).data)
response = Response(eventToDict(event, request=request))
response["Cache-Control"] = "no-cache"
return response
def put(self, request, graceid):
""" I am a doc. Do I not get put anywhere? """
raise NotImplementedError()
try:
event = Event.getByGraceid(graceid)
except Event.DoesNotExist:
return Response("Event Not Found",
status=status.HTTP_404_NOT_FOUND)
try:
if request.ligouser != event.submitter:
msg = "You (%s) Them (%s)" % (request.ligouser, event.submitter)
return HttpResponseForbidden("You did not create this event. %s" %msg)
except Exception, e:
return Response(str(e))
# messages = []
# if event.group.name != request.DATA['group']:
# messages += [
# "Existing event group ({0}) does not match "
# "replacement event group ({1})".format(
# event.group.name, request.DATA['group'])]
# if event.analysisType != request.DATA['type']:
# messages += [
# "Existing event type ({0}) does not match "
# "replacement event type ({1})".format(
# event.analysisType, request.DATA['type'])]
# if messages:
# return Response("\n".join(messages),
# status=status.HTTP_400_BAD_REQUEST)
dirPrefix = settings.GRACEDB_DATA_DIR
eventDir = os.path.join(dirPrefix, event.graceid())
# XXX handle duplicate file names.
f = request.FILES['eventFile']
uploadDestination = os.path.join(eventDir, "private", f.name)
fdest = open(uploadDestination, 'w')
# Save uploaded file into user private area.
for chunk in f.chunks():
fdest.write(chunk)
fdest.close()
# Extract Info from uploaded data
try:
handle_uploaded_data(event, uploadDestination)
event.submitter = request.ligouser
except:
return Response("Bad Data",
status=status.HTTP_400_BAD_REQUEST)
return Response(status=status.HTTP_202_ACCEPTED)
#==================================================================
# Neighbors
class EventNeighbors(APIView):
def get(self, request, graceid):
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 request.QUERY_PARAMS.has_key('delta'):
delta = request.QUERY_PARAMS['delta']
if delta.find(',') < 0:
delta = delta2 = int(delta)
else:
delta , delta2 = map(int, delta.split(','))
neighbors = event.neighbors(delta=delta, delta2=delta2)
else:
neighbors = event.neighbors()
return Response(
[eventToDict(neighbor, request=request)
for neighbor in neighbors])
#==================================================================
# Labels
# XXX NOT FINISHED
def labelToDict(label, request=None):
return {
"name" : label.label.name,
"creator" : label.creator.name,
"created" : label.created,
"self" : reverse("labels",
args=[label.event.graceid(), label.label.name],
request=request),
}
class EventLabel(APIView):
"""Event Label"""
authentication_classes = (LigoAuthentication,)
def get(self, request, graceid, label):
event = Event.getByGraceid(graceid)
if label is not None:
theLabel = event.labelling_set.filter(label__name=label).all()
if len(theLabel) < 1:
return Response("Label Not %s Found" % label,
status=status.HTTP_404_NOT_FOUND)
theLabel = theLabel[0]
return Response(labelToDict(theLabel, request=request))
else:
labels = [map(
lambda x: labelToDict(x,request=request),
event.labelling_set.all())]
return Response({
'links' : [{
'self': request.build_absolute_uri(),
'event': reverse("event-detail",
args=[event.graceid()],
request=request),
}],
'labels': labels
})
def put(self, request, graceid, label):
return Response("Not Implemented", status=status.HTTP_501_NOT_IMPLEMENTED)
def delete(self, request, graceid, label):
return Response("Not Implemented", status=status.HTTP_501_NOT_IMPLEMENTED)
#==================================================================
# EventLog
# Janky serialization
def eventLogToDict(log, n=None, request=None):
# XXX Messy. n should not be here but in the model.
if n is None and request:
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)
uri = reverse("eventlog-detail",
args=[log.event.graceid(), n],
request=request)
else:
uri = ""
uri = None
return {
"comment" : log.comment,
"created" : log.created,
......@@ -237,6 +430,10 @@ def eventLogToDict(log, n=None, request=None):
}
class EventLogList(APIView):
"""Event Log List Resource
POST param 'message'
"""
authentication_classes = (LigoAuthentication,)
def get(self, request, graceid):
......@@ -244,16 +441,30 @@ class EventLogList(APIView):
event = Event.getByGraceid(graceid)
except Event.DoesNotExist:
# XXX Real error message.
return Response("blah blah blah", status=status.HTTP_404_NOT_FOUND)
logset = event.eventlog_set
return Response("Event does not exist.",
status=status.HTTP_404_NOT_FOUND)
logset = event.eventlog_set.order_by("created")
count = logset.count()
rv = [ eventLogToDict(log, n, request)
for (n, log) in zip(range(0,count+2), logset.iterator()) ]
return Response(rv)
class EventLogDetail(APIView):
"""docstring for EventLogDetail"""
def post(self, request, graceid):
event = Event.getByGraceid(graceid)
message = request.DATA.get('message')
logentry = EventLog(
event=event,
issuer=request.ligouser,
comment=message)
logset = event.eventlog_set.order_by("created")
n = len(logset)
logentry.save()
rv = eventLogToDict(logentry, n, request=request)
response = Response(rv, status=status.HTTP_201_CREATED)
response['Location'] = rv['self']
return response
class EventLogDetail(APIView):
authentication_classes = (LigoAuthentication,)
def get(self, request, graceid, n):
......@@ -261,10 +472,14 @@ class EventLogDetail(APIView):
event = Event.getByGraceid(graceid)
except Event.DoesNotExist:
# XXX Real error message.
return Response("blah blah blah", status=status.HTTP_404_NOT_FOUND)
rv = event.eventlog_set.all()[int(n)]
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))
#==================================================================
# Root Resource
class GracedbRoot(APIView):
"""
Root of the Gracedb REST API
......@@ -286,14 +501,24 @@ class GracedbRoot(APIView):
filemeta = filemeta.replace("G1200", "{graceid}")
filemeta = filemeta.replace("filename", "{filename}")
labels = reverse('labels', args=["G1200", "thelabel"], request=request)
labels = labels.replace("G1200", "{graceid}")
labels = labels.replace("thelabel", "{label}")
return Response({
"event-list" : reverse("event-list", request=request),
"event-detail-template" : detail,
"resources" : {
"events" : reverse("event-list", request=request),
},
"resource-templates" : {
"event-template" : detail,
"event-log-template" : log,
"files-template" : files,
"filemeta-template" : filemeta,
"event-files-template" : files,
"event-filemeta-template" : filemeta,
"event-label-template" : labels,
},
"groups" : [group.name for group in Group.objects.all()],
"analysis-types" : dict(Event.ANALYSIS_TYPE_CHOICES),
"labels" : [label.name for label in Label.objects.all()],
})
##################################################################
......@@ -363,8 +588,8 @@ class Files(APIView):
authentication_classes = (LigoAuthentication,)
parser_classes = (parsers.MultiPartParser,)
def get(self, request, graceid, filename=""):
# Do not filename to be None. That messes up later os.path.join
def get(self, request, graceid, filename=None):
# Do not let filename be None. That messes up later os.path.join
filename = filename or ""
try:
......@@ -372,7 +597,7 @@ class Files(APIView):
except Event.DoesNotExist:
return HttpResponseNotFound("Event not found")
# The plan to deal with that wretched general/ directory maybe
# The plan to deal with that general/ directory maybe
# should be to move it INTO private. Then externally, things
# would look like they do now, but the code here would be MUCH
# more sane and much shorter.
......@@ -397,12 +622,19 @@ class Files(APIView):
# Get list of files w/urls.
rv = {}
filepath = event.datadir()
files = []
for dirname, dirnames, filenames in os.walk(filepath):
dirname = dirname[len(filepath):] # cut off base event dir path
for filename in filenames:
# relative path from root of event data dir
filename = os.path.join(dirname, filename)
rv[filename] = reverse("files", args=[graceid, filename], request=request)
files.append({
'name' : filename,
'link' : reverse("files",
args=[graceid, filename],
request=request),
})
# XXX UGH... that awful general/ dir
# Actually not terrible, but do not like private/general as siblings.
......@@ -415,10 +647,26 @@ class Files(APIView):
# relative path from root of event data dir
filename = os.path.join(dirname, filename)
rv[filename] = reverse("files", args=[graceid, filename], request=request)
files.append({
'name' : filename,
'link' : reverse("files",
args=[graceid, filename],
request=request),
})
#response = HttpResponse(simplejson.dumps(rv), content_type="application/json")
# response = Response({
# 'links' : {
# 'self' : request.build_absolute_uri(),
# 'event' : reverse("event-detail",
# args=[graceid],
# request=request),
# },
# 'files' : files,
# })
response = Response(rv)
elif os.path.isdir(filepath):
# XXX Really?
response = HttpResponseForbidden("%s is a directory" % filename)
else:
response = HttpResponseServerError("Should not happen.")
......
......@@ -123,14 +123,16 @@ class Event(models.Model):
gps_time = int(posixToGpsTime(posix_time))
return gps_time - self.gpstime
def neighbors(self, delta=5):
def neighbors(self, delta=5, delta2=None):
if not self.gpstime:
return []
if self.group.name == 'Test':
nearby = Event.objects.filter(group__name='Test')
else:
nearby = Event.objects.exclude(group__name='Test')
nearby = nearby.filter(gpstime__range=(self.gpstime-delta, self.gpstime+delta))
if delta2 is None:
delta2 = delta
nearby = nearby.filter(gpstime__range=(self.gpstime-delta, self.gpstime+delta2))
nearby = nearby.exclude(id=self.id)
nearby = nearby.order_by('gpstime')
return nearby
......
......@@ -6,36 +6,47 @@ from gracedb.api import GracedbRoot
from gracedb.api import EventList, EventDetail
from gracedb.api import EventLogList, EventLogDetail
from gracedb.api import Files, FileMeta
from gracedb.api import EventNeighbors, EventLabel
urlpatterns = patterns('gracedb.api',
url (r'^$', GracedbRoot.as_view(), name="api-root"),
# Event Resources
# events/[{graceid}[/{version}]]
url (r'events/$', EventList.as_view(), name='event-list'),
url (r'events/(?P<graceid>[GEHT]\d+)$', EventDetail.as_view(), name='event-detail'),
url (r'events/$',
EventList.as_view(), name='event-list'),
url (r'events/(?P<graceid>[GEHT]\d+)$',
EventDetail.as_view(), name='event-detail'),
# Event Log Resources
# events/{graceid}/logs/[{logid}]
url (r'events/(?P<graceid>[GEHT]\d+)/log/$', EventLogList.as_view(), name='eventlog-list'),
url (r'events/(?P<graceid>[GEHT]\d+)/log/(?P<n>\d+)$', EventLogDetail.as_view(), name='eventlog-detail'),
url (r'events/(?P<graceid>[GEHT]\d+)/log/$',
EventLogList.as_view(), name='eventlog-list'),
url (r'events/(?P<graceid>[GEHT]\d+)/log/(?P<n>\d+)$',
EventLogDetail.as_view(), name='eventlog-detail'),
# Event File Resources
# events/{graceid}/files/[{filename}[/{version}]]
# XXX change to DjangoRF. But this works now with the cli.
#url (r'^events/(?P<graceid>[\w\d]+)/files/(?P<filename>.+)?$', 'download', name="files"),
url (r'^events/(?P<graceid>\w[\d]+)/files/(?P<filename>.+)?$', Files.as_view(), name="files"),
url (r'^events/(?P<graceid>\w[\d]+)/files/(?P<filename>.+)?$',
Files.as_view(), name="files"),
# events/{graceid}/filemeta/[{filename}]
url (r'^events/(?P<graceid>\w[\d]+)/filemeta/(?P<filename>.+)?$', FileMeta.as_view(), name="filemeta"),
url (r'^events/(?P<graceid>\w[\d]+)/filemeta/(?P<filename>.+)?$',
FileMeta.as_view(), name="filemeta"),
# Event Labels
# events/{graceid}/labels/[{label}]
url (r'^events/(?P<graceid>\w[\d]+)/labels/(?P<label>.+)?$',
EventLabel.as_view(), name="labels"),
# Event Slots
# events/{graceid}/slots/[{slotid}]
# Event Neighbors
# events/{graceid}/neighbors/[?neighborhood=N]
# events/{graceid}/neighbors/[?delta=(N|(N,N))]
url (r'^events/(?P<graceid>\w[\d]+)/neighbors/$',
EventNeighbors.as_view(), name="neighbors"),
# Legacy
#url (r'^events/(?P<graceid>\w[\d]+)/files/(?P<filename>.+)?$', 'download', name="files"),
url (r'^event/(?P<graceid>\w[\d]+)/files/(?P<filename>.+)?$', 'download', name="download2"),
)
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