Commit 6652534a authored by Tanner Prestegard's avatar Tanner Prestegard Committed by GraceDB

api: new endpoint for updating GRB events

Add a new API endpoint for updating certain GRB event parameters:
ra, dec, error_radius, t90, redshift, designation. Only users with
specific permissions can use it.
parent a737109e
import logging
from rest_framework import permissions
# Set up logger
logger = logging.getLogger(__name__)
class CanUpdateGrbEvent(permissions.BasePermission):
def has_permission(self, request, view):
return request.user.has_perm('events.t90_grbevent')
......@@ -9,6 +9,8 @@ urlpatterns = [
url(r'^$', EventList.as_view(), name='event-list'),
url(r'^(?P<graceid>[GEHMT]\d+)$', EventDetail.as_view(),
GrbEventPatchView.as_view(), name='update-grbevent'),
# Event Log Resources
# events/{graceid}/logs/[{logid}]
......@@ -26,7 +26,9 @@ from glue.ligolw.ligolw import LIGOLWContentHandler
from glue.ligolw.lsctables import use_in
from guardian.models import GroupObjectPermission
from rest_framework import authentication, parsers, serializers, status
from rest_framework.permissions import IsAuthenticated, BasePermission, SAFE_METHODS
from rest_framework.exceptions import ValidationError as DrfValidationError
from rest_framework.permissions import IsAuthenticated, BasePermission, \
from rest_framework.renderers import BaseRenderer, JSONRenderer, \
from rest_framework.response import Response
......@@ -40,7 +42,7 @@ from core.vfile import VersionedFile
from events.buildVOEvent import buildVOEvent, VOEventBuilderException
from events.forms import CreateEventForm
from events.models import Event, Group, Search, Pipeline, EventLog, Tag, \
Label, Labelling, EMGroup, EMBBEventLog, EMSPECTRUM, VOEvent
Label, Labelling, EMGroup, EMBBEventLog, EMSPECTRUM, VOEvent, GrbEvent
from events.permission_utils import user_has_perm, filter_events_for_user, \
is_external, check_external_file_access
from events.translator import handle_uploaded_data
......@@ -53,6 +55,7 @@ from events.view_utils import eventToDict, eventLogToDict, labelToDict, \
from search.forms import SimpleSearchForm
from import parseQuery, ParseException
from superevents.models import Superevent
from .permissions import CanUpdateGrbEvent
from .throttling import EventCreationThrottle, AnnotationThrottle
from ..mixins import InheritDefaultPermissionsMixin
from ...utils import api_reverse
......@@ -82,7 +85,7 @@ class IsAuthorizedForEvent(BasePermission):
# "Unsafe methods" require change permissions on the event.
# Note that DELETE is only implemented for event-log-tag
# relationships.
elif request.method in ['PUT','POST','DELETE']:
elif request.method in ['PUT', 'PATCH', 'POST', 'DELETE']:
shortname = 'change'
return False
......@@ -637,6 +640,93 @@ class EventDetail(InheritPermissionsAPIView):
return Response(status=status.HTTP_202_ACCEPTED)
# New class *only* for updating GRB event properties
class GrbEventPatchView(InheritPermissionsAPIView):
permission_classes = (IsAuthenticated, IsAuthorizedForEvent,
updatable_attributes = ['t90', 'redshift', 'designation', 'ra', 'dec',
def process_data(self, data):
cleaned_data = {}
for k, v in data.items():
if k in ['t90', 'redshift', 'ra', 'dec', 'error_radius']:
cleaned_data[k] = float(v)
except ValueError as e:
err_msg = "Parameter '{k}' must be a float".format(k=k)
raise DrfValidationError(err_msg)
elif k == 'designation':
cleaned_data[k] = str(v)
except ValueError as e:
err_msg = "Parameter '{k}' must be a string".format(k=k)
raise DrfValidationError(err_msg)
return cleaned_data
def get_attributes_to_update(self, grbevent, data):
attrib_to_update = [k for k in data if (k in self.updatable_attributes
and getattr(grbevent, k, None) != data[k])]
# If none, raise an error
if not attrib_to_update:
raise DrfValidationError('Request would not modify the GRB event')
return {k: data[k] for k in attrib_to_update}
def generate_log_message(self, grbevent, update_dict):
# Templates
comment = "Updated GRB event parameters: {msg}"
param_template = "{name}: {old} -> {new}"
# Message strings for updated parameters
update_list = [
old=getattr(grbevent, k),
for k in update_dict
return comment.format(msg=", ".join(update_list))
def patch(self, request, grbevent):
# grbevent here should be a GrbEvent due to the way
# event_and_auth_required works
# Make sure this is a GRB event
if ( not in settings.GRB_PIPELINES
or not isinstance(grbevent, GrbEvent)):
msg = ("Cannot update GRB event parameters for non-GRB event "
return Response(msg, status=status.HTTP_400_BAD_REQUEST)
# Process data - should be all floats except designation
data = self.process_data(
# Get attributes to update and their values
update_dict = self.get_attributes_to_update(grbevent, data)
# Generate log message before updating event
update_message = self.generate_log_message(grbevent, update_dict)
# Update the event
for attribute in update_dict:
setattr(grbevent, attribute, update_dict[attribute])
# Save log message
# Send LVAlert
EventAlertIssuer(grbevent, alert_type='update').issue_alerts()
return Response(eventToDict(grbevent, request=request))
# Neighbors
......@@ -103,6 +103,10 @@ class GracedbRoot(APIView):
signofflist = api_reverse("events:signoff-list", args=["G1200"], request=request)
signofflist = signofflist.replace("G1200", "{graceid}")
update_grbevent = api_reverse("events:update-grbevent", args=["G1200"],
update_grbevent = update_grbevent.replace("G1200", "{graceid}")
# XXX Need a template for the tag list?
templates = {
......@@ -119,6 +123,7 @@ class GracedbRoot(APIView):
"tag-template" : tag,
"taglist-template" : taglist,
"signoff-list-template": signofflist,
"update-grbevent-template": update_grbevent,
# Get superevent templates
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment