Commit 5f569dac authored by Alexander Pace's avatar Alexander Pace
Browse files

gracedb-2.7.8

parent e6bfa68f
Pipeline #443344 passed with stages
in 13 minutes and 53 seconds
ligo-gracedb (2.7.8-1) unstable; urgency=low
* pipeline-specific preferred event features
* camelCase to snake_case transition
* documentation changes
-- Alexander E. Pace <alexander.pace@ligo.org> Thu, 04 Aug 2022 20:32:56 -0400
ligo-gracedb (2.7.7-1) unstable; urgency=low
* Drop python2 support
* Dependency and testing fixes
......
%define srcname ligo-gracedb
%define version 2.7.7
%define unmangled_version 2.7.7
%define version 2.7.8
%define unmangled_version 2.7.8
%define release 1
Summary: Gravity Wave Candidate Event Database
......
......@@ -17,4 +17,4 @@
# along with gracedb. If not, see <http://www.gnu.org/licenses/>.
from .version import __version__
__all__ = ["cli", "exceptions", "rest", "utils"]
__all__ = ["cli", "exceptions", "rest", "utils", "alias"]
# The below code was heavily influenced from:
# https://code.activestate.com/recipes/577659-decorators-for-adding-aliases-to-methods-in-a-clas/
# , and was modified for ligo-gracedb.
# Please see the referenced page for authorship and copyright information.
import warnings
import functools
class alias(object):
"""
Alias class that can be used as a decorator for making methods callable
through other names (or "aliases").
"""
def __init__(self, *aliases):
self.aliases = set(aliases)
def __call__(self, f):
"""
Method call wrapper.
"""
f._aliases = self.aliases
return f
def aliased(aliased_class):
"""
Decorator function that must be used in combination with @alias
decorator. Modified to produce a deprecation warning.
"""
def warning_wrapper(func, alias):
warning_string = ("Method {old_name} has been replaced by {new_name}"
", and will be deprecated in a future release")
@functools.wraps(func)
def wrapper(*args, **kwds):
warnings.warn(warning_string.format(old_name=alias,
new_name=func.__name__),
DeprecationWarning,
stacklevel=2)
return func(*args, **kwds)
return wrapper
original_methods = aliased_class.__dict__.copy()
for name, method in original_methods.items():
if hasattr(method, '_aliases'):
for alias in method._aliases - set(original_methods):
wrapped_method = warning_wrapper(method, alias)
setattr(aliased_class, alias, wrapped_method)
return aliased_class
......@@ -46,7 +46,7 @@ class AddEventCommand(AddChildBase):
return parser
def run(self, client, args):
return client.addEventToSuperevent(args.superevent_id, args.graceid)
return client.add_event_to_superevent(args.superevent_id, args.graceid)
class AddLabelCommand(AddChildBase):
......@@ -64,7 +64,7 @@ class AddLabelCommand(AddChildBase):
return parser
def run(self, client, args):
return client.writeLabel(args.object_id, args.label)
return client.write_label(args.object_id, args.label)
class AddTagCommand(AddChildBase):
......@@ -92,7 +92,7 @@ class AddTagCommand(AddChildBase):
return parser
def run(self, client, args):
return client.addTag(
return client.add_tag(
args.object_id, args.log_number, tag_name=args.tag_name,
displayName=args.tag_display_name
)
......@@ -98,7 +98,7 @@ class CreateEmobservationCommand(CreateChildBase):
return parser
def run(self, client, args):
return client.writeEMObservation(
return client.write_emobservation(
args.object_id, args.group, args.right_ascension,
args.right_ascension_width, args.declination,
args.declination_width, args.start_time, args.duration,
......@@ -161,7 +161,7 @@ class CreateEventCommand(CreateChildBase):
event_file = search
search = temp
return client.createEvent(
return client.create_event(
args.group, args.pipeline, event_file, search=search,
offline=args.offline, labels=args.labels
)
......@@ -184,7 +184,7 @@ class CreateLogCommand(CreateChildBase):
return parser
def run(self, client, args):
return client.writeLog(
return client.write_log(
args.object_id, args.comment, filename=args.filename,
tag_name=args.tag_name, displayName=args.tag_display_name
)
......@@ -281,7 +281,7 @@ class CreateSupereventCommand(CreateChildBase):
return parser
def run(self, client, args):
return client.createSuperevent(
return client.create_superevent(
args.t_start, args.t_0, args.t_end, args.preferred_event,
category=args.category, events=args.events, labels=args.labels
)
......@@ -386,7 +386,7 @@ class CreateVoeventCommand(CreateChildBase):
return parser
def run(self, client, args):
return client.createVOEvent(
return client.create_voevent(
args.object_id, args.voevent_type, skymap_type=args.skymap_type,
skymap_filename=args.skymap_filename, internal=(not args.external),
open_alert=args.open_alert, hardware_inj=args.hardware_inj,
......
......@@ -37,7 +37,7 @@ class RemoveLabelCommand(RemoveChildBase):
return parser
def run(self, client, args):
return client.removeLabel(args.object_id, args.label)
return client.remove_label(args.object_id, args.label)
class RemoveTagCommand(RemoveChildBase):
......@@ -55,7 +55,9 @@ class RemoveTagCommand(RemoveChildBase):
return parser
def run(self, client, args):
return client.removeTag(args.object_id, args.log_number, args.tag_name)
return client.remove_tag(args.object_id,
args.log_number,
args.tag_name)
class RemoveEventCommand(RemoveChildBase):
......@@ -64,5 +66,5 @@ class RemoveEventCommand(RemoveChildBase):
parent_parsers = (superevent_id_parser, graceid_parser,)
def run(self, client, args):
return client.removeEventFromSuperevent(args.superevent_id,
args.graceid)
return client.remove_event_from_superevent(args.superevent_id,
args.graceid)
......@@ -22,7 +22,7 @@ def test_add_event_subcommand(CLI):
}
cmd = 'add event {s_id} {g_id}'.format(**cmd_args)
func = 'ligo.gracedb.rest.GraceDb.addEventToSuperevent'
func = 'ligo.gracedb.rest.GraceDb.add_event_to_superevent'
with mock.patch(func) as mock_cli_func:
CLI(shlex.split(cmd))
......@@ -45,7 +45,7 @@ def test_add_label_subcommand(CLI):
}
cmd = 'add label {s_id} {label}'.format(**cmd_args)
func = 'ligo.gracedb.rest.GraceDb.writeLabel'
func = 'ligo.gracedb.rest.GraceDb.write_label'
with mock.patch(func) as mock_cli_func:
CLI(shlex.split(cmd))
......@@ -73,7 +73,7 @@ def test_add_tag_subcommand(CLI, display_name):
if display_name is not None:
cmd += ' --tag-display-name={dn}'.format(dn=display_name)
func = 'ligo.gracedb.rest.GraceDb.addTag'
func = 'ligo.gracedb.rest.GraceDb.add_tag'
with mock.patch(func) as mock_cli_func:
CLI(shlex.split(cmd))
......
......@@ -49,7 +49,7 @@ def test_create_emobservation_subcommand(CLI, ra_width, dec_width, duration,
if comment is not None:
cmd += " --comment='{comment}'".format(comment=comment)
func = 'ligo.gracedb.rest.GraceDb.writeEMObservation'
func = 'ligo.gracedb.rest.GraceDb.write_emobservation'
with mock.patch(func) as mock_cli_func:
CLI(shlex.split(cmd))
......@@ -92,7 +92,7 @@ def test_create_event_subcommand(CLI, search, labels, offline):
if offline:
cmd += " --offline"
func = 'ligo.gracedb.rest.GraceDb.createEvent'
func = 'ligo.gracedb.rest.GraceDb.create_event'
func2 = 'ligo.gracedb.rest.GraceDb.searches'
with mock.patch(func2, new_callable=mock.PropertyMock) as mock_searches, \
mock.patch(func) as mock_cli_func: # noqa: E127
......@@ -128,7 +128,7 @@ def test_create_event_legacy_usage(CLI):
**cmd_args)
# Have to patch client.searches and os.path.isfile
func = 'ligo.gracedb.rest.GraceDb.createEvent'
func = 'ligo.gracedb.rest.GraceDb.create_event'
func2 = 'ligo.gracedb.rest.GraceDb.searches'
func3 = 'ligo.gracedb.cli.commands.create.os.path.isfile'
with mock.patch(func2, new_callable=mock.PropertyMock) as mock_searches, \
......@@ -186,7 +186,7 @@ def test_create_log_subcommand(CLI, filename, tag_name, tag_disp_name):
cmd += " --tag-display-name={tag_disp_name}".format(
tag_disp_name=tag_disp_str)
func = 'ligo.gracedb.rest.GraceDb.writeLog'
func = 'ligo.gracedb.rest.GraceDb.write_log'
with mock.patch(func) as mock_cli_func:
CLI(shlex.split(cmd))
......@@ -267,7 +267,7 @@ def test_create_superevent_subcommand(CLI, category, labels, events):
else:
cmd += " --events={events}".format(events=events)
func = 'ligo.gracedb.rest.GraceDb.createSuperevent'
func = 'ligo.gracedb.rest.GraceDb.create_superevent'
with mock.patch(func) as mock_cli_func:
CLI(shlex.split(cmd))
......@@ -345,7 +345,7 @@ def test_create_voevent_subcommand(
if mass_gap is not None:
cmd += " --mass-gap={mg}".format(mg=mass_gap)
func = 'ligo.gracedb.rest.GraceDb.createVOEvent'
func = 'ligo.gracedb.rest.GraceDb.create_voevent'
with mock.patch(func) as mock_cli_func:
CLI(shlex.split(cmd))
......
......@@ -20,7 +20,7 @@ def test_remove_event_subcommand(CLI):
g_id = 'G123456'
cmd = 'remove event {s_id} {g_id}'.format(s_id=s_id, g_id=g_id)
func = 'ligo.gracedb.rest.GraceDb.removeEventFromSuperevent'
func = 'ligo.gracedb.rest.GraceDb.remove_event_from_superevent'
with mock.patch(func) as mock_cli_func:
CLI(shlex.split(cmd))
......@@ -41,7 +41,7 @@ def test_remove_label_subcommand(CLI):
label_name = 'TEST_LABEL'
cmd = 'remove label {s_id} {label}'.format(s_id=s_id, label=label_name)
func = 'ligo.gracedb.rest.GraceDb.removeLabel'
func = 'ligo.gracedb.rest.GraceDb.remove_label'
with mock.patch(func) as mock_cli_func:
CLI(shlex.split(cmd))
......@@ -66,7 +66,7 @@ def test_remove_tag_subcommand(CLI):
tag=tag_name
)
func = 'ligo.gracedb.rest.GraceDb.removeTag'
func = 'ligo.gracedb.rest.GraceDb.remove_tag'
with mock.patch(func) as mock_cli_func:
CLI(shlex.split(cmd))
......
......@@ -24,7 +24,7 @@ def test_update_event_subcommand(CLI):
# Generate command
cmd = 'update event {g_id} {filename}'.format(**cmd_args)
func = 'ligo.gracedb.rest.GraceDb.replaceEvent'
func = 'ligo.gracedb.rest.GraceDb.replace_event'
with mock.patch(func) as mock_cli_func:
CLI(shlex.split(cmd))
......@@ -98,7 +98,7 @@ def test_update_superevent_subcommand(CLI, t_start, t_0, t_end, pref_ev):
if pref_ev is not None:
cmd += " --preferred-event={pe}".format(pe=pref_ev)
func = 'ligo.gracedb.rest.GraceDb.updateSuperevent'
func = 'ligo.gracedb.rest.GraceDb.update_superevent'
with mock.patch(func) as mock_cli_func:
CLI(shlex.split(cmd))
......
......@@ -47,7 +47,7 @@ class UpdateEventCommand(UpdateChildBase):
return parser
def run(self, client, args):
return client.replaceEvent(args.graceid, args.filename)
return client.replace_event(args.graceid, args.filename)
class UpdateSignoffCommand(UpdateChildBase):
......@@ -128,7 +128,7 @@ class UpdateSupereventCommand(UpdateChildBase):
return parser
def run(self, client, args):
return client.updateSuperevent(
return client.update_superevent(
args.superevent_id, t_0=args.t_0, t_start=args.t_start,
t_end=args.t_end, preferred_event=args.preferred_event
)
......
......@@ -23,6 +23,7 @@ import sys
from six.moves import map
from six.moves.urllib.parse import urlencode
from .alias import alias, aliased
from .exceptions import HTTPError
from .utils import event_or_superevent, dict_to_form_encoded, get_mimetype
from .client import GraceDBClient
......@@ -33,6 +34,7 @@ DEFAULT_SERVICE_URL = "https://gracedb.ligo.org/api/"
# -----------------------------------------------------------------
# GraceDb REST client
# -----------------------------------------------------------------
@aliased
class GraceDb(GraceDBClient):
"""GraceDb REST client class.
......@@ -285,8 +287,9 @@ class GraceDb(GraceDBClient):
return None
# Search and filecontents are optional when creating an event.
def createEvent(self, group, pipeline, filename, search=None, labels=None,
offline=False, filecontents=None, **kwargs):
@alias('createEvent')
def create_event(self, group, pipeline, filename, search=None, labels=None,
offline=False, filecontents=None, **kwargs):
"""Create a new event on the server.
All LIGO-Virgo users can create events in the 'Test' group. Special
......@@ -389,7 +392,8 @@ class GraceDb(GraceDBClient):
return self.post(uri, data=fields, files=files)
def replaceEvent(self, graceid, filename, filecontents=None):
@alias('replaceEvent')
def replace_event(self, graceid, filename, filecontents=None):
"""Replace an existing event by uploading a new event file.
The event's parameters are updated from the new file. Only the user
......@@ -412,7 +416,7 @@ class GraceDb(GraceDBClient):
Example:
>>> g = GraceDb()
>>> r = g.replaceEvent('T101383', '/path/to/new/something.xml')
>>> r = g.replace_event('T101383', '/path/to/new/something.xml')
"""
if filecontents is None:
# Note: not allowing filename '-' here. We want the event datafile
......@@ -558,7 +562,8 @@ class GraceDb(GraceDBClient):
return
yield event
def numEvents(self, query=None):
@alias('numEvents')
def num_events(self, query=None):
"""Get the number of events satisfying a query.
Args:
......@@ -573,7 +578,7 @@ class GraceDb(GraceDBClient):
Example:
>>> g = GraceDb()
>>> g.numEvents('ER5 submitter: "gstlalcbc"')
>>> g.num_events('ER5 submitter: "gstlalcbc"')
213
"""
uri = self.links['events']
......@@ -581,8 +586,9 @@ class GraceDb(GraceDBClient):
uri += "?" + urlencode({'query': query})
return self.get(uri).json()['numRows']
def createSuperevent(self, t_start, t_0, t_end, preferred_event,
category='production', events=None, labels=None):
@alias('createSuperevent')
def create_superevent(self, t_start, t_0, t_end, preferred_event,
category='production', events=None, labels=None):
r"""Create a superevent.
All LIGO-Virgo users can create test superevents, but special
......@@ -615,7 +621,7 @@ class GraceDb(GraceDBClient):
Example:
>>> g = GraceDb()
>>> r = g.createSuperevent(1, 2, 3, 'G123456',
>>> r = g.create_superevent(1, 2, 3, 'G123456',
... category='production', events=['G100', 'G101'],
... labels=['EM_READY', 'DQV'])
>>> r.status_code
......@@ -682,9 +688,10 @@ class GraceDb(GraceDBClient):
uri = self.links['superevents']
return self.post(uri, data=request_body)
def updateSuperevent(self, superevent_id, t_start=None, t_0=None,
t_end=None, preferred_event=None, em_type=None,
time_coinc_far=None, space_coinc_far=None):
@alias('updateSuperevent')
def update_superevent(self, superevent_id, t_start=None, t_0=None,
t_end=None, preferred_event=None, em_type=None,
time_coinc_far=None, space_coinc_far=None):
r"""Update a superevent's parameters.
The same permission restrictions apply as for
......@@ -726,8 +733,8 @@ class GraceDb(GraceDBClient):
Example:
>>> g = GraceDb()
>>> r = g.updateSuperevent('S181224a', t_start=12, preferred_event=
... 'G654321')
>>> r = g.update_superevent('S181224a', t_start=12,
... preferred_event='G654321')
>>> r.status_code
200
""" # noqa: W605
......@@ -757,7 +764,8 @@ class GraceDb(GraceDBClient):
uri = template.format(superevent_id=superevent_id)
return self.patch(uri, data=request_body)
def addEventToSuperevent(self, superevent_id, graceid):
@alias('addEventToSuperevent')
def add_event_to_superevent(self, superevent_id, graceid):
"""Add an event to a superevent.
The event must be in the same category as the superevent and must not
......@@ -777,7 +785,7 @@ class GraceDb(GraceDBClient):
Example:
>>> g = GraceDb()
>>> r = addEventToSuperevent('S181224a', 'G123456')
>>> r = add_event_to_superevent('S181224a', 'G123456')
>>> r.status_code
201
"""
......@@ -786,7 +794,8 @@ class GraceDb(GraceDBClient):
uri = template.format(superevent_id=superevent_id)
return self.post(uri, data=request_body)
def removeEventFromSuperevent(self, superevent_id, graceid):
@alias('removeEventFromSuperevent')
def remove_event_from_superevent(self, superevent_id, graceid):
"""Remove an event from a superevent.
The event must already be a part of the superevent.
......@@ -805,7 +814,7 @@ class GraceDb(GraceDBClient):
Example:
>>> g = GraceDb()
>>> r = removeEventFromSuperevent('S181224a', 'G123456')
>>> r = remove_event_from_superevent('S181224a', 'G123456')
>>> r.status_code
204
"""
......@@ -813,6 +822,77 @@ class GraceDb(GraceDBClient):
uri = template.format(superevent_id=superevent_id, graceid=graceid)
return self.delete(uri)
def add_pipeline_preferred_event(self, superevent_id, graceid):
"""Add an event to a superevent's pipeline-preferred
list. The event must already be part of a superevent,
and there can be only one event per pipeline in the list.
Adding a pipeline's event to the list, when an event from that
pipeline is already present will replace that event in the list,
except in the case where the existing event is the preferred event.
Args:
superevent_id (str): ID of the superevent to which the
pipeline-preferred event will be added.
graceid (str): graceid of the event to add to this superevent.
Returns:
:class:`requests.models.Response`
Raises:
ligo.gracedb.exceptions.HTTPError: if the response has a status
code >= 400.
Example:
>>> g = GraceDb()
>>> r = add_pipeline_preferred_event('S181224a', 'G123456')
>>> r.status_code
201
"""
request_body = {'event': graceid}
try:
template = self.templates['superevent-pipeline'
'-preferred-event-list-template']
except KeyError:
raise UserWarning("Feature not enabled on the server")
return
uri = template.format(superevent_id=superevent_id)
return self.post(uri, data=request_body)
def remove_pipeline_preferred_event(self, superevent_id, graceid):
"""Remove an event from a superevent's pipeline-preferred list
The event must already be a part of the preferred list. The preferred
event cannot be removed from the pipeline preferred list.
Args:
superevent_id (str): GraceDB ID of the superevent from which the
event will be removed.
graceid (str): graceid of the event to remove from this superevent.
Returns:
:class:`requests.models.Response`
Raises:
ligo.gracedb.exceptions.HTTPError: if the response has a status
code >= 400.
Example:
>>> g = GraceDb()
>>> r = remove_pipeline_preferred_event('S181224a', 'G123456')
>>> r.status_code
204
"""
try:
template = self.templates['superevent-pipeline-preferred-'
'event-detail-template']
except KeyError:
raise UserWarning("Feature not enabled on the server")
return
uri = template.format(superevent_id=superevent_id, graceid=graceid)
return self.delete(uri)
def superevent(self, superevent_id):
"""Get information about an individual superevent.
......@@ -1058,8 +1138,9 @@ class GraceDb(GraceDBClient):
return self.get(uri)
@event_or_superevent
def writeLog(self, object_id, message, filename=None, filecontents=None,
tag_name=[], displayName=[], *args, **kwargs):
@alias('writeLog')
def write_log(self, object_id, message, filename=None, filecontents=None,
tag_name=[], displayName=[], *args, **kwargs):
"""Create a new log entry associated with an event or superevent.
Args:
......@@ -1086,7 +1167,7 @@ class GraceDb(GraceDBClient):
Example:
>>> g = GraceDb()
>>> r = g.writeLog('T101383', 'Good stuff.', '/path/to/plot.png',
>>> r = g.write_log('T101383', 'Good stuff.', '/path/to/plot.png',
... tag_name='analyst_comments')
>>> print r.status_code
201
......@@ -1212,9 +1293,10 @@ class GraceDb(GraceDBClient):
return self.get(uri)
@event_or_superevent
def writeEMObservation(self, object_id, group, raList, raWidthList,
decList, decWidthList, startTimeList, durationList,
comment="", *args, **kwargs):
@alias('writeEMObservation')
def write_emobservation(self, object_id, group, raList, raWidthList,
decList, decWidthList, startTimeList, durationList,
comment="", *args, **kwargs):
"""Create an EM observation data entry for an event or superevent.
Args:
......@@ -1246,7 +1328,7 @@ class GraceDb(GraceDBClient):
Example:
>>> g = GraceDb()
>>> r = g.writeEMObservation('S190131g', 'ZTF', [1, 2, 3],
>>> r = g.write_emobservation('S190131g', 'ZTF', [1, 2, 3],
... [0.1, 0.1, 0.1], [4, 5, 6], 0.2, [],
... [10, 9, 11], comment="data uploaded")
>>> r.status
......@@ -1347,7 +1429,8 @@ class GraceDb(GraceDBClient):
return self.get(uri)
@event_or_superevent
def writeLabel(self, object_id, label, *args, **kwargs):
@alias('writeLabel')
def write_label(self, object_id, label, *args, **kwargs):
"""Add a label to an event or superevent.
Args:
......@@ -1365,7 +1448,7 @@ class GraceDb(GraceDBClient):
Example:
>>> g = GraceDb()
>>> r = g.writeLabel('T101383', 'DQV')
>>> r = g.write_label('T101383', 'DQV')
>>> r.status_code
201
"""
......@@ -1390,7 +1473,8 @@ class GraceDb(GraceDBClient):
return self.put(uri, data=request_body)
@event_or_superevent
def removeLabel(self, object_id, label, *args, **kwargs):
@alias('removeLabel')
def remove_label(self, object_id, label, *args, **kwargs):
"""Remove a label from an event or superevent.
Args:
......@@ -1407,7 +1491,7 @@ class GraceDb(GraceDBClient):
Example: