...
 
Commits (6)
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')
try:
from unittest import mock
except ImportError: # python < 3
import mock
import pytest
from django.conf import settings
from django.contrib.auth import get_user_model
from django.contrib.auth.models import Permission
from django.contrib.contenttypes.models import ContentType
from django.urls import reverse
from guardian.shortcuts import assign_perm
from rest_framework.test import APIRequestFactory as rf
from events.models import Event, GrbEvent, Group, Pipeline, Search
from ..views import GrbEventPatchView
from ...settings import API_VERSION
UserModel = get_user_model()
###############################################################################
# UTILITIES ###################################################################
###############################################################################
def v_reverse(viewname, *args, **kwargs):
"""Easily customizable versioned API reverse for testing"""
viewname = 'api:{version}:'.format(version=API_VERSION) + viewname
return reverse(viewname, *args, **kwargs)
def create_grbevent(internal_group):
user = UserModel.objects.create(username='grbevent.creator')
grb_search, _ = Search.objects.get_or_create(name='GRB')
grbevent = GrbEvent.objects.create(
submitter=user,
group=Group.objects.create(name='External'),
pipeline=Pipeline.objects.create(name=settings.GRB_PIPELINES[0]),
search=grb_search
)
p, _ = Permission.objects.get_or_create(
content_type=ContentType.objects.get_for_model(GrbEvent),
codename='change_grbevent'
)
assign_perm(p, internal_group, grbevent)
return grbevent
###############################################################################
# FIXTURES ####################################################################
###############################################################################
###############################################################################
# TESTS #######################################################################
###############################################################################
@pytest.mark.django_db
def test_access(internal_user, internal_group, standard_plus_grb_user):
# NOTE: standard_plus_grb_user is a parametrized fixture (basically a
# list of three users), so this test will run three times.
# Create a GrbEvent
grbevent = create_grbevent(internal_group)
# Get URL and set up request and view
url = v_reverse("events:update-grbevent", args=[grbevent.graceid])
data = {'redshift': 2}
request = rf().patch(url, data=data)
request.user = standard_plus_grb_user
view = GrbEventPatchView.as_view()
with mock.patch('api.v1.events.views.EventAlertIssuer'):
# Process request
response = view(request, grbevent.graceid)
response.render()
# Update grbevent in memory from database
grbevent.refresh_from_db()
if standard_plus_grb_user.username != 'grb.user':
assert response.status_code == 403
assert grbevent.redshift is None
else:
assert response.status_code == 200
assert grbevent.redshift == 2
@pytest.mark.parametrize("data",
[
{'redshift': 2, 't90': 12, 'designation': 'good'},
{'ra': 1, 'dec': 2, 'error_radius': 3},
# FAR should not be updated
{'far': 123, 't90': 15},
]
)
@pytest.mark.django_db
def test_parameter_updates(grb_user, internal_group, data):
grbevent = create_grbevent(internal_group)
grbevent.far = 321
grbevent.save(update_fields=['far'])
# Get URL and set up request and view
url = v_reverse("events:update-grbevent", args=[grbevent.graceid])
request = rf().patch(url, data=data)
request.user = grb_user
view = GrbEventPatchView.as_view()
with mock.patch('api.v1.events.views.EventAlertIssuer'):
# Process request
response = view(request, grbevent.graceid)
response.render()
# Update grbevent in memory from database
grbevent.refresh_from_db()
# Check response
assert response.status_code == 200
# Compare parameters
for attr in GrbEventPatchView.updatable_attributes:
grbevent_attr = getattr(grbevent, attr)
if attr in data:
assert grbevent_attr == data.get(attr)
else:
assert grbevent_attr is None
# FAR should not be updated even by requests which include FAR
assert grbevent.far == 321
@pytest.mark.parametrize("data", [{}, {'redshift': 2}])
@pytest.mark.django_db
def test_update_with_no_new_data(grb_user, internal_group, data):
grbevent = create_grbevent(internal_group)
grbevent.redshift = 2
grbevent.save(update_fields=['redshift'])
# Get URL and set up request and view
url = v_reverse("events:update-grbevent", args=[grbevent.graceid])
request = rf().patch(url, data=data)
request.user = grb_user
view = GrbEventPatchView.as_view()
with mock.patch('api.v1.events.views.EventAlertIssuer'):
# Process request
response = view(request, grbevent.graceid)
response.render()
# Check response
assert response.status_code == 400
assert 'Request would not modify the GRB event' in response.content
@pytest.mark.parametrize("data",
[
{'redshift': 'random string'},
{'t90': 'random string'},
{'ra': 'random string'},
{'dec': 'random string'},
{'error_radius': 'random string'},
]
)
@pytest.mark.django_db
def test_update_with_bad_data(grb_user, internal_group, data):
grbevent = create_grbevent(internal_group)
# Get URL and set up request and view
url = v_reverse("events:update-grbevent", args=[grbevent.graceid])
request = rf().patch(url, data=data)
request.user = grb_user
view = GrbEventPatchView.as_view()
with mock.patch('api.v1.events.views.EventAlertIssuer'):
# Process request
response = view(request, grbevent.graceid)
response.render()
# Check response
assert response.status_code == 400
assert 'must be a float' in response.content
@pytest.mark.django_db
def test_update_non_grbevent(grb_user, internal_group):
event = Event.objects.create(
submitter=grb_user,
group=Group.objects.create(name='External'),
pipeline=Pipeline.objects.create(name='other_pipeline'),
)
p, _ = Permission.objects.get_or_create(
content_type=ContentType.objects.get_for_model(Event),
codename='change_event'
)
assign_perm(p, internal_group, event)
# Get URL and set up request and view
url = v_reverse("events:update-grbevent", args=[event.graceid])
request = rf().patch(url, data={'redshift': 2})
request.user = grb_user
view = GrbEventPatchView.as_view()
with mock.patch('api.v1.events.views.EventAlertIssuer'):
# Process request
response = view(request, event.graceid)
response.render()
# Check response
assert response.status_code == 400
assert 'Cannot update GRB event parameters for non-GRB event' \
in response.content
......@@ -9,6 +9,8 @@ urlpatterns = [
url(r'^$', EventList.as_view(), name='event-list'),
url(r'^(?P<graceid>[GEHMT]\d+)$', EventDetail.as_view(),
name='event-detail'),
url(r'^(?P<graceid>[GEHMT]\d+)/update-grbevent/$',
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, \
SAFE_METHODS
from rest_framework.renderers import BaseRenderer, JSONRenderer, \
BrowsableAPIRenderer
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 search.query.events 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'
else:
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,
CanUpdateGrbEvent)
updatable_attributes = ['t90', 'redshift', 'designation', 'ra', 'dec',
'error_radius']
def process_data(self, data):
cleaned_data = {}
for k, v in data.items():
if k in ['t90', 'redshift', 'ra', 'dec', 'error_radius']:
try:
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':
try:
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 = [
param_template.format(
name=k,
old=getattr(grbevent, k),
new=update_dict[k]
)
for k in update_dict
]
return comment.format(msg=", ".join(update_list))
@event_and_auth_required
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 (grbevent.pipeline.name not in settings.GRB_PIPELINES
or not isinstance(grbevent, GrbEvent)):
msg = ("Cannot update GRB event parameters for non-GRB event "
"{gid}").format(gid=grbevent.graceid)
return Response(msg, status=status.HTTP_400_BAD_REQUEST)
# Process data - should be all floats except designation
data = self.process_data(request.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])
grbevent.save()
# Save log message
grbevent.eventlog_set.create(comment=update_message,
issuer=request.user)
# 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"],
request=request)
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
......
try:
from unittest import mock
except ImportError: # python < 3
import mock
import pytest
from django.conf import settings
from django.contrib.auth.models import (
Group, Permission, AnonymousUser,
)
from django.contrib.contenttypes.models import ContentType
# Groups ----------------------------------------------------------------------
@pytest.mark.django_db
......@@ -37,6 +43,7 @@ def internal_group():
return group
@pytest.mark.django_db
@pytest.fixture
def em_advocates_group():
......@@ -61,6 +68,27 @@ def em_advocates_group():
return group
@pytest.mark.django_db
@pytest.fixture
def grb_managers_group():
group, _ = Group.objects.get_or_create(name='grb_managers')
# Add permissions
perm_data = [
{'model': 'grbevent', 'codename': 't90_grbevent'},
]
permission_list = []
for perm in perm_data:
p, _ = Permission.objects.get_or_create(
content_type=ContentType.objects.get(model=perm['model']),
codename=perm['codename']
)
permission_list.append(p)
group.permissions.add(*permission_list)
return group
# Users =======================================================================
## Basic users ------------------------
......@@ -78,6 +106,7 @@ def public_user():
## Special users ----------------------
@pytest.mark.django_db
@pytest.fixture
def em_advocate_user(django_user_model, internal_group, em_advocates_group):
user, _ = django_user_model.objects.get_or_create(
......@@ -85,7 +114,16 @@ def em_advocate_user(django_user_model, internal_group, em_advocates_group):
em_advocates_group.user_set.add(user)
# Also add to internal group
internal_group.user_set.add(user)
return user
@pytest.mark.django_db
@pytest.fixture
def grb_user(django_user_model, internal_group, grb_managers_group):
user, _ = django_user_model.objects.get_or_create(username='grb.user')
grb_managers_group.user_set.add(user)
# Also add to internal group
internal_group.user_set.add(user)
return user
......@@ -97,3 +135,12 @@ def standard_user(request):
internal user, public user (LV-EM user to come?)
"""
return request.getfixturevalue(request.param)
@pytest.fixture(params=['internal_user', 'public_user', 'grb_user'])
def standard_plus_grb_user(request):
"""
Parametrized fixture which includes:
internal user, public user, GRB managers user
"""
return request.getfixturevalue(request.param)
......@@ -3,7 +3,7 @@ import logging
from django import forms
from django.utils.safestring import mark_safe
from django.utils.html import escape
from .models import Event, Group, Label
from .models import Event, Group, Label, GrbEvent
from .models import Pipeline, Search, Signoff
from django.contrib.auth.models import User
from django.core.exceptions import FieldError
......@@ -49,3 +49,10 @@ class SignoffForm(ModelForm):
class Meta:
model = Signoff
fields = [ 'status', 'comment' ]
class GrbEventUpdateForm(ModelForm):
class Meta:
model = GrbEvent
fields = ['ra', 'dec', 'error_radius', 't90', 'redshift',
'designation']
......@@ -473,76 +473,6 @@ class TestEventNeighborsView(EventSetup, GraceDbTestBase):
self.assertEqual(response.status_code, 403)
class TestEventModifyT90(EventSetup, GraceDbTestBase):
@classmethod
def setUpTestData(cls):
super(TestEventModifyT90, cls).setUpTestData()
# Create a grb event
ext_group = Group.objects.create(name='External')
fermi_pipeline = Pipeline.objects.create(name='Fermi')
grb_search = Search.objects.create(name='GRB')
cls.grb_event = GrbEvent.objects.create(group=ext_group,
pipeline=fermi_pipeline, search=grb_search,
submitter=cls.internal_user)
ctype = ContentType.objects.get_for_model(GrbEvent)
perm = Permission.objects.create(codename='view_grbevent',
content_type=ctype)
assign_default_event_perms(cls.grb_event)
@classmethod
def setUpClass(cls):
super(TestEventModifyT90, cls).setUpClass()
cls.t90_data = {
'redshift': 2,
}
def test_basic_internal_user_t90(self):
"""Basic internal user can't t90 GRB events"""
url = reverse('modify_t90', args=[self.grb_event.graceid])
response = self.request_as_user(url, "POST", self.internal_user,
data=self.t90_data)
self.assertEqual(response.status_code, 403)
self.assertEqual(response.content,
"You aren't authorized to modify GRB attributes.")
def test_privileged_internal_user_t90(self):
"""Privileged internal user can t90 GRB events"""
# Create permission and give to user
ctype = ContentType.objects.get_for_model(GrbEvent)
perm = Permission.objects.create(codename='t90_grbevent',
content_type=ctype)
perm.user_set.add(self.internal_user)
# Make request and check response
url = reverse('modify_t90', args=[self.grb_event.graceid])
response = self.request_as_user(url, "POST", self.internal_user,
data=self.t90_data)
# 302 response code means success since we were redirected to the
# event page
self.assertEqual(response.status_code, 302)
self.assertEqual(response.url, reverse('view',
args=[self.grb_event.graceid]))
self.grb_event.refresh_from_db()
self.assertEqual(self.grb_event.redshift, self.t90_data['redshift'])
def test_lvem_user_t90(self):
"""LV-EM user can't t90 GRB events"""
url = reverse('modify_t90', args=[self.grb_event.graceid])
response = self.request_as_user(url, "POST", self.lvem_user,
data=self.t90_data)
self.assertEqual(response.status_code, 403)
self.assertEqual(response.templates[0].name, '403.html')
def test_public_user_t90(self):
"""Public user can't t90 GRB events"""
url = reverse('modify_t90', args=[self.grb_event.graceid])
response = self.request_as_user(url, "POST", data=self.t90_data)
self.assertEqual(response.status_code, 403)
self.assertEqual(response.templates[0].name, '403.html')
class TestEventModifyPermissions(EventSetup, GraceDbTestBase):
@classmethod
......
try:
from unittest import mock
except ImportError: # python < 3
import mock
import pytest
from django.conf import settings
from django.contrib.auth import get_user_model
from django.contrib.auth.models import Permission
from django.contrib.contenttypes.models import ContentType
from django.test import Client
from django.urls import reverse
from guardian.shortcuts import assign_perm
from events.models import Event, GrbEvent, Group, Pipeline, Search
UserModel = get_user_model()
###############################################################################
# UTILITIES ###################################################################
###############################################################################
def create_grbevent(internal_group):
user = UserModel.objects.create(username='grbevent.creator')
grb_search, _ = Search.objects.get_or_create(name='GRB')
grbevent = GrbEvent.objects.create(
submitter=user,
group=Group.objects.create(name='External'),
pipeline=Pipeline.objects.create(name=settings.GRB_PIPELINES[0]),
search=grb_search
)
p, _ = Permission.objects.get_or_create(
content_type=ContentType.objects.get_for_model(GrbEvent),
codename='view_grbevent'
)
assign_perm(p, internal_group, grbevent)
return grbevent
###############################################################################
# FIXTURES ####################################################################
###############################################################################
###############################################################################
# TESTS #######################################################################
###############################################################################
# 1. Test that form shows up for authorized users for grbevents
# 2. Test that form doesn't show up for unauthorized users for grbevents
# 3. Test that form doesn't show up for authorized users and for non-grbevents
@pytest.mark.django_db
def test_view_access_and_context(internal_group, standard_plus_grb_user):
# NOTE: standard_plus_grb_user is a parametrized fixture (basically a
# list of three users), so this test will run three times.
# Create a GrbEvent
grbevent = create_grbevent(internal_group)
# Get URL and set up request and view
url = reverse("view", args=[grbevent.graceid])
c = Client()
if not standard_plus_grb_user.is_anonymous:
c.force_login(standard_plus_grb_user)
with mock.patch('events.views.filter_events_for_user'):
# Process request
response = c.get(url)
# Check response code
if standard_plus_grb_user.is_anonymous:
assert response.status_code == 403
else:
assert response.status_code == 200
if standard_plus_grb_user.groups.filter(name='grb_managers').exists():
assert response.context.get('can_update_grbevent', None) is True
assert 'update_grbevent_form' in response.context
else:
assert response.context.get('can_update_grbevent', None) is False
assert 'update_grbevent_form' not in response.context
@pytest.mark.django_db
def test_view_for_non_grbevent(internal_group, grb_user):
event = Event.objects.create(
submitter=grb_user,
group=Group.objects.create(name='External'),
pipeline=Pipeline.objects.create(name='other_pipeline'),
)
p, _ = Permission.objects.get_or_create(
content_type=ContentType.objects.get_for_model(Event),
codename='view_event'
)
assign_perm(p, internal_group, event)
# Get URL and set up request and view
url = reverse("view", args=[event.graceid])
c = Client()
c.force_login(grb_user)
with mock.patch('events.views.filter_events_for_user'):
# Process request
response = c.get(url)
# Check response
assert response.status_code == 200
assert 'can_update_grbevent' not in response.context
assert 'update_grbevent_form' not in response.context
......@@ -26,9 +26,6 @@ urlpatterns = [
'(,(?P<delta2>[-+]?\d+)\)?)?/$'), views.neighbors, name="neighbors"),
# Form processing ---------------------------------------------------------
# Modify t90
url(r'^(?P<graceid>[GEHMT]\d+)/t90/$', views.modify_t90,
name="modify_t90"),
# Modify permissions
url(r'^(?P<graceid>[GEHMT]\d+)/perms/$', views.modify_permissions,
name="modify_permissions"),
......
......@@ -16,7 +16,7 @@ from core.file_utils import get_file_list
from core.http import check_and_serve_file
from .models import Event, Group, EventLog, Label, Tag, Pipeline, Search, GrbEvent
from .models import EMGroup, Signoff, PipelineLog
from .forms import CreateEventForm, SignoffForm
from .forms import CreateEventForm, SignoffForm, GrbEventUpdateForm
from django.contrib.auth.decorators import permission_required
from django.contrib.auth.models import User, Permission
......@@ -369,8 +369,15 @@ def view(request, event):
# given permission to access an event. We want it to be the observers group.
context['lvem_group_name'] = settings.LVEM_OBSERVERS_GROUP
# GRB event update permissions/form
if event.pipeline.name in settings.GRB_PIPELINES:
context['can_modify_t90'] = request.user.has_perm('events.t90_grbevent')
context['can_update_grbevent'] = request.user.has_perm(
'events.t90_grbevent')
# If the user has permission, add the form
if context['can_update_grbevent']:
context['update_grbevent_form'] = \
GrbEventUpdateForm(instance=event)
# Is the user an external user? (I.e., not part of the LVC?) The template
# needs to know that in order to decide what pieces of information to show.
......@@ -747,41 +754,6 @@ def emobservation_entry(request, event, num=None):
else:
return HttpResponseBadRequest("This URL only supports POST.")
#
# Despite the name, this view function will handle updates to all of the
# hand-entered GRB data, including t90, redshift, and event designation.
#
@event_and_auth_required
def modify_t90(request, event):
if not request.method=='POST':
msg = 'This URL only allows POST.'
return HttpResponseBadRequest(msg)
if not isinstance(event, GrbEvent):
msg = 'This method only works on GrbEvent objects.'
return HttpResponseBadRequest(msg)
if not request.user.has_perm('events.t90_grbevent'):
msg = "You aren't authorized to modify GRB attributes."
return HttpResponseForbidden(msg)
designation = request.POST.get('designation', None)
redshift = request.POST.get('redshift', None)
t90 = request.POST.get('t90', None)
if not (t90 or designation or redshift):
msg = 'This method requires one of: designation, redshift, or t90 in POST.'
return HttpResponseBadRequest(msg)
if t90:
event.t90 = t90
elif redshift:
event.redshift = redshift
elif designation:
event.designation = designation
event.save()
# Finished. Redirect back to the event.
return HttpResponseRedirect(reverse("view", args=[event.graceid]))
def get_signoff_type(stype):
for t in Signoff.SIGNOFF_TYPE_CHOICES:
......
# -*- coding: utf-8 -*-
# Generated by Django 1.11.23 on 2019-08-09 18:21
from __future__ import unicode_literals
from django.db import migrations
AUTHGROUP_INFO = {
'name': 'grb_managers',
'description': ('LIGO/Virgo members in the GRB subgroup who have '
'permission to update external GRB events in GraceDB'),
}
def create_authgroup(apps, schema_editor):
AuthGroup = apps.get_model('ligoauth', 'AuthGroup')
Permission = apps.get_model('auth', 'Permission')
# Create AuthGroup
ag = AuthGroup.objects.create(**AUTHGROUP_INFO)
# Get permissions for "T90"-ing a grbevent
perm = Permission.objects.get(
content_type__app_label='events',
codename='t90_grbevent'
)
# *IMPORTANT*: clear all current users who have this
# permission. It should only be assigned to groups
# going forward.
perm.user_set.clear()
# Add this permission to the new AuthGroup
ag.permissions.add(perm)
def delete_authgroup(apps, schema_editor):
AuthGroup = apps.get_model('ligoauth', 'AuthGroup')
# Delete AuthGroup
ag = AuthGroup.objects.get(name=AUTHGROUP_INFO['name'])
ag.delete()
class Migration(migrations.Migration):
dependencies = [
('ligoauth', '0049_update_access_managers_membership'),
]
operations = [
migrations.RunPython(create_authgroup, delete_authgroup),
]
# -*- coding: utf-8 -*-
# Generated by Django 1.11.20 on 2019-08-08 18:27
from __future__ import unicode_literals
from django.db import migrations
GROUP_NAME = 'grb_managers'
USERS = [
'robert.coyne@LIGO.ORG',
'ryan.fisher@LIGO.ORG',
'francesco.pannarale@LIGO.ORG',
'jordan.palamos@LIGO.ORG',
'ronaldas.macas@LIGO.ORG',
'andrew.williamson@LIGO.ORG',
'iain.dorrington@LIGO.ORG',
]
def add_users(apps, schema_editor):
AuthGroup = apps.get_model('ligoauth', 'AuthGroup')
User = apps.get_model('auth', 'User')
group = AuthGroup.objects.get(name=GROUP_NAME)
for username in USERS:
user, _ = User.objects.get_or_create(username=username)
group.user_set.add(user)
class Migration(migrations.Migration):
dependencies = [
('ligoauth', '0050_create_grb_managers_authgroup'),
]
operations = [
migrations.RunPython(add_users, migrations.RunPython.noop),
]
......@@ -12,6 +12,7 @@
<script src="{% static "moment/moment.js" %}"></script>
<script src="{% static "moment-timezone/builds/moment-timezone-with-data.min.js" %}"></script>
<script src="{% static "dojo/dojo.js" %}" data-dojo-config="async: true"></script>
<script src="{% static "jquery/dist/jquery.min.js" %}"></script>
<!-- Styles for dgrid -->
<!-- <link rel="stylesheet" href="{% static "dgrid/css/dgrid.css" %}" /> -->
<!-- Styles for the editor components -->
......
......@@ -90,19 +90,18 @@
</tbody>
</table>
{% if can_modify_t90 %}
{% if can_update_grbevent %}
<div class="content-area">
<form action="{% url "modify_t90" object.graceid %}" method="post">
<input type="text" name="t90" value="{{ object.t90 }}">
<input type="submit" value="Update T90" class="t90ButtonClass">
</form>
<form action="{% url "modify_t90" object.graceid %}" method="post">
<input type="text" name="redshift" value="{{ object.redshift }}">
<input type="submit" value="Update redshift" class="t90ButtonClass">
</form>
<form action="{% url "modify_t90" object.graceid %}" method="post">
<input type="text" name="designation" value="{{ object.designation }}">
<input type="submit" value="Update designation" class="t90ButtonClass">
<h3>Update GRB Event</h3>
<form id="update_grbevent_form" action="{% url "legacy_apiweb:default:events:update-grbevent" object.graceid %}">
<table>
{{ update_grbevent_form.as_table }}
<tr>
<td></td>
<td><input type="submit" value="Update" disabled></td>
</tr>
</table>
</form>
</div>
{% endif %}
......
......@@ -225,6 +225,49 @@ require([
Save, Preview, ScrollPane, Uploader) {
parser.parse();
// We don't enable the input buttons until right now otherwise fast users
// can trigger the form before the javascript is ready... not ideal
$("#update_grbevent_form input[type=submit]").attr('disabled', false);
// Update GRB form
$("#update_grbevent_form").submit(function(e) {
e.preventDefault();
// Get button and disable it to prevent multiple clicks
var submit_button = $(this).find("input[type=submit]");
submit_button.attr("disabled", true);
// Make ajax request - we have to specify a PATCH method here
// since we can't do it in the HTML
$.ajax({
type: 'PATCH',
url: $(this).attr('action'),
data: $(this).serialize(),
success: function(resp) {
// Don't need to re-enable since we reload the page
//submit_button.attr("disabled", false);
location.reload(true);
},
error: function(error) {
//this.button.set("disabled", false);
var err_msg = "Error " + error.status + ": ";
if (error.responseText != "") {
err_msg += error.responseText;
} else {
err_msg += error.statusText;
}
if (error.status == 404) {
err_msg += ". Reload the page.";
}
alert(err_msg);
// Re-enable submit button
submit_button.attr("disabled", false);
}
});
});
//----------------------------------------------------------------------------------------
// Some utility functions
//----------------------------------------------------------------------------------------
......