From 2cbce188b402995d5ac9f47965bc4d069cd01c68 Mon Sep 17 00:00:00 2001
From: Tanner Prestegard <tanner.prestegard@ligo.org>
Date: Mon, 25 Jun 2018 15:05:30 -0500
Subject: [PATCH] Properly handling floats on event creation

Fixing issue where some float fields were left as strings in
LVAlert JSONs and response JSONs.  Occurred because they were
extracted as strings from files and saving the event did not
fix the attribute types without refreshing from the database.
See https://git.ligo.org/lscsoft/gracedb/issues/10.
---
 gracedb/events/translator.py | 64 ++++++++++++++++++++++--------------
 gracedb/events/view_logic.py |  4 +++
 gracedb/events/view_utils.py |  3 ++
 3 files changed, 46 insertions(+), 25 deletions(-)

diff --git a/gracedb/events/translator.py b/gracedb/events/translator.py
index 013bbd142..bdd19d292 100644
--- a/gracedb/events/translator.py
+++ b/gracedb/events/translator.py
@@ -22,6 +22,9 @@ import StringIO
 
 from math import sqrt
 
+import logging
+logger = logging.getLogger(__name__)
+
 use_in(LIGOLWContentHandler)
 
 # This function checks for 'inf' in a float field, asks the database
@@ -397,31 +400,37 @@ def handle_uploaded_data(event, datafilename,
                            comment=error)
             log.save()
     elif pipeline == 'LIB':
+        # lambda function for converting to a type if not None
+        typecast = lambda t, v: t(v) if v is not None else v
+        n_int = lambda v: typecast(int, v)
+        n_float = lambda v: typecast(float, v)
+
+        # Open event file and get data
         event_file = open(datafilename, 'r')
         event_file_contents = event_file.read()
         event_file.close()
         event_dict = json.loads(event_file_contents)
 
         # Extract relevant data from dictionary to put into event record.
-        event.gpstime     = event_dict['gpstime']
-        event.far         = event_dict['FAR']
+        event.gpstime     = n_float(event_dict.get('gpstime'))
+        event.far         = n_float(event_dict.get('FAR'))
         event.instruments = event_dict['instruments']
-        event.nevents     = event_dict.get('nevents', 1)
-        event.likelihood  = event_dict.get('likelihood', None)
+        event.nevents     = n_int(event_dict.get('nevents', 1))
+        event.likelihood  = n_float(event_dict.get('likelihood', None))
 
         # Assign analysis-specific attributes
-        event.bci         = event_dict.get('BCI', None)
-        event.quality_mean = event_dict.get('quality_posterior_mean', None)
-        event.quality_median = event_dict.get('quality_posterior_median', None)
-        event.bsn         = event_dict.get('BSN', None)
-        event.omicron_snr_network = event_dict.get('Omicron_SNR_Network', None)
-        event.omicron_snr_H1 = event_dict.get('Omicron_SNR_H1', None)
-        event.omicron_snr_L1 = event_dict.get('Omicron_SNR_L1', None)
-        event.omicron_snr_V1 = event_dict.get('Omicron_SNR_V1', None)
-        event.hrss_mean   = event_dict.get('hrss_posterior_mean', None)
-        event.hrss_median   = event_dict.get('hrss_posterior_median', None)
-        event.frequency_mean   = event_dict.get('frequency_posterior_mean', None)
-        event.frequency_median = event_dict.get('frequency_posterior_median', None)
+        event.bci         = n_float(event_dict.get('BCI', None))
+        event.quality_mean = n_float(event_dict.get('quality_posterior_mean', None))
+        event.quality_median = n_float(event_dict.get('quality_posterior_median', None))
+        event.bsn         = n_float(event_dict.get('BSN', None))
+        event.omicron_snr_network = n_float(event_dict.get('Omicron_SNR_Network', None))
+        event.omicron_snr_H1 = n_float(event_dict.get('Omicron_SNR_H1', None))
+        event.omicron_snr_L1 = n_float(event_dict.get('Omicron_SNR_L1', None))
+        event.omicron_snr_V1 = n_float(event_dict.get('Omicron_SNR_V1', None))
+        event.hrss_mean   = n_float(event_dict.get('hrss_posterior_mean', None))
+        event.hrss_median   = n_float(event_dict.get('hrss_posterior_median', None))
+        event.frequency_mean   = n_float(event_dict.get('frequency_posterior_mean', None))
+        event.frequency_median = n_float(event_dict.get('frequency_posterior_median', None))
         event.save()
 
     else:
@@ -586,11 +595,16 @@ class CwbData(Translator):
                     pass
                 break
 
+        # lambda function for converting to a type if not None
+        typecast = lambda t, v: t(v) if v is not None else v
+        n_int = lambda v: typecast(int, v)
+        n_float = lambda v: typecast(float, v) 
+
         data = {}
         data['rawdata'] = rawdata
-        data['gpstime']    = rawdata.get('time',[None])[0]
-        data['likelihood'] = rawdata.get('likelihood',[None])[0]
-        data['far']        = rawdata.get('far',[None])[0]
+        data['gpstime']    = n_float(rawdata.get('time',[None])[0])
+        data['likelihood'] = n_float(rawdata.get('likelihood',[None])[0])
+        data['far']        = n_float(rawdata.get('far',[None])[0])
 
 
         # Get ifos and corresponding GPS times.
@@ -614,14 +628,14 @@ class CwbData(Translator):
             data['start_time_ns'] = None
 
         data['ifo'] = ','.join(ifos)
-        data['duration']      = rawdata.get('duration',[None])[0]
-        data['central_freq']  = rawdata.get('frequency',[None])[0]
-        data['bandwidth']     = rawdata.get('bandwidth',[None])[0]
+        data['duration']      = n_float(rawdata.get('duration',[None])[0])
+        data['central_freq']  = n_float(rawdata.get('frequency',[None])[0])
+        data['bandwidth']     = n_float(rawdata.get('bandwidth',[None])[0])
         #data['snr']           = rawdata.get('snr',[None])[0]
         # rho is what log file says is "effective snr"
-        data['snr']           = data['rawdata'].get('rho',[None])[0]
-        data['ligo_axis_ra']     = data['rawdata'].get('phi',[None,None,None])[2]
-        data['ligo_axis_dec']    = data['rawdata'].get('theta',[None,None,None])[2]
+        data['snr']           = n_float(data['rawdata'].get('rho',[None])[0])
+        data['ligo_axis_ra']     = n_float(data['rawdata'].get('phi',[None,None,None])[2])
+        data['ligo_axis_dec']    = n_float(data['rawdata'].get('theta',[None,None,None])[2])
 
         # Check for the links at the end.
         ced_link = None
diff --git a/gracedb/events/view_logic.py b/gracedb/events/view_logic.py
index 1067697d3..945686c01 100644
--- a/gracedb/events/view_logic.py
+++ b/gracedb/events/view_logic.py
@@ -125,6 +125,10 @@ def _createEventFromForm(request, form):
                 file_contents = file_contents)
             warnings += translator_warnings
 
+            # Refresh event from database to ensure attribute types are
+            # properly set.
+            event.refresh_from_db()
+
             # Add labels here - need event to have been saved already
             for label in label_list:
 
diff --git a/gracedb/events/view_utils.py b/gracedb/events/view_utils.py
index 9e721f400..b4272ce32 100644
--- a/gracedb/events/view_utils.py
+++ b/gracedb/events/view_utils.py
@@ -35,6 +35,9 @@ import calendar
 from django.utils import timezone
 from datetime import datetime, timedelta
 
+import logging
+logger = logging.getLogger(__name__)
+
 SERVER_TZ = pytz.timezone(settings.TIME_ZONE)
 def timeToUTC(dt):
     if not dt.tzinfo:
-- 
GitLab