From f78c0e5444a0bf888f03a797dc0a260e5b534ffb Mon Sep 17 00:00:00 2001
From: Tanner Prestegard <tanner.prestegard@ligo.org>
Date: Fri, 28 Sep 2018 08:53:39 -0500
Subject: [PATCH] Updates to new phone alert code

Switch over to new phone alert code (somewhat improved compared
to the old code).  Made a few more updates and bugfixes.
---
 gracedb/alerts/main.py  | 57 +++++++++++++++++++----------------------
 gracedb/alerts/phone.py | 44 +++++++++++--------------------
 2 files changed, 42 insertions(+), 59 deletions(-)

diff --git a/gracedb/alerts/main.py b/gracedb/alerts/main.py
index be64aa04d..29ed51821 100644
--- a/gracedb/alerts/main.py
+++ b/gracedb/alerts/main.py
@@ -17,19 +17,12 @@ from events.permission_utils import is_external
 from events.query import filter_for_labels
 from events.shortcuts import is_event
 from superevents.shortcuts import is_superevent
+from userprofile.models import Contact
+from .phone import issue_phone_alerts
 from .xmpp import issue_xmpp_alerts
 
 # Set up logger
-log = logging.getLogger(__name__)
-
-
-def check_recips(recips_qs):
-    """
-    Make sure only internal users are included. Assumes that the queryset's
-    model has a foreign key to the user object.
-    """
-    LVC_GROUP = Group.objects.get(name=settings.LVC_GROUP)
-    return recips_qs.filter(user__groups=LVC_GROUP)
+logger = logging.getLogger(__name__)
 
 
 def get_alert_recips(event_or_superevent):
@@ -39,18 +32,20 @@ def get_alert_recips(event_or_superevent):
         pass
     elif is_event(event_or_superevent):
         event = event_or_superevent
-        triggers = event.pipeline.trigger_set.filter(labels=None) \
-            .prefetch_related('contacts')
-        email_recips = [c for t in triggers for c in
-            t.contacts.all().select_related('user')
-            if ((not t.farThresh or (event.far and event.far < t.farThresh))
-            and r.email)]
-        phone_recips = [c for t in triggers for c in
-            t.contacts.all().select_related('user')
-            if ((not t.farThresh or (event.far and event.far < t.farThresh))
-            and r.phone)]
-
-    return check_recips(email_recips), check_recips(phone_recips)
+        # Queryset of all triggers for this pipeline
+        triggers = event.pipeline.trigger_set.filter(labels=None)
+        # Filter on FAR threshold requirements
+        query = Q(farThresh__isnull=True)
+        if event.far:
+            query |= Q(farThresh__lt=event.far)
+        triggers = triggers.filter(query)
+        # Contacts for all triggers, make sure user is in LVC group (safeguard)
+        contacts = Contact.objects.filter(trigger__in=triggers,
+            user__groups__name=settings.LVC_GROUP).select_related('user')
+        email_recips = contacts.exclude(email="")
+        phone_recips = contacts.exclude(phone="")
+
+    return email_recips, phone_recips
 
 
 def get_alert_recips_for_label(event_or_superevent, label):
@@ -60,7 +55,8 @@ def get_alert_recips_for_label(event_or_superevent, label):
 
     # Construct a queryset containing only this object; needed for
     # call to filter_for_labels
-    qs = event_or_superevent.model.objects.filter(id=event_or_superevent.id)
+    qs = event_or_superevent._meta.model.objects.filter(
+        pk=event_or_superevent.pk)
 
     # Triggers on given label matching pipeline OR with no pipeline;
     # no pipeline indicates that pipeline is irrelevant
@@ -78,7 +74,7 @@ def get_alert_recips_for_label(event_or_superevent, label):
     # Idea: have filter_for_labels return a Q object generated from the
     #       label query
     triggers = label.trigger_set.filter(query).prefetch_related('contacts')
-    for trigger in triggers.related():
+    for trigger in triggers:
 
         if len(trigger.label_query) > 0:
             qs_out = filter_for_labels(qs, trigger.label_query)
@@ -88,13 +84,14 @@ def get_alert_recips_for_label(event_or_superevent, label):
             if not qs_out.exists():
                 continue
 
-        # Compile a list of recipients from the trigger's contacts
-        email_recips |= trigger.contacts.exclude(email="") \
-            .select_related('user')
-        phone_recips |= trigger.contacts.exclude(phone="") \
-            .select_related('user')
+        # Compile a list of recipients from the trigger's contacts.
+        # Require that the user is in the LVC group as a safeguard
+        contacts = trigger.contacts.filter(user__groups__name=
+            settings.LVC_GROUP)
+        email_recips |= contacts.exclude(email="").select_related('user')
+        phone_recips |= contacts.exclude(phone="").select_related('user')
 
-    return check_recips(email_recips), check_recips(phone_recips)
+    return email_recips, phone_recips
 
 
 def issue_alerts(event_or_superevent, alert_type, serialized_object,
diff --git a/gracedb/alerts/phone.py b/gracedb/alerts/phone.py
index 30ea12230..ddaefd826 100644
--- a/gracedb/alerts/phone.py
+++ b/gracedb/alerts/phone.py
@@ -7,7 +7,7 @@ from django_twilio.client import twilio_client
 from events.permission_utils import is_external
 
 # Set up logger
-log = logging.getLogger(__name__)
+logger = logging.getLogger(__name__)
 
 
 # TODO: generalize to superevents
@@ -75,43 +75,29 @@ def issue_phone_alerts(event, contacts, label=None):
     msg_body = TWILIO_MSG_CONTENT[alert_type].format(**msg_params)
 
     # Loop over recipients and make calls and/or texts.
-    for contact in twilio_recips:
-        if is_external(recip.user):
+    for contact in contacts:
+        if is_external(contact.user):
             # Only make calls to LVC members (non-LVC members
             # shouldn't even be able to sign up for phone alerts,
             # but this is another safety measure.
-            log.warning("External user {0} is somehow signed up for"
-                        " phone alerts".format(recip.user.username))
+            logger.warning("External user {0} is somehow signed up for"
+                        " phone alerts".format(contact.user.username))
             continue
 
         try:
             # POST to TwiML bin to make voice call.
-            if recip.call_phone:
-                log.debug("Calling {0} at {1}".format(recip.user.username,
-                    recip.phone))
-                twilio_client.calls.create(to=recip.phone, from_=from_,
+            if contact.call_phone:
+                logger.debug("Calling {0} at {1}".format(contact.user.username,
+                    contact.phone))
+                twilio_client.calls.create(to=contact.phone, from_=from_,
                     url=twiml_url, method='GET')
 
             # Create Twilio message.
-            if recip.text_phone:
-                log.debug("Texting {0} at {1}".format(recip.user.username,
-                    recip.phone))
-                twilio_client.messages.create(to=recip.phone, from_=from_,
+            if contact.text_phone:
+                logger.debug("Texting {0} at {1}".format(contact.user.username,
+                    contact.phone))
+                twilio_client.messages.create(to=contact.phone, from_=from_,
                     body=msg_body)
         except Exception as e:
-            log.exception("Failed to contact {0} at {1}.".format(
-                recip.user.username, recip.phone))
-
-
-# TODO: update for superevents
-def get_phone_recips(event):
-    triggers = event.pipeline.trigger_set.filter(labels=None) \
-        .prefetch_related('contacts')
-    phone_recips = [c for t in triggers for c in
-        t.contacts.all().select_related('user')
-        if ((not t.farThresh or (event.far and event.far < t.farThresh)) and
-        r.phone)]
-
-    return phone_recips
-
-
+            logger.exception("Failed to contact {0} at {1}.".format(
+                contact.user.username, contact.phone))
-- 
GitLab