Maintenance will be performed on,,, and tomorrow, 2020/08/04, starting at approximately 9am PDT. It is expected to take around 15 minutes and there will be a short period of downtime towards the end of the maintenance window. Please direct any comments, questions or concerns to

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
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment