From 01a132effb2e5c79024cac063e9389634a745e7a Mon Sep 17 00:00:00 2001 From: Alexander Pace <alexander.pace@ligo.org> Date: Wed, 6 Mar 2024 19:37:18 +0000 Subject: [PATCH] add icecube pipeline --- ..._external.json => event_external_grb.json} | 0 .../source/dicts/event_external_neutrino.json | 48 +++ docs/user_docs/source/dicts/event_ml.json | 43 +++ docs/user_docs/source/models.rst | 23 +- gracedb/core/utils.py | 46 ++- .../migrations/0091_add_icecube_pipeline.py | 52 +++ .../events/migrations/0092_neutrinoevent.py | 39 +++ gracedb/events/models.py | 35 ++ gracedb/events/translator.py | 52 +++ gracedb/events/view_logic.py | 3 + gracedb/events/view_utils.py | 30 ++ gracedb/events/views.py | 2 + .../0022_populate_icecube_uploaders.py | 59 ++++ .../templates/gracedb/event_detail_GRB.html | 305 +++++++++--------- .../templates/gracedb/event_detail_NE.html | 165 ++++++++++ 15 files changed, 746 insertions(+), 156 deletions(-) rename docs/user_docs/source/dicts/{event_external.json => event_external_grb.json} (100%) create mode 100644 docs/user_docs/source/dicts/event_external_neutrino.json create mode 100644 docs/user_docs/source/dicts/event_ml.json create mode 100644 gracedb/events/migrations/0091_add_icecube_pipeline.py create mode 100644 gracedb/events/migrations/0092_neutrinoevent.py create mode 100644 gracedb/migrations/guardian/0022_populate_icecube_uploaders.py create mode 100644 gracedb/templates/gracedb/event_detail_NE.html diff --git a/docs/user_docs/source/dicts/event_external.json b/docs/user_docs/source/dicts/event_external_grb.json similarity index 100% rename from docs/user_docs/source/dicts/event_external.json rename to docs/user_docs/source/dicts/event_external_grb.json diff --git a/docs/user_docs/source/dicts/event_external_neutrino.json b/docs/user_docs/source/dicts/event_external_neutrino.json new file mode 100644 index 000000000..ee076c4bb --- /dev/null +++ b/docs/user_docs/source/dicts/event_external_neutrino.json @@ -0,0 +1,48 @@ +{ + "warnings": [], + "submitter": "wolfgang.pauli@ligo.org", + "created": "2024-03-01 20:18:46 UTC", + "group": "External", + "graceid": "E653136", + "pipeline": "IceCube", + "gpstime": 1384986914.64, + "reporting_latency": 8372630.079705, + "instruments": "", + "nevents": null, + "offline": false, + "search": "HEN", + "far": 4.667681380010147e-09, + "far_is_upper_limit": false, + "likelihood": null, + "labels": [], + "extra_attributes": { + "NeutrinoEvent": { + "ivorn": "ivo://nasa.gsfc.gcn/AMON#ICECUBE_GOLD_Event2023-11-25T22:34:56.64_24_138599_039138591_0", + "coord_system": "UTC-FK5-GEO", + "ra": 176.2601, + "dec": 52.6366, + "error_radius": 0.7792, + "far_ne": 0.1472, + "far_unit": "yr^-1", + "signalness": 0.6312, + "energy": 191.7344, + "src_error_90": 0.7792, + "src_error_50": 0.3035, + "amon_id": 13859939138591, + "run_id": 138599, + "event_id": 39138591, + "stream": 24 + } + }, + "superevent": null, + "superevent_neighbours": {}, + "links": { + "neighbors": "https://gracedb-test.ligo.org/api/events/E653136/neighbors/", + "log": "https://gracedb-test.ligo.org/api/events/E653136/log/", + "emobservations": "https://gracedb-test.ligo.org/api/events/E653136/emobservation/", + "files": "https://gracedb-test.ligo.org/api/events/E653136/files/", + "labels": "https://gracedb-test.ligo.org/api/events/E653136/labels/", + "self": "https://gracedb-test.ligo.org/api/events/E653136", + "tags": "https://gracedb-test.ligo.org/api/events/E653136/tag/" + } +} diff --git a/docs/user_docs/source/dicts/event_ml.json b/docs/user_docs/source/dicts/event_ml.json new file mode 100644 index 000000000..11e1e8ef6 --- /dev/null +++ b/docs/user_docs/source/dicts/event_ml.json @@ -0,0 +1,43 @@ +{ + "submitter": "alan.turing@ligo.org", + "created": "2024-02-20 16:41:20 UTC", + "group": "Burst", + "graceid": "G648217", + "pipeline": "MLy", + "gpstime": 1392456472.379048, + "reporting_latency": 26026.564137, + "instruments": "H1,L1", + "nevents": null, + "offline": false, + "search": "AllSky", + "far": 5.855080848625316e-05, + "far_is_upper_limit": false, + "likelihood": null, + "labels": [], + "extra_attributes": { + "MLyBurst": { + "bandwidth": 64.0, + "central_freq": 309.246308659392, + "central_time": 1392456472.379048, + "duration": 0.1875, + "SNR": 6.015912207824155, + "detection_statistic": null, + "scores": { + "coherency": 0.0799756646156311, + "coincidence": 0.2124568223953247, + "combined": 0.016991375573191192 + } + } + }, + "superevent": null, + "superevent_neighbours": {}, + "links": { + "neighbors": "https://gracedb-test.ligo.org/api/events/G648217/neighbors/", + "log": "https://gracedb-test.ligo.org/api/events/G648217/log/", + "emobservations": "https://gracedb-test.ligo.org/api/events/G648217/emobservation/", + "files": "https://gracedb-test.ligo.org/api/events/G648217/files/", + "labels": "https://gracedb-test.ligo.org/api/events/G648217/labels/", + "self": "https://gracedb-test.ligo.org/api/events/G648217", + "tags": "https://gracedb-test.ligo.org/api/events/G648217/tag/" + } +} diff --git a/docs/user_docs/source/models.rst b/docs/user_docs/source/models.rst index 66c16e1a9..fcfe169e0 100644 --- a/docs/user_docs/source/models.rst +++ b/docs/user_docs/source/models.rst @@ -12,9 +12,9 @@ The different types of events in GraceDB are distinguished by the following para - ``Group``: the working group responsible for finding the candidate - values: ``CBC``, ``Burst``, ``Detchar``, ``External``, ``Test`` - ``Pipeline``: the data analysis software tool used make the detection - - values: ``MBTA``, ``MBTAOnline``, ``CWB``, ``CWB2G``, ``gstlal``, ``pycbc``, ``spiir``, ``HardwareInjection``, ``Fermi``, ``Swift``, ``INTEGRAL``, ``AGILE``, ``SNEWS``, ``oLIB``, ``MLy`` + - values: ``CWB2G``, ``spiir``, ``HardwareInjection``, ``X``, ``Q``, ``Omega``, ``Ringdown``, ``Fermi``, ``Swift``, ``CWB``, ``SNEWS``, ``oLIB``, ``pycbc``, ``INTEGRAL``, ``AGILE``, ``gstlal``, ``MLy``, ``MBTAOnline``, ``MBTA``, ``CHIME``, ``PyGRB``, ``aframe``, ``SVOM``, ``IceCube`` - ``Search``: the search activity which led to the detection - - values: ``AllSky``, ``AllSkyLong``, ``LowMass``, ``HighMass``, ``GRB``, ``Supernova``, ``MDC``, ``BBH``, ``EarlyWarning``, ``IMBH``, ``SubGRB``, ``SubGRBTargeted``, ``VTInjection`` + - values: ``AllSky``, ``LowMass``, ``HighMass``, ``GRB``, ``Supernova``, ``MDC``, ``LowMassSim``, ``AllSkyLong``, ``O2VirgoTest``, ``BBH``, ``IMBH``, ``SubGRB``, ``EarlyWarning``, ``SubGRBTargeted``, ``SSM``, ``FRB``, ``LensingSubthreshold``, ``VTInjection``, ``HEN`` An individual "event stream" is specified by setting the values of these three parameters. For example, choosing ``Group=CBC``, ``Pipeline=gstlal``, and ``Search=LowMass`` selects the event stream consisting of low-mass inspiral events detected by the gstlal pipeline from the CBC group. @@ -77,11 +77,24 @@ oLIB .. literalinclude:: dicts/event_olib.json :language: JSON +Machine Learning (MLy, aframe) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -External -~~~~~~~~ +.. literalinclude:: dicts/event_ml.json + :language: JSON + + +External (GRB) +~~~~~~~~~~~~~~ + +.. literalinclude:: dicts/event_external_grb.json + :language: JSON + + +External (Neutrino) +~~~~~~~~~~~~~~~~~~~ -.. literalinclude:: dicts/event_external.json +.. literalinclude:: dicts/event_external_neutrino.json :language: JSON diff --git a/gracedb/core/utils.py b/gracedb/core/utils.py index 22102af84..aa40550e6 100644 --- a/gracedb/core/utils.py +++ b/gracedb/core/utils.py @@ -16,8 +16,18 @@ ALPHABET = string.ascii_lowercase BASE = len(ALPHABET) ASCII_ALPHABET_START = ord('a') - 1 -# Unit conversions -far_hr_to_yr = 86400*365.25 +# FAR Unit conversions (fixed 2024/3/1, big error in hr_to_yr) +# Note that the converstion error that was just fixed was only +# in the web page display, so there's thankfully no values in the +# database to go back and fix. + +far_hr_to_yr = 24*365 # 1/yr = 1/hr * (8760 hr/year) +far_yr_to_sec = 1.0/(60*60*24*365) # 1/sec = 1/yr * (1 yr / 3.154e7 sec) +far_hr_to_sec = far_hr_to_yr * far_yr_to_sec # 1/sec = 1/hr * (8760 hr/ yr) * (1 yr/ 3.154e7 sec) + +# Unit formats: +per_hr_formats = ['1/hr', 'hr^-1', '1/hour', 'hour^-1'] +per_yr_formats = ['1/yr', 'yr^-1', '1/year', 'year^-1'] def int_to_letters(num, positive_only=True): @@ -85,6 +95,38 @@ def display_far_hr_to_yr(display_far): return display_far_hr +def return_far_in_hz(far_in, units=None): + """ + A helper function that takes in a far value and a set of units + and returns the value in Hz. expects /hr or /yr atm. + + Raising errors instead of 400's is probably safe here, since users + never directly call this function from the API. + """ + + # Check if units were provided, if not, quit + if not units: raise ValueError('no units input') + + # Make sure the units are a string: + if not isinstance(units, str): raise TypeError('units must be a string') + + # Change the units' formatting so they match the templates: + # make it lowercase and get rid of any spaces: + units_in = units.lower().replace(' ', '') + + # Convert per hour: + if units_in in per_hr_formats: + return far_in * far_hr_to_sec + + # Convert per year: + elif units_in in per_yr_formats: + return far_in * far_yr_to_sec + + # Log and give up if not recognized: + else: + logger.debug('could not recognize input far units') + return None + class CustomExceptionReporter(ExceptionReporter): """ diff --git a/gracedb/events/migrations/0091_add_icecube_pipeline.py b/gracedb/events/migrations/0091_add_icecube_pipeline.py new file mode 100644 index 000000000..744770c2d --- /dev/null +++ b/gracedb/events/migrations/0091_add_icecube_pipeline.py @@ -0,0 +1,52 @@ +# -*- coding: utf-8 -*- + +from django.db import migrations +from events.models import Pipeline, Search + +# Creates initial search pipeline instances + +# List of search pipeline names +NEW_PIPELINES = [ + ('IceCube', Pipeline.PIPELINE_TYPE_EXTERNAL), +] + +NEW_SEARCHES = [ + 'HEN', +] + +def add_pipelines(apps, schema_editor): + Pipeline = apps.get_model('events', 'Pipeline') + Search = apps.get_model('events', 'Search') + + # Create pipelines + for pipeline_name in NEW_PIPELINES: + pipeline, created = Pipeline.objects.get_or_create(name=pipeline_name[0]) + pipeline.pipeline_type = pipeline_name[1] + pipeline.save() + + # Create searches + for search_name in NEW_SEARCHES: + search, created = Search.objects.get_or_create(name=search_name) + search.save() + +def remove_pipelines(apps, schema_editor): + Pipeline = apps.get_model('events', 'Pipeline') + Search = apps.get_model('events', 'Search') + + # Delete pipelines + for pipe in NEW_PIPELINES: + Pipeline.objects.filter(name=pipe[0]).delete() + + # Delete searches + for search in NEW_SEARCHES: + Search.objects.get(name=search).delete() + +class Migration(migrations.Migration): + + dependencies = [ + ('events', '0090_add_svom_pipeline'), + ] + + operations = [ + migrations.RunPython(add_pipelines, remove_pipelines), + ] diff --git a/gracedb/events/migrations/0092_neutrinoevent.py b/gracedb/events/migrations/0092_neutrinoevent.py new file mode 100644 index 000000000..4b0cae390 --- /dev/null +++ b/gracedb/events/migrations/0092_neutrinoevent.py @@ -0,0 +1,39 @@ +# Generated by Django 4.2.10 on 2024-02-29 20:53 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('events', '0091_add_icecube_pipeline'), + ] + + operations = [ + migrations.CreateModel( + name='NeutrinoEvent', + fields=[ + ('event_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='events.event')), + ('ivorn', models.CharField(max_length=200, null=True)), + ('coord_system', models.CharField(max_length=200, null=True)), + ('ra', models.FloatField(null=True)), + ('dec', models.FloatField(null=True)), + ('error_radius', models.FloatField(null=True)), + ('signalness', models.FloatField(null=True)), + ('energy', models.FloatField(null=True)), + ('src_error_90', models.FloatField(null=True)), + ('src_error_50', models.FloatField(null=True)), + ('amon_id', models.BigIntegerField(null=True)), + ('run_id', models.PositiveIntegerField(null=True)), + ('event_id', models.PositiveIntegerField(null=True)), + ('stream', models.PositiveIntegerField(null=True)), + ('far_ne', models.FloatField(null=True)), + ('far_unit', models.CharField(max_length=10, null=True)), + ], + options={ + 'indexes': [models.Index(fields=['ivorn'], name='events_neut_ivorn_3ba7e5_idx'), models.Index(fields=['coord_system'], name='events_neut_coord_s_5392db_idx'), models.Index(fields=['ra'], name='events_neut_ra_5fd98d_idx'), models.Index(fields=['dec'], name='events_neut_dec_250378_idx'), models.Index(fields=['error_radius'], name='events_neut_error_r_b9e51f_idx'), models.Index(fields=['signalness'], name='events_neut_signaln_b90f17_idx'), models.Index(fields=['energy'], name='events_neut_energy_d11350_idx'), models.Index(fields=['src_error_90'], name='events_neut_src_err_27b466_idx'), models.Index(fields=['src_error_50'], name='events_neut_src_err_039571_idx'), models.Index(fields=['amon_id'], name='events_neut_amon_id_fbd455_idx'), models.Index(fields=['run_id'], name='events_neut_run_id_acd04a_idx'), models.Index(fields=['event_id'], name='events_neut_event_i_bb1d6a_idx'), models.Index(fields=['far_ne'], name='events_neut_far_ne_96b9e8_idx'), models.Index(fields=['stream'], name='events_neut_stream_37f8df_idx')], + }, + bases=('events.event',), + ), + ] diff --git a/gracedb/events/models.py b/gracedb/events/models.py index 4908cdbaa..0998b4ed1 100644 --- a/gracedb/events/models.py +++ b/gracedb/events/models.py @@ -734,6 +734,41 @@ class GrbEvent(Event): # models.Index(fields=['redshift', ]), # models.Index(fields=['ivorn', ])] +# External event subclass for neutrino observations. Created in +# support of IceCube integration. +class NeutrinoEvent(Event): + ivorn = models.CharField(max_length=200, null=True) + coord_system = models.CharField(max_length=200, null=True) + ra = models.FloatField(null=True) + dec = models.FloatField(null=True) + error_radius = models.FloatField(null=True) + signalness = models.FloatField(null=True) + energy = models.FloatField(null=True) + src_error_90 = models.FloatField(null=True) + src_error_50 = models.FloatField(null=True) + amon_id = models.BigIntegerField(null=True) + run_id = models.PositiveIntegerField(null=True) + event_id = models.PositiveIntegerField(null=True) + stream = models.PositiveIntegerField(null=True) + far_ne = models.FloatField(null=True) #neutrino event far + far_unit = models.CharField(max_length=10, null=True) + + class Meta: + indexes = [models.Index(fields=['ivorn', ]), + models.Index(fields=['coord_system', ]), + models.Index(fields=['ra', ]), + models.Index(fields=['dec', ]), + models.Index(fields=['error_radius', ]), + models.Index(fields=['signalness', ]), + models.Index(fields=['energy', ]), + models.Index(fields=['src_error_90', ]), + models.Index(fields=['src_error_50', ]), + models.Index(fields=['amon_id', ]), + models.Index(fields=['run_id', ]), + models.Index(fields=['event_id', ]), + models.Index(fields=['far_ne', ]), + models.Index(fields=['stream', ])] + class CoincInspiralEvent(Event): ifos = models.CharField(max_length=20, default="") diff --git a/gracedb/events/translator.py b/gracedb/events/translator.py index f4009c0c5..f6a98a0ed 100644 --- a/gracedb/events/translator.py +++ b/gracedb/events/translator.py @@ -13,6 +13,7 @@ from core.ligolw import GraceDBFlexibleContentHandler import voeventparse as vp from core.time_utils import utc_datetime_to_gps_float +from core.utils import return_far_in_hz from core.vfile import create_versioned_file from .models import EventLog from .models import SingleInspiral @@ -31,6 +32,13 @@ logger = logging.getLogger(__name__) # okay? use_in(GraceDBFlexibleContentHandler) +# some attributes for NeutrinoEvents. These are case-sensitive +# to how they are written in the VOEvent: + +neutrino_event_attrs = ['signalness', 'energy', 'src_error_90', + 'src_error_50', 'AMON_ID', 'run_id', 'event_id', + 'Stream'] + # A small helper function to unzip gzipped files. normally ligolw would # handle all this, but since we're selectively parsing certain tables, # we just unzip the initial upload and then write it as xml. NOTE: this @@ -351,6 +359,7 @@ def handle_uploaded_data(event, datafilename, # Get the event time from the VOEvent file error = None populateGrbEventFromVOEventFile(datafilename, event) + elif pipeline == 'oLIB': # lambda function for converting to a type if not None typecast = lambda t, v: t(v) if v is not None else v @@ -425,6 +434,8 @@ def handle_uploaded_data(event, datafilename, event.save() + elif pipeline in ['IceCube']: + populate_neutrinoevent_from_voevent(datafilename, event) else: # XXX should we do something here? pass @@ -768,3 +779,44 @@ def populateGrbEventFromVOEventFile(filename, event): # Save event event.save() + +def populate_neutrinoevent_from_voevent(filename, event): + # Load file into vp.Voevent instance + with open(filename, 'rb') as f: + v = vp.load(f) + + # Get gpstime: + utc_time = vp.convenience.get_event_time_as_utc(v) + gpstime = utc_datetime_to_gps_float(utc_time) + + # Get position: + pos2d = vp.get_event_position(v) + + # get top-level 'What' params: + voevent_what_params = vp.convenience.get_toplevel_params(v) + + # Assign information to event + event.gpstime = gpstime + + # NeutrinoEvent attributes: + event.ivorn = v.get('ivorn') + event.coord_system = pos2d.system + event.ra = pos2d.ra + event.dec = pos2d.dec + event.error_radius = pos2d.err + + for param in neutrino_event_attrs: + if param in voevent_what_params: + setattr(event, param.lower(), voevent_what_params.get(param).get('value')) + + # see if we captured FAR, and if so, parse the unit: + if 'FAR' in voevent_what_params: + + event.far_ne = float(voevent_what_params.get('FAR').get('value')) + event.far_unit = voevent_what_params.get('FAR').get('unit') + + # Now try and convert the far into hz for the base far: + event.far = return_far_in_hz(event.far_ne, event.far_unit) + + # save the event: + event.save() diff --git a/gracedb/events/view_logic.py b/gracedb/events/view_logic.py index f1199469a..884c15afc 100644 --- a/gracedb/events/view_logic.py +++ b/gracedb/events/view_logic.py @@ -6,6 +6,7 @@ from .models import Pipeline, Search from .models import CoincInspiralEvent from .models import MultiBurstEvent from .models import MLyBurstEvent +from .models import NeutrinoEvent from .models import GrbEvent from .models import SimInspiralEvent from .models import LalInferenceBurstEvent @@ -67,6 +68,8 @@ def _createEventFromForm(request, form): event = LalInferenceBurstEvent() elif pipeline.name in ['MLy', 'aframe']: event = MLyBurstEvent() + elif pipeline.name in ['IceCube']: + event = NeutrinoEvent() else: event = Event() diff --git a/gracedb/events/view_utils.py b/gracedb/events/view_utils.py index 5e7ab58db..22523852f 100644 --- a/gracedb/events/view_utils.py +++ b/gracedb/events/view_utils.py @@ -221,6 +221,11 @@ def assemble_event_extra_attributes(event, request, is_alert): event.mlyburstevent) except: pass + try: + extra_attributes_dict['NeutrinoEvent'] = neutrino_to_dict( + event.neutrinoevent) + except: + pass # Finally add extra attributes for any SingleInspiral objects associated with this event @@ -817,6 +822,31 @@ def lalinferenceburst_to_dict(event): pass return return_dict +def neutrino_to_dict(event): + # A safe routine for returning a neutrinoevent dict + return_dict = {} + try: + return_dict.update({ + "ivorn": event.ivorn, + "coord_system": event.coord_system, + "ra": event.ra, + "dec": event.dec, + "error_radius": event.error_radius, + "far_ne": event.far_ne, + "far_unit": event.far_unit, + "signalness": event.signalness, + "energy": event.energy, + "src_error_90": event.src_error_90, + "src_error_50": event.src_error_50, + "amon_id": event.amon_id, + "run_id": event.run_id, + "event_id": event.event_id, + "stream": event.stream, + }) + except: + pass + return return_dict + def signoffToDict(signoff): return { 'submitter': signoff.submitter.username, diff --git a/gracedb/events/views.py b/gracedb/events/views.py index f80bd58ac..4f1c54ee2 100644 --- a/gracedb/events/views.py +++ b/gracedb/events/views.py @@ -473,6 +473,8 @@ def view(request, event): templates.insert(0, 'gracedb/event_detail_oLIB.html') elif event.pipeline.name in ['MLy', 'aframe']: templates.insert(0, 'gracedb/event_detail_mly.html') + elif event.pipeline.name in ['IceCube']: + templates.insert(0, 'gracedb/event_detail_NE.html') return render(request, templates, context=context) diff --git a/gracedb/migrations/guardian/0022_populate_icecube_uploaders.py b/gracedb/migrations/guardian/0022_populate_icecube_uploaders.py new file mode 100644 index 000000000..6e6670f3a --- /dev/null +++ b/gracedb/migrations/guardian/0022_populate_icecube_uploaders.py @@ -0,0 +1,59 @@ +# -*- coding: utf-8 -*- +# supports: https://git.ligo.org/computing/gracedb/server/-/issues/255 + +from django.db import migrations + +# Creates UserObjectPermission objects which allow specific users +# to add events for pipelines. Based on current production database +# content (27 October 2017) + +# List of pipeline names and lists of usernames who should +# be allowed to add events for them +PP_LIST = [ + { + 'pipeline': 'IceCube', + 'usernames': [ + 'brandon.piotrzkowski@ligo.org', + 'andrew.toivonen@ligo.org', + 'emfollow', + ] + }, +] + +def add_permissions(apps, schema_editor): + User = apps.get_model('auth', 'User') + Permission = apps.get_model('auth', 'Permission') + UserObjectPermission = apps.get_model('guardian', 'UserObjectPermission') + Pipeline = apps.get_model('events', 'Pipeline') + ContentType = apps.get_model('contenttypes', 'ContentType') + + perm = Permission.objects.get(codename='populate_pipeline') + ctype = ContentType.objects.get_for_model(Pipeline) + for pp_dict in PP_LIST: + pipeline, created = Pipeline.objects.get_or_create(name=pp_dict['pipeline']) + + # Loop over users + for username in pp_dict['usernames']: + + # Robot users should have been already created by ligoauth 0003, + # but we have to create human user accounts here + user, _ = User.objects.get_or_create(username=username) + + # Create UserObjectPermission + uop, uop_created = UserObjectPermission.objects.get_or_create( + user=user, permission=perm, content_type=ctype, + object_pk=pipeline.id) + +def remove_permissions(apps, schema_editor): + pass + +class Migration(migrations.Migration): + + dependencies = [ + ('guardian', '0021_populate_svom_uploaders'), + ('events', '0091_add_icecube_pipeline'), + ] + + operations = [ + migrations.RunPython(add_permissions, remove_permissions), + ] diff --git a/gracedb/templates/gracedb/event_detail_GRB.html b/gracedb/templates/gracedb/event_detail_GRB.html index 4a7a5ed47..3614f8e4b 100644 --- a/gracedb/templates/gracedb/event_detail_GRB.html +++ b/gracedb/templates/gracedb/event_detail_GRB.html @@ -1,179 +1,186 @@ {% extends "gracedb/event_detail.html" %} {% load timeutil %} +{% load scientific %} {% block basic_info %} <div class="row my-3 justify-content-center"> - <div class="col-md-10"> - <div id="gdb-table-basic-grb"> - <table class="table-hover table-condensed table-resp-gracedb shadow p-3 mb-5 rounded"> - <thead> - <tr> - <th colspan="8"><h3>Basic Event Information</h3></th> - </tr> - </thead> - <tbody> + <div class="col-md-10"> + <div id="gdb-table-basic-grb"> + <table class="table-hover table-condensed table-resp-gracedb shadow p-3 mb-5 rounded"> + <thead> + <tr> + <th colspan="8"><h3>Basic Event Information</h3></th> + </tr> + </thead> + <tbody> - <tr> - <td>UID</td> - <td data-title="UID">{{ object.graceid }}</td> - </tr> - <tr> - <td>Labels</td> - <td data-title="Labels"> - {% if object.labelling_set.all %} - {% for labelling in object.labelling_set.all %} - <span style="color: {{labelling.label.defaultColor}};" class="label" - data-toggle="tooltip" - data-placement="top" - data-html="true" - title="<em>{{labelling.label.description}}</em><br> - <b>Added by:</b> {{labelling.creator.get_full_name}}<br> - <b>Added:</b> {{labelling.created}}"> - {{ labelling.label.name }}</span> - {% endfor %} - {% else %} - - {% endif %} - </td> - </tr> - <tr> - <td>Group</td> - <td data-title="Group">{{ object.group.name }} </td> - </tr> - <tr> - <td>Pipeline</td> - <td data-title="Pipeline">{{ object.pipeline.name }} </td> - </tr> - <tr> - <td>Search</td> - <td data-title="Search">{{ object.search.name }} </td> - </tr> - <tr> - <td> - {{ "gps"|timeselect:"gps" }} - </td> - <td data-title="Event Time">{% if object.gpstime%} - <!-- <span title="{{ object.gpstime|gpsdate }}">{{ object.gpstime }}</span> --> - {{ object.gpstime|multiTime:"gps" }} - {% endif %}</td> - </tr> - <tr> - <td>Latency (s)</td> - <td>{{ object.reporting_latency|floatformat:"-3" }} </td> - </tr> - <tr> - <td>Links</td> - <td data-title="Links"><a href="{{ object.weburl }}">Data</a></td> - </tr> - <tr> - <td> - {{ "submitted"|timeselect:"utc" }} - </td> - <td data-title="Submitted">{{ object.created|multiTime:"submitted" }}</td> - </tr> + <tr> + <td>UID</td> + <td data-title="UID">{{ object.graceid }}</td> + </tr> + <tr> + <td>Labels</td> + <td data-title="Labels"> + {% if object.labelling_set.all %} + {% for labelling in object.labelling_set.all %} + <span style="color: {{labelling.label.defaultColor}};" class="label" + data-toggle="tooltip" + data-placement="top" + data-html="true" + title="<em>{{labelling.label.description}}</em><br> + <b>Added by:</b> {{labelling.creator.get_full_name}}<br> + <b>Added:</b> {{labelling.created}}"> + {{ labelling.label.name }}</span> + {% endfor %} + {% else %} + + {% endif %} + </td> + </tr> + <tr> + <td>Group</td> + <td data-title="Group">{{ object.group.name }} </td> + </tr> + <tr> + <td>Pipeline</td> + <td data-title="Pipeline">{{ object.pipeline.name }} </td> + </tr> + <tr> + <td>Search</td> + <td data-title="Search">{{ object.search.name }} </td> + </tr> + <tr> + <td> + {{ "gps"|timeselect:"gps" }} + </td> + <td data-title="Event Time">{% if object.gpstime%} + <!-- <span title="{{ object.gpstime|gpsdate }}">{{ object.gpstime }}</span> --> + {{ object.gpstime|multiTime:"gps" }} + {% endif %}</td> + </tr> + <tr> + <td>Latency (s)</td> + <td>{{ object.reporting_latency|floatformat:"-3" }} </td> + </tr> + {% if object.far %} + <tr> + <td>FAR (Hz)</td> + <td data-title="FAR">{{ object.far | scientific }} </td> + </tr> + {% endif %} + <tr> + <td>Links</td> + <td data-title="Links"><a href="{{ object.weburl }}">Data</a></td> + </tr> + <tr> + <td> + {{ "submitted"|timeselect:"utc" }} + </td> + <td data-title="Submitted">{{ object.created|multiTime:"submitted" }}</td> + </tr> - {% if object.superevent %} - <tr> - <td>Superevent</td> - <td><a href="{{ object.superevent.get_web_url }}">{{ object.superevent.superevent_id }}</a></td> - </tr> - {% endif %} + {% if object.superevent %} + <tr> + <td>Superevent</td> + <td><a href="{{ object.superevent.get_web_url }}">{{ object.superevent.superevent_id }}</a></td> + </tr> + {% endif %} - </tbody> + </tbody> - </table> - </div> + </table> </div> + </div> </div> {% endblock %} {% block analysis_specific %} {% if object.grbevent %} <div class="row my-3 justify-content-center"> - <div class="col-md-10"> - <div id="grb-info"> - <table class="table-hover table-condensed table-resp-gracedb shadow p-3 mb-5 rounded"> - <thead> - <tr> - <th colspan="2"><h3>GRB Info</h3></th> - </tr> - </thead> - <tr><td>IVORN</td><td>{{object.ivorn}}</td></tr> - <tr><td>Author</td><td>{{object.author_shortname}}</td></tr> - <tr><td>Author IVORN</td><td>{{object.author_ivorn}}</td></tr> - <tr><td>Observatory</td><td>{{object.observatory_location_id}}</td></tr> - <tr><td>How</th><td> - {% if object.how_reference_url %} - <a href="{{object.how_reference_url}}" target="_new">{{object.how_description}}</a> - {% else %} - {{object.how_description}} - {% endif %} - </td></tr> - {% if object.trigger_id %} - <tr><td>Trigger ID</td><td>{{object.trigger_id}}</td></tr> - {% endif %} - {% if object.trigger_duration %} - <tr><td>Trigger duration</td><td>{{object.trigger_duration}}</td></tr> - {% endif %} - {% if object.far %} - <tr><td>FAR (Hz)</td><td>{{object.far}}</td></tr> - {% endif %} - {% if object.t90 %} - <tr><td>T90</td><td>{{object.t90}}</td></tr> - {% endif %} - {% if object.redshift %} - <tr><td>Redshift</td><td>{{object.redshift}}</td></tr> - {% endif %} - {% if object.designation %} - <tr><td>Event designation</td><td>{{object.designation}}</td></tr> - {% endif %} - </table> - </div> + <div class="col-md-10"> + <div id="grb-info"> + <table class="table-hover table-condensed table-resp-gracedb shadow p-3 mb-5 rounded"> + <thead> + <tr> + <th colspan="2"><h3>GRB Info</h3></th> + </tr> + </thead> + <tr><td>IVORN</td><td>{{object.ivorn}}</td></tr> + <tr><td>Author</td><td>{{object.author_shortname}}</td></tr> + <tr><td>Author IVORN</td><td>{{object.author_ivorn}}</td></tr> + <tr><td>Observatory</td><td>{{object.observatory_location_id}}</td></tr> + <tr><td>How</th><td> + {% if object.how_reference_url %} + <a href="{{object.how_reference_url}}" target="_new">{{object.how_description}}</a> + {% else %} + {{object.how_description}} + {% endif %} + </td></tr> + {% if object.trigger_id %} + <tr><td>Trigger ID</td><td>{{object.trigger_id}}</td></tr> + {% endif %} + {% if object.trigger_duration %} + <tr><td>Trigger duration</td><td>{{object.trigger_duration}}</td></tr> + {% endif %} + {% if object.far %} + <tr><td>FAR (Hz)</td><td>{{object.far}}</td></tr> + {% endif %} + {% if object.t90 %} + <tr><td>T90</td><td>{{object.t90}}</td></tr> + {% endif %} + {% if object.redshift %} + <tr><td>Redshift</td><td>{{object.redshift}}</td></tr> + {% endif %} + {% if object.designation %} + <tr><td>Event designation</td><td>{{object.designation}}</td></tr> + {% endif %} + </table> </div> + </div> </div> <div class="row my-3 justify-content-center"> - <div class="col-md-10"> - <div id="grb-info"> - <table class="table-hover table-condensed table-resp-gracedb shadow p-3 mb-5 rounded"> - <thead> - <tr> - <th colspan="2"><h3>GRB Location</h3></th> - </tr> - </thead> - <tbody> - <tr><td>Coord System</td><td>{{object.coord_system}}</td></tr> - <tr><td>RA</td><td>{{object.ra}}</td></tr> - <tr><td>Dec</td><td>{{object.dec}}</td></tr> - <tr><td>Err</td><td>{{object.error_radius}}</td></tr> - </tbody> - </table> - </div> + <div class="col-md-10"> + <div id="grb-info"> + <table class="table-hover table-condensed table-resp-gracedb shadow p-3 mb-5 rounded"> + <thead> + <tr> + <th colspan="2"><h3>GRB Location</h3></th> + </tr> + </thead> + <tbody> + <tr><td>Coord System</td><td>{{object.coord_system}}</td></tr> + <tr><td>RA</td><td>{{object.ra}}</td></tr> + <tr><td>Dec</td><td>{{object.dec}}</td></tr> + <tr><td>Err</td><td>{{object.error_radius}}</td></tr> + </tbody> + </table> </div> + </div> </div> {% if can_update_grbevent %} <div class="row my-3 justify-content-center"> - <div class="col-md-10"> - <table class="table-hover table-condensed table-resp-gracedb shadow p-3 mb-5 rounded"> - <thead> - <tr> - <th colspan="2"><h3>Update GRB Event</h3></th> - </tr> - </thead> - <tbody> + <div class="col-md-10"> + <table class="table-hover table-condensed table-resp-gracedb shadow p-3 mb-5 rounded"> + <thead> + <tr> + <th colspan="2"><h3>Update GRB Event</h3></th> + </tr> + </thead> + <tbody> - <form id="update_grbevent_form" action="{% url "legacy_apiweb:default:events:update-grbevent" object.graceid %}"> - {{ update_grbevent_form.as_table }} - <tr> - <td></td> - <td><input type="submit" value="Update" disabled></td> - </tr> - </form> - </tbody> - </table> - </div> + <form id="update_grbevent_form" action="{% url "legacy_apiweb:default:events:update-grbevent" object.graceid %}"> + {{ update_grbevent_form.as_table }} + <tr> + <td></td> + <td><input type="submit" value="Update" disabled></td> + </tr> + </form> + </tbody> + </table> + </div> </div> diff --git a/gracedb/templates/gracedb/event_detail_NE.html b/gracedb/templates/gracedb/event_detail_NE.html new file mode 100644 index 000000000..43f57cd0c --- /dev/null +++ b/gracedb/templates/gracedb/event_detail_NE.html @@ -0,0 +1,165 @@ +{% extends "gracedb/event_detail.html" %} +{% load timeutil %} +{% load scientific %} + +{% block basic_info %} +<div class="row my-3 justify-content-center"> + <div class="col-md-10"> + <div id="gdb-table-basic-ne"> + <table class="table-hover table-condensed table-resp-gracedb shadow p-3 mb-5 rounded"> + <thead> + <tr> + <th colspan="8"><h3>Basic Event Information</h3></th> + </tr> + </thead> + <tbody> + + <tr> + <td>UID</td> + <td data-title="UID">{{ object.graceid }}</td> + </tr> + <tr> + <td>Labels</td> + <td data-title="Labels"> + {% if object.labelling_set.all %} + {% for labelling in object.labelling_set.all %} + <span style="color: {{labelling.label.defaultColor}};" class="label" + data-toggle="tooltip" + data-placement="top" + data-html="true" + title="<em>{{labelling.label.description}}</em><br> + <b>Added by:</b> {{labelling.creator.get_full_name}}<br> + <b>Added:</b> {{labelling.created}}"> + {{ labelling.label.name }}</span> + {% endfor %} + {% else %} + + {% endif %} + </td> + </tr> + <tr> + <td>Group</td> + <td data-title="Group">{{ object.group.name }} </td> + </tr> + <tr> + <td>Pipeline</td> + <td data-title="Pipeline">{{ object.pipeline.name }} </td> + </tr> + <tr> + <td>Search</td> + <td data-title="Search">{{ object.search.name }} </td> + </tr> + <tr> + <td> + {{ "gps"|timeselect:"gps" }} + </td> + <td data-title="Event Time">{% if object.gpstime%} + <!-- <span title="{{ object.gpstime|gpsdate }}">{{ object.gpstime }}</span> --> + {{ object.gpstime|multiTime:"gps" }} + {% endif %}</td> + </tr> + {% if object.far %} + <tr> + <td>FAR (Hz)</td> + <td data-title="FAR">{{ object.far | scientific }} </td> + </tr> + {% endif %} + <tr> + <td>Latency (s)</td> + <td>{{ object.reporting_latency|floatformat:"-3" }} </td> + </tr> + <tr> + <td>Links</td> + <td data-title="Links"><a href="{{ object.weburl }}">Data</a></td> + </tr> + <tr> + <td> + {{ "submitted"|timeselect:"utc" }} + </td> + <td data-title="Submitted">{{ object.created|multiTime:"submitted" }}</td> + </tr> + + {% if object.superevent %} + <tr> + <td>Superevent</td> + <td><a href="{{ object.superevent.get_web_url }}">{{ object.superevent.superevent_id }}</a></td> + </tr> + {% endif %} + + </tbody> + + </table> + </div> + </div> +</div> +{% endblock %} + +{% block analysis_specific %} +{% if object.neutrinoevent %} +<div class="row my-3 justify-content-center"> + <div class="col-md-10"> + <div id="ne-info"> + <table class="table-hover table-condensed table-resp-gracedb shadow p-3 mb-5 rounded"> + <thead> + <tr> + <th colspan="2"><h3>Neutrino Observation Info</h3></th> + </tr> + </thead> + <tr><td>IVORN</td><td>{{object.ivorn}}</td></tr> + {% if object.amon_id %} + <tr><td>Alert Identification Number</td><td>{{object.amon_id}}</td></tr> + {% endif %} + {% if object.run_id %} + <tr><td>Run ID</td><td>{{object.run_id}}</td></tr> + {% endif %} + {% if object.event_id %} + <tr><td>Event ID</td><td>{{object.event_id}}</td></tr> + {% endif %} + {% if object.stream %} + <tr><td>IceCube Coincidence Stream ID</td><td>{{object.stream}}</td></tr> + {% endif %} + {% if object.far_ne %} + <tr><td>FAR ({{ object.far_unit }}) </td><td>{{object.far_ne}}</td></tr> + {% endif %} + {% if object.signalness %} + <tr><td>Signalness</td><td>{{object.signalness}}</td></tr> + {% endif %} + {% if object.energy %} + <tr><td>Energy</td><td>{{object.energy}}</td></tr> + {% endif %} + {% if object.src_err_90 %} + <tr><td>Source Angular Error (90%)</td><td>{{object.src_err_90}}</td></tr> + {% endif %} + {% if object.src_err_50 %} + <tr><td>Source Angular Error (50%)</td><td>{{object.src_err_50}}</td></tr> + {% endif %} + </table> + </div> + </div> +</div> + +<div class="row my-3 justify-content-center"> + <div class="col-md-10"> + <div id="ne-loc"> + <table class="table-hover table-condensed table-resp-gracedb shadow p-3 mb-5 rounded"> + <thead> + <tr> + <th colspan="2"><h3>Neutrino Location</h3></th> + </tr> + </thead> + <tbody> + <tr><td>Coord System</td><td>{{object.coord_system}}</td></tr> + <tr><td>RA</td><td>{{object.ra}}</td></tr> + <tr><td>Dec</td><td>{{object.dec}}</td></tr> + <tr><td>Err</td><td>{{object.error_radius}}</td></tr> + </tbody> + </table> + </div> + </div> +</div> + + +{% endif %} + +{% endblock %} + -- GitLab