Source code for gwsumm.tabs.gracedb

# coding=utf-8
# Copyright (C) Duncan Macleod (2013)
#
# This file is part of GWSumm
#
# GWSumm is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# GWSumm is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with GWSumm.  If not, see <http://www.gnu.org/licenses/>

"""Custom `SummaryTab` to display events queried from the Gravitational-wave
Candidate Event Database (GraceDb)
"""

from collections import OrderedDict
from configparser import NoOptionError

from MarkupPy import markup

from gwpy.time import from_gps

from .registry import (get_tab, register_tab)
from ..config import GWSummConfigParser
from ..utils import (re_quote, vprint)

__author__ = 'Duncan Macleod <duncan.macleod@ligo.org>'
__all__ = ['GraceDbTab']

LABELS = OrderedDict()
LABELS["danger"] = {
    "ADVNO",
    "DQV",
    "H1NO",
    "L1NO",
    "V1NO",
}
LABELS["warning"] = {
    "INJ",
}
LABELS["success"] = {
    'GCN_PRELIM_SENT',
}


[docs] class GraceDbTab(get_tab('default')): """Custom tab displaying a summary of GraceDb results. """ type = 'gracedb' def __init__(self, name, url='https://gracedb.ligo.org', query='External', columns=['gpstime', 'date', 'pipeline'], headers=['GPS time', 'UTC time', 'Source'], rank='gpstime', **kwargs): super(GraceDbTab, self).__init__(name, **kwargs) self.url = url self.query = '{} {} .. {}'.format( query, int(self.start), int(self.end), ) self.events = dict() self.headers = headers self.columns = columns self.rank = rank
[docs] @classmethod def from_ini(cls, config, section, **kwargs): """Define a new `GraceDbTab` from a `ConfigParser`. """ for key in ['url', 'query', 'rank']: try: kwargs.setdefault( key, re_quote.sub('', config.get(section, key))) except NoOptionError: pass for key in ['columns', 'headers']: try: raw = config.get(section, key) val = eval(raw) except NoOptionError: continue except (SyntaxError, NameError, TypeError): val = [x.strip().rstrip() for x in raw.split(',')] kwargs.setdefault(key, val) return super(GraceDbTab, cls).from_ini(config, section, **kwargs)
[docs] def process(self, config=GWSummConfigParser(), **kwargs): try: from ligo.gracedb.rest import GraceDb from ligo.gracedb.exceptions import HTTPError except ImportError as e: e.args = ('%s, this module is required to generate a GraceDbTab' % str(e),) raise # query gracedb service_url = '%s/api/' % self.url self.connection = GraceDb(service_url=service_url) self.exception = HTTPError vprint('Connected to gracedb at %s\n' % service_url) try: self.events[None] = list(self.connection.superevents(self.query)) self._query_type = 'S' except self.exception: self.events[None] = list(self.connection.events(self.query)) event_method = self.connection.event eventid_name = 'graceid' self._query_type = 'E' else: event_method = self.connection.superevent eventid_name = 'superevent_id' for event in self.events[None]: # get preferred event parameters event.update(self.connection.event( event['preferred_event'], ).json()) vprint('Recovered %d events for query %r\n' % (len(self.events[None]), self.query)) if 'labels' in self.columns: for e in self.events[None]: e['labels'] = ', '.join(event_method( e[eventid_name]).json()['labels']) vprint('Downloaded labels\n') return super(GraceDbTab, self).process(config=config, **kwargs)
[docs] def process_state(self, state, **kwargs): def in_state(event): return int(event['gpstime']) in state.active self.events[str(state)] = list(filter(in_state, self.events[None])) reverse = self.rank not in ['gpstime', 'far'] self.events[str(state)].sort(key=lambda x: x[self.rank], reverse=reverse) vprint(' Selected %d events\n' % len(self.events[str(state)]))
[docs] def write_state_html(self, state): """Write the '#main' HTML content for this `GraceDbTab`. """ page = markup.page() # build table of events page.table(class_='table table-sm table-hover table-striped mt-2', id_='gracedb') # thead page.thead() page.tr() for head in self.headers: page.th(head) page.tr.close() page.thead.close() # tbody page.tbody() for event in sorted(self.events[str(state)], key=lambda e: e['gpstime']): context = None try: labs = set(event['labels'].split(', ')) except (AttributeError, KeyError): pass else: for ctx, labels in LABELS.items(): if ( ctx == 'success' and labs.union(labels) == labs or labs.intersection(labels) ): context = ctx break if context: page.tr(class_='table-%s' % context) else: page.tr() for col in self.columns: if col == 'date': gpskey = 't_0' if 'superevent_id' in event else 'gpstime' page.td(from_gps(event[gpskey]).strftime( '%B %d %Y %H:%M:%S.%f', )[:-3]) continue elif col.lower() == 'dqr' and 'superevent_id' in event: page.td() sid = event['superevent_id'] href = ('{0}/apiweb/superevents/{1}/files/' 'dqr.html'.format(self.url, sid)) try: self.connection.get(href) except self.exception: page.p('&mdash;') else: title = 'Data-quality report for {}'.format(sid) page.a('DQR', title=title, href=href, target='_blank', rel='external', class_='btn btn-info btn-sm') page.td.close() continue elif col.lower() == 'dqr': page.td() page.p('&mdash;') page.td.close() continue try: v = event[col] except KeyError: try: v = event['extra_attributes']['GRB'][col] assert v is not None except (KeyError, AssertionError): page.td('-') continue if col in ('graceid', 'superevent_id', 'preferred_event'): page.td() tag = 'superevents' if col == 'superevent_id' else 'events' href = '{}/{}/view/{}'.format(self.url, tag, v) title = 'GraceDB {} page for {}'.format(tag[:-1], v) page.a(v, title=title, href=href, target='_blank', rel='external', class_='btn btn-info btn-sm') page.td.close() elif col not in ('gpstime', 't_0') and isinstance(v, float): page.td('%.3g' % v) elif col == 'labels': page.td(', '.join( ['<samp>%s</samp>' % iterable for iterable in sorted(labs)])) else: page.td(str(v)) page.tr.close() page.tbody.close() page.table.close() if len(self.events[str(state)]) == 0: page.p('No events were recovered for this state.') else: page.button( 'Export to CSV', class_='btn btn-outline-secondary btn-table mt-2', **{'data-table-id': 'gracedb', 'data-filename': 'gracedb.csv'}) # query doc qurl = '{}/search/?query={}&query_type={}&results_format=S'.format( self.url, self.query.replace(' ', '+'), getattr(self, '_query_type', 'E'), ) qlink = markup.oneliner.a( 'here', href=qurl, target='_blank', ) page.p('The above table was generated from a query to {} with the ' 'form <code>{}</code>. To view the results of the same query ' 'via the GraceDB web interface, click {}.'.format( self.url, self.query, qlink), class_='mt-2') # reference the labelling page.h4('Labelling reference') page.p('Events in the above table may have a context based on ' 'its labels as follows:') for c, labels in LABELS.items(): c = (c if c == 'warning' else '%s text-white' % c) labstr = ', '.join(['<samp>%s</samp>' % item for item in sorted(labels)]) page.p(labstr, class_='bg-%s pl-2' % c, style='width: auto;') # write to file idx = self.states.index(state) with open(self.frames[idx], 'w') as fobj: fobj.write(str(page)) return self.frames[idx]
register_tab(GraceDbTab)