From 90452855503c4c6d7e8c9516e98b9568fc251167 Mon Sep 17 00:00:00 2001 From: Tanner Prestegard <tanner.prestegard@ligo.org> Date: Fri, 16 Aug 2019 15:34:03 -0500 Subject: [PATCH] alerts: rework and significantly expand scope of tests --- gracedb/alerts/tests/conftest.py | 243 ++ gracedb/alerts/tests/constants.py | 16 + gracedb/alerts/tests/test_alerts.py | 127 + gracedb/alerts/tests/test_contacts.py | 149 + gracedb/alerts/tests/test_recipients.py | 4198 ++++++++++------------- 5 files changed, 2371 insertions(+), 2362 deletions(-) create mode 100644 gracedb/alerts/tests/conftest.py create mode 100644 gracedb/alerts/tests/constants.py create mode 100644 gracedb/alerts/tests/test_alerts.py create mode 100644 gracedb/alerts/tests/test_contacts.py diff --git a/gracedb/alerts/tests/conftest.py b/gracedb/alerts/tests/conftest.py new file mode 100644 index 000000000..e870491e3 --- /dev/null +++ b/gracedb/alerts/tests/conftest.py @@ -0,0 +1,243 @@ +import copy +import pytest +import re + +from django.contrib.auth import get_user_model + +from alerts.models import Contact, Notification +from events.models import Label, Group, Pipeline, Search, Event +from superevents.models import Superevent +from .constants import ( + DEFAULT_FAR_T, DEFAULT_LABELS, DEFAULT_LABEL_QUERY, LABEL_QUERY2, + RANDOM_LABEL, LABEL_QUERY_PARSER, DEFAULT_GROUP, DEFAULT_PIPELINE, + DEFAULT_SEARCH +) + +UserModel = get_user_model() + + +############################################################################### +# UTILITY FUNCTIONS ########################################################### +############################################################################### +def create_notification( + user, + notification_category, + contact_description='test', + phone=None, + email=None, + phone_method=Contact.CONTACT_PHONE_BOTH, + notification_description='test', + far_threshold=None, + ns_candidate=None, + label_names=None, + label_query=None, + groups=None, + pipelines=None, + searches=None, +): + # Create contact + contact_dict = {} + if phone is not None and email is not None: + raise ValueError("Specify only one of label_names or label_query") + elif phone: + contact_dict['phone'] = phone + contact_dict['phone_method'] = phone_method + elif email: + contact_dict['email'] = email + c = Contact.objects.create( + user=user, + description=contact_description, + verified=True, + **contact_dict + ) + + # Create notification + notification_dict = {} + if far_threshold: + notification_dict['far_threshold'] = far_threshold + if ns_candidate: + notification_dict['ns_candidate'] = ns_candidate + if label_query: + notification_dict['label_query'] = label_query + + n = Notification.objects.create( + user=user, + description=notification_description, + category=notification_category, + **notification_dict + ) + # Add m2m relations + n.contacts.add(c) + if label_names and label_query: + raise ValueError('') + elif label_query: + label_names = LABEL_QUERY_PARSER.findall(label_query) + if label_names: + for l in label_names: + label, _ = Label.objects.get_or_create(name=l) + n.labels.add(label) + if notification_category == Notification.NOTIFICATION_CATEGORY_EVENT: + if groups: + for g in groups: + group, _ = Group.objects.get_or_create(name=g) + n.groups.add(group) + if pipelines: + for p in pipelines: + pipeline, _ = Pipeline.objects.get_or_create(name=p) + n.pipelines.add(pipeline) + if searches: + for s in searches: + search, _ = Search.objects.get_or_create(name=s) + n.searches.add(search) + return n + + +############################################################################### +# FIXTURES #################################################################### +############################################################################### +@pytest.mark.django_db +@pytest.fixture +def event(): + group, _ = Group.objects.get_or_create(name='event_group') + pipeline, _ = Pipeline.objects.get_or_create(name='event_pipeline') + search, _ = Search.objects.get_or_create(name='event_search') + user = UserModel.objects.create(username='event.creator') + event = Event.objects.create(group=group, pipeline=pipeline, search=search, + far=1, submitter=user) + return event + + +@pytest.mark.django_db +@pytest.fixture +def superevent(event): + user = UserModel.objects.create(username='superevent.creator') + superevent = Superevent.objects.create(submitter=user, t_start=0, t_0=1, + t_end=2, preferred_event=event) + return superevent + + +SUPEREVENT_NOTIFICATION_DATA = [ + dict(desc='all'), + dict(desc='far_t_only', far_threshold=DEFAULT_FAR_T), + dict(desc='nscand_only', ns_candidate=True), + dict(desc='labels_only', label_names=DEFAULT_LABELS), + dict(desc='labelq_only', label_query=DEFAULT_LABEL_QUERY['query']), + dict(desc='far_t_and_nscand', far_threshold=DEFAULT_FAR_T, + ns_candidate=True), + dict(desc='far_t_and_labels', far_threshold=DEFAULT_FAR_T, + label_names=DEFAULT_LABELS), + dict(desc='far_t_and_labelq', far_threshold=DEFAULT_FAR_T, + label_query=DEFAULT_LABEL_QUERY['query']), + dict(desc='nscand_and_labels', ns_candidate=True, + label_names=DEFAULT_LABELS), + dict(desc='nscand_and_labelq', ns_candidate=True, + label_query=DEFAULT_LABEL_QUERY['query']), + dict(desc='far_t_and_nscand_and_labels', far_threshold=DEFAULT_FAR_T, + ns_candidate=True, label_names=DEFAULT_LABELS), + dict(desc='far_t_and_nscand_and_labelq', far_threshold=DEFAULT_FAR_T, + ns_candidate=True, label_query=DEFAULT_LABEL_QUERY['query']), + dict(desc='labelq2_only', label_query=LABEL_QUERY2), + dict(desc='far_t_and_labelq2', far_threshold=DEFAULT_FAR_T, + label_query=LABEL_QUERY2), + dict(desc='nscand_and_labelq2', ns_candidate=True, + label_query=LABEL_QUERY2), + dict(desc='far_t_and_nscand_and_labelq2', far_threshold=DEFAULT_FAR_T, + ns_candidate=True, label_query=LABEL_QUERY2), +] +@pytest.mark.django_db +@pytest.fixture +def superevent_notifications(request): + # Get user fixture + user = request.getfixturevalue('internal_user') + + # Create notifications + notification_pks = [] + notification_data = copy.deepcopy(SUPEREVENT_NOTIFICATION_DATA) + for notification_dict in notification_data: + desc = notification_dict.pop('desc') + n = create_notification( + user, + Notification.NOTIFICATION_CATEGORY_SUPEREVENT, + phone='12345678901', + notification_description=desc, + **notification_dict + ) + notification_pks.append(n.pk) + + return Notification.objects.filter(pk__in=notification_pks) + + +EVENT_NOTIFICATION_DATA = copy.deepcopy(SUPEREVENT_NOTIFICATION_DATA) +EVENT_NOTIFICATION_DATA += [ + dict(desc='gps_only', groups=[DEFAULT_GROUP], pipelines=[DEFAULT_PIPELINE], + searches=[DEFAULT_SEARCH]), + dict(desc='far_t_and_gps', far_threshold=DEFAULT_FAR_T, + groups=[DEFAULT_GROUP], pipelines=[DEFAULT_PIPELINE], + searches=[DEFAULT_SEARCH]), + dict(desc='nscand_and_gps', ns_candidate=True, + groups=[DEFAULT_GROUP], pipelines=[DEFAULT_PIPELINE], + searches=[DEFAULT_SEARCH]), + dict(desc='labels_and_gps', label_names=DEFAULT_LABELS, + groups=[DEFAULT_GROUP], pipelines=[DEFAULT_PIPELINE], + searches=[DEFAULT_SEARCH]), + dict(desc='labelq_and_gps', label_query=DEFAULT_LABEL_QUERY['query'], + groups=[DEFAULT_GROUP], pipelines=[DEFAULT_PIPELINE], + searches=[DEFAULT_SEARCH]), + dict(desc='far_t_and_nscand_and_gps', far_threshold=DEFAULT_FAR_T, + ns_candidate=True, groups=[DEFAULT_GROUP], + pipelines=[DEFAULT_PIPELINE], searches=[DEFAULT_SEARCH]), + dict(desc='far_t_and_labels_and_gps', far_threshold=DEFAULT_FAR_T, + label_names=DEFAULT_LABELS, groups=[DEFAULT_GROUP], + pipelines=[DEFAULT_PIPELINE], searches=[DEFAULT_SEARCH]), + dict(desc='far_t_and_labelq_and_gps', far_threshold=DEFAULT_FAR_T, + label_query=DEFAULT_LABEL_QUERY['query'], groups=[DEFAULT_GROUP], + pipelines=[DEFAULT_PIPELINE], searches=[DEFAULT_SEARCH]), + dict(desc='nscand_and_labels_and_gps', ns_candidate=True, + label_names=DEFAULT_LABELS, groups=[DEFAULT_GROUP], + pipelines=[DEFAULT_PIPELINE], searches=[DEFAULT_SEARCH]), + dict(desc='nscand_and_labelq_and_gps', ns_candidate=True, + label_query=DEFAULT_LABEL_QUERY['query'], groups=[DEFAULT_GROUP], + pipelines=[DEFAULT_PIPELINE], searches=[DEFAULT_SEARCH]), + dict(desc='far_t_and_nscand_and_labels_and_gps', + far_threshold=DEFAULT_FAR_T, ns_candidate=True, + label_names=DEFAULT_LABELS, groups=[DEFAULT_GROUP], + pipelines=[DEFAULT_PIPELINE], searches=[DEFAULT_SEARCH]), + dict(desc='far_t_and_nscand_and_labelq_and_gps', + far_threshold=DEFAULT_FAR_T, ns_candidate=True, + label_query=DEFAULT_LABEL_QUERY['query'], groups=[DEFAULT_GROUP], + pipelines=[DEFAULT_PIPELINE], searches=[DEFAULT_SEARCH]), + dict(desc='labelq2_and_gps', label_query=LABEL_QUERY2, + groups=[DEFAULT_GROUP], pipelines=[DEFAULT_PIPELINE], + searches=[DEFAULT_SEARCH]), + dict(desc='far_t_and_labelq2_and_gps', far_threshold=DEFAULT_FAR_T, + label_query=LABEL_QUERY2, groups=[DEFAULT_GROUP], + pipelines=[DEFAULT_PIPELINE], searches=[DEFAULT_SEARCH]), + dict(desc='nscand_and_labelq2_and_gps', ns_candidate=True, + label_query=LABEL_QUERY2, groups=[DEFAULT_GROUP], + pipelines=[DEFAULT_PIPELINE], searches=[DEFAULT_SEARCH]), + dict(desc='far_t_and_nscand_and_labelq2_and_gps', + far_threshold=DEFAULT_FAR_T, ns_candidate=True, + label_query=LABEL_QUERY2, groups=[DEFAULT_GROUP], + pipelines=[DEFAULT_PIPELINE], searches=[DEFAULT_SEARCH]), +] +@pytest.mark.django_db +@pytest.fixture +def event_notifications(request): + # Get user fixture + user = request.getfixturevalue('internal_user') + + # Create notifications + notification_pks = [] + notification_data = copy.deepcopy(EVENT_NOTIFICATION_DATA) + for notification_dict in notification_data: + desc = notification_dict.pop('desc') + n = create_notification( + user, + Notification.NOTIFICATION_CATEGORY_EVENT, + phone='12345678901', + notification_description=desc, + **notification_dict + ) + notification_pks.append(n.pk) + + return Notification.objects.filter(pk__in=notification_pks) diff --git a/gracedb/alerts/tests/constants.py b/gracedb/alerts/tests/constants.py new file mode 100644 index 000000000..89c92017a --- /dev/null +++ b/gracedb/alerts/tests/constants.py @@ -0,0 +1,16 @@ +import re + + +# Constants for use in various tests +DEFAULT_FAR_T = 1 # Hz +DEFAULT_LABELS = ['L1', 'L2'] +LABEL_QUERY_PARSER = re.compile(r'([a-zA-Z0-9]+)') +DEFAULT_LABEL_QUERY = {'query': 'L3 & L4'} +DEFAULT_LABEL_QUERY['labels'] = LABEL_QUERY_PARSER.findall( + DEFAULT_LABEL_QUERY['query'] +) +LABEL_QUERY2 = 'L5 & L6 & ~L7' +RANDOM_LABEL = 'L12345' +DEFAULT_GROUP = 'Group1' +DEFAULT_PIPELINE = 'Pipeline1' +DEFAULT_SEARCH = 'Search1' diff --git a/gracedb/alerts/tests/test_alerts.py b/gracedb/alerts/tests/test_alerts.py new file mode 100644 index 000000000..30096903e --- /dev/null +++ b/gracedb/alerts/tests/test_alerts.py @@ -0,0 +1,127 @@ +import itertools +try: + from unittest import mock +except ImportError: # python < 3 + import mock +import types + +import pytest + +from alerts.main import issue_alerts +from events.models import Event +from superevents.models import Superevent + + +############################################################################### +# FIXTURES #################################################################### +############################################################################### +# Mock recipient getter dict +@pytest.fixture +def mock_rg_dict(): + def _inner(alert_type='new', recips_exist=True): + # Set up mock recipient getter stuff + mock_recipients = mock.MagicMock() + mock_recipients.exists.return_value = recips_exist + mock_rg = mock.MagicMock() + mock_rg.get_recipients.return_value = \ + (mock_recipients, mock_recipients) + mock_rg_class = mock.MagicMock() + mock_rg_class.return_value = mock_rg + mock_rg_dict = {alert_type: mock_rg_class} + return mock_rg_dict + return _inner + + +############################################################################### +# TESTS ####################################################################### +############################################################################### +@pytest.mark.parametrize( + "xmpp,email,phone", + list(itertools.product((True, False), repeat=3)) +) +def test_alert_settings(settings, mock_rg_dict, xmpp, email, phone): + # Set up settings + settings.SEND_XMPP_ALERTS = xmpp + settings.SEND_EMAIL_ALERTS = email + settings.SEND_PHONE_ALERTS = phone + + # Set up mock superevent object + superevent = mock.MagicMock() + superevent.is_test.return_value = False + superevent.is_mdc.return_value = False + preferred_event = mock.MagicMock() + type(preferred_event).offline = mock.PropertyMock(return_value=False) + superevent.preferred_event = preferred_event + + # Call issue_alerts + with mock.patch('alerts.main.issue_xmpp_alerts') as mock_xmpp, \ + mock.patch('alerts.main.issue_email_alerts') as mock_email, \ + mock.patch('alerts.main.issue_phone_alerts') as mock_phone, \ + mock.patch('alerts.main.is_event') as mock_is_event, \ + mock.patch.dict('alerts.main.ALERT_TYPE_RECIPIENT_GETTERS', + mock_rg_dict(), clear=True): + + mock_is_event.return_value = False + issue_alerts(superevent, 'new', None) + + # Check results + if xmpp: + assert mock_xmpp.call_count == 1 + if phone: + assert mock_phone.call_count == 1 + if email: + assert mock_email.call_count == 1 + if not (phone or email): + assert mock_is_event.call_count == 0 + + +@pytest.mark.parametrize( + "is_event,is_mdc,is_test,is_offline", + list(itertools.product((True, False), repeat=4)) +) +def test_no_alerts_for_test_mdc_offline_events_and_superevents( + settings, mock_rg_dict, is_event, is_mdc, is_test, is_offline +): + # Set up settings + settings.SEND_XMPP_ALERTS = False + settings.SEND_EMAIL_ALERTS = True + settings.SEND_PHONE_ALERTS = True + + # Set up mock event/superevent object + # First, set up event + event = mock.MagicMock() + type(event).offline = mock.PropertyMock(return_value=is_offline) + # If we're doing a superevent, then set the event as its preferred_event + if not is_event: + es = mock.MagicMock() + es.preferred_event = event + else: + es = event + # is_mdc and is_test are handled the same way for both events and + # superevents, so we can set it up at this point + es.is_mdc.return_value = is_mdc + es.is_test.return_value = is_test + + # Instantiate mock_rg_dict + mock_rg_dict = mock_rg_dict() + + # Call issue_alerts + with mock.patch('alerts.main.issue_xmpp_alerts') as mock_xmpp, \ + mock.patch('alerts.main.issue_email_alerts') as mock_email, \ + mock.patch('alerts.main.issue_phone_alerts') as mock_phone, \ + mock.patch('alerts.main.is_event') as mock_is_event, \ + mock.patch.dict('alerts.main.ALERT_TYPE_RECIPIENT_GETTERS', + mock_rg_dict, clear=True): + + mock_is_event.return_value = is_event + issue_alerts(es, 'new', None) + + # Check results + assert es.is_mdc.call_count == 1 + assert es.is_test.call_count == int(not es.is_mdc()) + # Whether the recipient_getter class is called or not depends + # finally on whether the event is offline or not + if (not (es.is_mdc() or es.is_test())): + assert mock_rg_dict['new'].call_count == int(not event.offline) + else: + assert mock_rg_dict['new'].call_count == 0 diff --git a/gracedb/alerts/tests/test_contacts.py b/gracedb/alerts/tests/test_contacts.py new file mode 100644 index 000000000..5fd9e5c10 --- /dev/null +++ b/gracedb/alerts/tests/test_contacts.py @@ -0,0 +1,149 @@ +import pytest + +from django.contrib.auth import get_user_model + +from alerts.models import Contact, Notification +from alerts.recipients import CreationRecipientGetter + +UserModel = get_user_model() + + +@pytest.mark.django_db +def test_multiple_contacts(superevent, internal_user): + # Set up contacts and notifications + c1 = Contact.objects.create( + user=internal_user, description='c1', verified=True, + phone='12345678901', phone_method=Contact.CONTACT_PHONE_TEXT, + ) + c2 = Contact.objects.create( + user=internal_user, description='c2', verified=True, + email='test@test.com' + ) + n = Notification.objects.create( + user=internal_user, description='test', + category=Notification.NOTIFICATION_CATEGORY_SUPEREVENT + ) + n.contacts.add(*(c1, c2)) + + # Get recipients + recipient_getter = CreationRecipientGetter(superevent) + email_contacts, phone_contacts = recipient_getter.get_recipients() + + # Check results + assert email_contacts.count() == 1 + assert email_contacts.first().pk == c2.pk + assert phone_contacts.count() == 1 + assert phone_contacts.first().pk == c1.pk + + +@pytest.mark.django_db +def test_duplicate_contacts(superevent, internal_user): + # Set up contacts and notifications + c = Contact.objects.create( + user=internal_user, description='test', verified=True, + phone='12345678901', phone_method=Contact.CONTACT_PHONE_TEXT, + ) + n1 = Notification.objects.create( + user=internal_user, description='n1', + category=Notification.NOTIFICATION_CATEGORY_SUPEREVENT + ) + n2 = Notification.objects.create( + user=internal_user, description='n2', + category=Notification.NOTIFICATION_CATEGORY_SUPEREVENT + ) + n1.contacts.add(c) + n2.contacts.add(c) + + # Get notifications and check results + recipient_getter = CreationRecipientGetter(superevent) + notifications = recipient_getter.get_notifications() + assert notifications.count() == 2 + for pk in (n1.pk, n2.pk): + assert notifications.filter(pk=pk).exists() + + # Get recipients and check results + email_contacts, phone_contacts = recipient_getter.get_recipients() + + # Check results + assert email_contacts.count() == 0 + assert phone_contacts.count() == 1 + assert phone_contacts.first().pk == c.pk + + +@pytest.mark.django_db +def test_contacts_non_internal_user(superevent, internal_user): + # NOTE: this test handles the case where a user with contacts/notifications + # already set up leaves the collboration. + + # Create a non-internal user + external_user = UserModel.objects.create(username='external.user') + + # Set up contacts and notifications + c_internal = Contact.objects.create( + user=internal_user, description='test', verified=True, + phone='12345678901', phone_method=Contact.CONTACT_PHONE_TEXT, + ) + c_external = Contact.objects.create( + user=external_user, description='test', verified=True, + phone='12345678901', phone_method=Contact.CONTACT_PHONE_TEXT, + ) + n_internal = Notification.objects.create( + user=internal_user, description='internal', + category=Notification.NOTIFICATION_CATEGORY_SUPEREVENT + ) + n_internal.contacts.add(c_internal) + n_external = Notification.objects.create( + user=external_user, description='external', + category=Notification.NOTIFICATION_CATEGORY_SUPEREVENT + ) + n_external.contacts.add(c_external) + + # Get notifications and check results + recipient_getter = CreationRecipientGetter(superevent) + notifications = recipient_getter.get_notifications() + assert notifications.count() == 2 + for pk in (n_internal.pk, n_external.pk): + assert notifications.filter(pk=pk).exists() + + # Get recipients and check results + email_contacts, phone_contacts = recipient_getter.get_recipients() + + # Check results + assert email_contacts.count() == 0 + assert phone_contacts.count() == 1 + assert phone_contacts.first().pk == c_internal.pk + + +@pytest.mark.django_db +def test_unverified_contact(superevent, internal_user): + # NOTE: this test handles the case where a user with contacts/notifications + # already set up leaves the collboration. + + # Set up contacts and notifications + c_verified = Contact.objects.create( + user=internal_user, description='test', verified=True, + phone='12345678901', phone_method=Contact.CONTACT_PHONE_TEXT, + ) + c_unverified = Contact.objects.create( + user=internal_user, description='test', verified=False, + phone='12345678901', phone_method=Contact.CONTACT_PHONE_TEXT, + ) + n = Notification.objects.create( + user=internal_user, description='internal', + category=Notification.NOTIFICATION_CATEGORY_SUPEREVENT + ) + n.contacts.add(*(c_verified, c_unverified)) + + # Get notifications and check results + recipient_getter = CreationRecipientGetter(superevent) + notifications = recipient_getter.get_notifications() + assert notifications.count() == 1 + assert notifications.first().pk == n.pk + + # Get recipients and check results + email_contacts, phone_contacts = recipient_getter.get_recipients() + + # Check results + assert email_contacts.count() == 0 + assert phone_contacts.count() == 1 + assert phone_contacts.first().pk == c_verified.pk diff --git a/gracedb/alerts/tests/test_recipients.py b/gracedb/alerts/tests/test_recipients.py index 22b83dbcd..0130d853a 100644 --- a/gracedb/alerts/tests/test_recipients.py +++ b/gracedb/alerts/tests/test_recipients.py @@ -1,2368 +1,1842 @@ -import mock - -from django.test import override_settings - -from alerts.issuers.events import EventAlertIssuer, EventLabelAlertIssuer -from alerts.issuers.superevents import ( - SupereventAlertIssuer, SupereventLabelAlertIssuer, +try: + from unittest import mock +except ImportError: # python < 3 + import mock +import pytest +import types + +from alerts.models import Notification +from alerts.recipients import ( + CreationRecipientGetter, UpdateRecipientGetter, + LabelAddedRecipientGetter, LabelRemovedRecipientGetter, ) -from alerts.models import Contact, Notification -from core.tests.utils import GraceDbTestBase from events.models import Label, Group, Pipeline, Search -from events.tests.mixins import EventCreateMixin -from superevents.tests.mixins import SupereventCreateMixin - - -@override_settings( - SEND_XMPP_ALERTS=False, - SEND_EMAIL_ALERTS=True, - SEND_PHONE_ALERTS=True, +from .constants import ( + DEFAULT_FAR_T, DEFAULT_LABELS, DEFAULT_LABEL_QUERY, LABEL_QUERY2, + RANDOM_LABEL, DEFAULT_GROUP, DEFAULT_PIPELINE, DEFAULT_SEARCH ) -@mock.patch('alerts.main.issue_phone_alerts') -@mock.patch('alerts.main.issue_email_alerts') -class TestEventRecipients(GraceDbTestBase, EventCreateMixin): - - @classmethod - def setUpTestData(cls): - super(TestEventRecipients, cls).setUpTestData() - - # Create an event - cls.event = cls.create_event('fake_group', 'fake_pipeline', - 'fake_search', user=cls.internal_user) - - # Create a bunch of notifications - cls.far_thresh = 0.01 - cls.notification_dict = {} - cls.notification_dict['basic'] = Notification.objects.create( - user=cls.internal_user, description='basic', - category=Notification.NOTIFICATION_CATEGORY_EVENT) - cls.notification_dict['far'] = Notification.objects.create( - user=cls.internal_user, description='far', - far_threshold=cls.far_thresh, - category=Notification.NOTIFICATION_CATEGORY_EVENT) - cls.notification_dict['nscand'] = Notification.objects.create( - user=cls.internal_user, description='nscand', - ns_candidate=True, - category=Notification.NOTIFICATION_CATEGORY_EVENT) - cls.notification_dict['far_nscand'] = Notification.objects.create( - user=cls.internal_user, description='far_nscand', - far_threshold=cls.far_thresh, ns_candidate=True, - category=Notification.NOTIFICATION_CATEGORY_EVENT) - cls.notification_dict['labels'] = Notification.objects.create( - user=cls.internal_user, description='labels', - category=Notification.NOTIFICATION_CATEGORY_EVENT) - cls.notification_dict['far_labels'] = Notification.objects.create( - user=cls.internal_user, description='far_labels', - far_threshold=cls.far_thresh, - category=Notification.NOTIFICATION_CATEGORY_EVENT) - cls.notification_dict['nscand_labels'] = Notification.objects.create( - user=cls.internal_user, description='nscand_labels', - ns_candidate=True, - category=Notification.NOTIFICATION_CATEGORY_EVENT) - cls.notification_dict['far_nscand_labels'] = \ - Notification.objects.create(user=cls.internal_user, - description='far_nscand_labels', far_threshold=cls.far_thresh, - ns_candidate=True, - category=Notification.NOTIFICATION_CATEGORY_EVENT) - cls.notification_dict['labelq'] = Notification.objects.create( - label_query='TEST_LABEL3 & ~TEST_LABEL4', - user=cls.internal_user, description='labelq', - category=Notification.NOTIFICATION_CATEGORY_EVENT) - cls.notification_dict['far_labelq'] = Notification.objects.create( - user=cls.internal_user, description='far_labelq', - far_threshold=cls.far_thresh, - label_query='TEST_LABEL3 & ~TEST_LABEL4', - category=Notification.NOTIFICATION_CATEGORY_EVENT) - cls.notification_dict['nscand_labelq'] = Notification.objects.create( - user=cls.internal_user, description='nscand_labelq', - ns_candidate=True, label_query='TEST_LABEL3 & ~TEST_LABEL4', - category=Notification.NOTIFICATION_CATEGORY_EVENT) - cls.notification_dict['far_nscand_labelq'] = \ - Notification.objects.create(user=cls.internal_user, - description='far_nscand_labelq', far_threshold=cls.far_thresh, - ns_candidate=True, label_query='TEST_LABEL3 & ~TEST_LABEL4', - category=Notification.NOTIFICATION_CATEGORY_EVENT) - - # Group-pipeline-search notification - group, _ = Group.objects.get_or_create(name='TEST_GROUP') - pipeline, _ = Pipeline.objects.get_or_create(name='TEST_PIPELINE') - search, _ = Search.objects.get_or_create(name='TEST_SEARCH') - cls.notification_dict['gps'] = Notification.objects.create( - user=cls.internal_user, description='gps', - category=Notification.NOTIFICATION_CATEGORY_EVENT) - cls.notification_dict['gps'].groups.add(group) - cls.notification_dict['gps'].pipelines.add(pipeline) - cls.notification_dict['gps'].searches.add(search) - - # Add label stuff - cls.label1, _ = Label.objects.get_or_create(name='TEST_LABEL1') - cls.label2, _ = Label.objects.get_or_create(name='TEST_LABEL2') - cls.label3, _ = Label.objects.get_or_create(name='TEST_LABEL3') - cls.label4, _ = Label.objects.get_or_create(name='TEST_LABEL4') - for k in cls.notification_dict: - if 'labels' in k: - cls.notification_dict[k].labels.add(cls.label1) - cls.notification_dict[k].labels.add(cls.label2) - elif 'labelq' in k: - cls.notification_dict[k].labels.add(cls.label3) - cls.notification_dict[k].labels.add(cls.label4) - - # Create an email and phone contact for each notification - for k in cls.notification_dict: - n = cls.notification_dict[k] - n.contacts.create(user=cls.internal_user, - description=n.description, email='test@test.com', - verified=True) - n.contacts.create(user=cls.internal_user, - description=n.description, phone='12345678901', - phone_method=Contact.CONTACT_PHONE_BOTH, verified=True) - - def test_new(self, email_mock, phone_mock): - """Test alerts for event creation - no FAR, no NSCAND""" - EventAlertIssuer(self.event, alert_type='new').issue_alerts() - - # Check recipients passed to alert functions - email_recips = email_mock.call_args[0][2] - phone_recips = phone_mock.call_args[0][2] - - # Should just be the "basic" notification being triggered - self.assertEqual(email_recips.count(), 1) - self.assertEqual(phone_recips.count(), 1) - self.assertEqual(email_recips.first().description, 'basic') - self.assertEqual(phone_recips.first().description, 'basic') - - def test_new_with_far(self, email_mock, phone_mock): - """Test alerts for event creation with FAR""" - # Add FAR to event - self.event.far = 1e-10 - self.event.save() - - # Create a new notification with too low of a FAR threshold - n = Notification.objects.create(user=self.internal_user, - description='far_low', far_threshold=1e-20, - category=Notification.NOTIFICATION_CATEGORY_EVENT) - n.contacts.create(user=self.internal_user, - description=n.description, email='test@test.com', - verified=True) - n.contacts.create(user=self.internal_user, - description=n.description, phone='12345678901', - phone_method=Contact.CONTACT_PHONE_BOTH, verified=True) - - # Issue alerts - EventAlertIssuer(self.event, alert_type='new').issue_alerts() - - # Check recipients passed to alert functions - email_recips = email_mock.call_args[0][2] - phone_recips = phone_mock.call_args[0][2] - - # Basic and FAR alerts should be triggered, but not the "new" FAR - # one we defined with a really low threshold - for recips in [email_recips, phone_recips]: - self.assertEqual(recips.count(), 2) - for r in recips: - self.assertIn(r.description, ['basic', 'far']) - - # Ensure that "new" FAR alert is not in the lists - self.assertFalse(email_recips.filter( - description=n.description).exists()) - self.assertFalse(phone_recips.filter( - description=n.description).exists()) - - - def test_new_with_nscand(self, email_mock, phone_mock): - """Test alerts for event creation with NS candidate""" - # Add NSCAND to event - self.event.singleinspiral_set.create(mass1=3, mass2=1) - - # Issue alerts - EventAlertIssuer(self.event, alert_type='new').issue_alerts() - - # Check recipients passed to alert functions - email_recips = email_mock.call_args[0][2] - phone_recips = phone_mock.call_args[0][2] - - # Only basic and NSCAND alerts should be triggered - for recips in [email_recips, phone_recips]: - self.assertEqual(recips.count(), 2) - for r in recips: - self.assertIn(r.description, ['basic', 'nscand']) - - def test_new_with_far_nscand(self, email_mock, phone_mock): - """Test alerts for event creation with FAR and NS candidate""" - # Add FAR to event - self.event.far = 1e-10 - self.event.save() - # Add NSCAND to event - self.event.singleinspiral_set.create(mass1=3, mass2=1) - - # Create a new notification with too low of a FAR threshold - n = Notification.objects.create(user=self.internal_user, - description='far_low', far_threshold=1e-20, - category=Notification.NOTIFICATION_CATEGORY_EVENT) - n.contacts.create(user=self.internal_user, - description=n.description, email='test@test.com', - verified=True) - n.contacts.create(user=self.internal_user, - description=n.description, phone='12345678901', - phone_method=Contact.CONTACT_PHONE_BOTH, verified=True) - - # Issue alerts - EventAlertIssuer(self.event, alert_type='new').issue_alerts() - - # Check recipients passed to alert functions - email_recips = email_mock.call_args[0][2] - phone_recips = phone_mock.call_args[0][2] - - # Basic and FAR alerts should be triggered, but not the "new" FAR - # one we defined with a really low threshold - for recips in [email_recips, phone_recips]: - self.assertEqual(recips.count(), 4) - for r in recips: - self.assertIn(r.description, - ['basic', 'far', 'nscand', 'far_nscand']) - - # Ensure that "new" FAR alert is not in the lists - self.assertFalse(email_recips.filter( - description=n.description).exists()) - self.assertFalse(phone_recips.filter( - description=n.description).exists()) - - def test_new_with_group_pipeline_search(self, email_mock, phone_mock): - """Test alerts for event creation which match group-pipeline-search""" - # Change event group, pipeline, search - self.event.group = self.notification_dict['gps'].groups.first() - self.event.pipeline = self.notification_dict['gps'].pipelines.first() - self.event.search = self.notification_dict['gps'].searches.first() - - # Issue alerts - EventAlertIssuer(self.event, alert_type='new').issue_alerts() - - # Check recipients passed to alert functions - email_recips = email_mock.call_args[0][2] - phone_recips = phone_mock.call_args[0][2] - - # Basic alert and 'gps' alert (which matches group-pipeline-search) - # should be triggered - for recips in [email_recips, phone_recips]: - self.assertEqual(recips.count(), 2) - for r in recips: - self.assertIn(r.description, ['basic', 'gps']) - - def test_update_with_no_change(self, email_mock, phone_mock): - """Test alerts for event update with no FAR or NSCAND change""" - # Issue alerts - EventAlertIssuer(self.event, alert_type='update').issue_alerts() - - # In this case, no recipients should match so the alert functions - # are not even called - email_mock.assert_not_called() - phone_mock.assert_not_called() - - def test_update_with_same_far(self, email_mock, phone_mock): - """Test alerts for event update with no FAR or NSCAND change""" - # Add FAR to event - self.event.far = 1e-10 - self.event.save() - - # Issue alerts - EventAlertIssuer(self.event, alert_type='update').issue_alerts( - old_far=self.event.far) - - # In this case, no recipients should match so the alert functions - # are not even called - email_mock.assert_not_called() - phone_mock.assert_not_called() - - def test_update_with_lower_far(self, email_mock, phone_mock): - """Test alerts for event update with lower FAR""" - # Add FAR to event - self.event.far = 1e-10 - self.event.save() - - # Create a new notification with too low of a FAR threshold - n_low = Notification.objects.create(user=self.internal_user, - description='far_low', far_threshold=1e-20, - category=Notification.NOTIFICATION_CATEGORY_EVENT) - n_low.contacts.create(user=self.internal_user, - description=n_low.description, email='test@test.com', - verified=True) - n_low.contacts.create(user=self.internal_user, - description=n_low.description, phone='12345678901', - phone_method=Contact.CONTACT_PHONE_BOTH, verified=True) - - # Create a new notification with too high of a FAR threshold - n_high = Notification.objects.create(user=self.internal_user, - description='far_high', far_threshold=1, - category=Notification.NOTIFICATION_CATEGORY_EVENT) - n_high.contacts.create(user=self.internal_user, - description=n_high.description, email='test@test.com', - verified=True) - n_high.contacts.create(user=self.internal_user, - description=n_high.description, phone='12345678901', - phone_method=Contact.CONTACT_PHONE_BOTH, verified=True) - - # Issue alerts - EventAlertIssuer(self.event, alert_type='update').issue_alerts( - old_far=self.far_thresh*2) - - # Check recipients passed to alert functions - email_recips = email_mock.call_args[0][2] - phone_recips = phone_mock.call_args[0][2] - - # Only the original 'far' alert should be triggered and not the - # n_low or n_high that we defined here - self.assertEqual(email_recips.count(), 1) - self.assertEqual(phone_recips.count(), 1) - self.assertEqual(email_recips.first().description, 'far') - self.assertEqual(phone_recips.first().description, 'far') - - def test_update_with_nscand(self, email_mock, phone_mock): - """Test alerts for event update with NSCAND trigger""" - # Add NSCAND to event - self.event.singleinspiral_set.create(mass1=3, mass2=1) - - # Issue alerts - EventAlertIssuer(self.event, alert_type='update').issue_alerts( - old_nscand=False) - - # Check recipients passed to alert functions - email_recips = email_mock.call_args[0][2] - phone_recips = phone_mock.call_args[0][2] - - # Only the original 'nscand' alert should be triggered - self.assertEqual(email_recips.count(), 1) - self.assertEqual(phone_recips.count(), 1) - self.assertEqual(email_recips.first().description, 'nscand') - self.assertEqual(phone_recips.first().description, 'nscand') - - def test_update_with_far_no_prev_far(self, email_mock, phone_mock): - """Test alerts for event update with new FAR, no previous FAR""" - # Add FAR to event - self.event.far = 1e-10 - self.event.save() - - # Create a new notification with too low of a FAR threshold - n_low = Notification.objects.create(user=self.internal_user, - description='far_low', far_threshold=1e-20, - category=Notification.NOTIFICATION_CATEGORY_EVENT) - n_low.contacts.create(user=self.internal_user, - description=n_low.description, email='test@test.com', - verified=True) - n_low.contacts.create(user=self.internal_user, - description=n_low.description, phone='12345678901', - phone_method=Contact.CONTACT_PHONE_BOTH, verified=True) - - # Create a new notification with too high of a FAR threshold - n_high = Notification.objects.create(user=self.internal_user, - description='far_high', far_threshold=1, - category=Notification.NOTIFICATION_CATEGORY_EVENT) - n_high.contacts.create(user=self.internal_user, - description=n_high.description, email='test@test.com', - verified=True) - n_high.contacts.create(user=self.internal_user, - description=n_high.description, phone='12345678901', - phone_method=Contact.CONTACT_PHONE_BOTH, verified=True) - - # Issue alerts - EventAlertIssuer(self.event, alert_type='update').issue_alerts( - old_far=None) - - # Check recipients passed to alert functions - email_recips = email_mock.call_args[0][2] - phone_recips = phone_mock.call_args[0][2] - - # Only the original 'far' alert and 'far_high' should be in here - for recips in [email_recips, phone_recips]: - self.assertEqual(recips.count(), 2) - for r in recips: - self.assertIn(r.description, ['far', 'far_high']) - - def test_update_with_far_no_old_far_passed(self, email_mock, - phone_mock): - """Test alerts for event update with new FAR, no old FAR passed""" - # Add FAR to event - self.event.far = 1e-10 - self.event.save() - - # Create a new notification with too low of a FAR threshold - n_low = Notification.objects.create(user=self.internal_user, - description='far_low', far_threshold=1e-20, - category=Notification.NOTIFICATION_CATEGORY_EVENT) - n_low.contacts.create(user=self.internal_user, - description=n_low.description, email='test@test.com', - verified=True) - n_low.contacts.create(user=self.internal_user, - description=n_low.description, phone='12345678901', - phone_method=Contact.CONTACT_PHONE_BOTH, verified=True) - - # Create a new notification with too high of a FAR threshold - n_high = Notification.objects.create(user=self.internal_user, - description='far_high', far_threshold=1, - category=Notification.NOTIFICATION_CATEGORY_EVENT) - n_high.contacts.create(user=self.internal_user, - description=n_high.description, email='test@test.com', - verified=True) - n_high.contacts.create(user=self.internal_user, - description=n_high.description, phone='12345678901', - phone_method=Contact.CONTACT_PHONE_BOTH, verified=True) - - # Issue alerts - EventAlertIssuer(self.event, alert_type='update').issue_alerts() - - # Check recipients passed to alert functions - email_recips = email_mock.call_args[0][2] - phone_recips = phone_mock.call_args[0][2] - - # Only the original 'far' alert and 'far_high' should be in here - for recips in [email_recips, phone_recips]: - self.assertEqual(recips.count(), 2) - for r in recips: - self.assertIn(r.description, ['far', 'far_high']) - - def test_update_with_nscand_still_true(self, email_mock, phone_mock): - """Test alerts for event update where NSCAND was and is true""" - # Add NSCAND to event - self.event.singleinspiral_set.create(mass1=3, mass2=1) - - # Issue alerts - EventAlertIssuer(self.event, alert_type='update').issue_alerts( - old_nscand=True) - - # In this case, no recipients should match so the alert functions - # are not even called - email_mock.assert_not_called() - phone_mock.assert_not_called() - - def test_update_with_lower_far_nscand(self, email_mock, phone_mock): - """Test alerts for event update with lower FAR and NSCAND""" - # Add FAR to event - self.event.far = 1e-10 - self.event.save() - # Add NSCAND to event - self.event.singleinspiral_set.create(mass1=3, mass2=1) - - # Create a new notification with too low of a FAR threshold - n_low = Notification.objects.create(user=self.internal_user, - description='far_low', far_threshold=1e-20, - category=Notification.NOTIFICATION_CATEGORY_EVENT) - n_low.contacts.create(user=self.internal_user, - description=n_low.description, email='test@test.com', - verified=True) - n_low.contacts.create(user=self.internal_user, - description=n_low.description, phone='12345678901', - phone_method=Contact.CONTACT_PHONE_BOTH, verified=True) - - # Create a new notification with too high of a FAR threshold - n_high = Notification.objects.create(user=self.internal_user, - description='far_high', far_threshold=1, - category=Notification.NOTIFICATION_CATEGORY_EVENT) - n_high.contacts.create(user=self.internal_user, - description=n_high.description, email='test@test.com', - verified=True) - n_high.contacts.create(user=self.internal_user, - description=n_high.description, phone='12345678901', - phone_method=Contact.CONTACT_PHONE_BOTH, verified=True) - - # Issue alerts - EventAlertIssuer(self.event, alert_type='update').issue_alerts( - old_far=self.far_thresh*2, old_nscand=False) - - # Check recipients passed to alert functions - email_recips = email_mock.call_args[0][2] - phone_recips = phone_mock.call_args[0][2] - - # Only the original 'far' alert should be triggered and not the - # n_low or n_high that we defined here - for recips in [email_recips, phone_recips]: - self.assertEqual(recips.count(), 3) - for r in recips: - self.assertIn(r.description, ['far', 'nscand', 'far_nscand']) - - def test_labeled_update_with_lower_far(self, email_mock, phone_mock): - """ - Test alerts for an event which has labels and updated with lower FAR - """ - # Add FAR to event - self.event.far = 1e-10 - self.event.save() - - # Add label1 to event - not enough to match labels yet - self.event.labelling_set.create(label=self.label1, - creator=self.internal_user) - - # Issue alerts - EventAlertIssuer(self.event, alert_type='update').issue_alerts( - old_far=self.far_thresh*2) - - # Check recipients passed to alert functions - email_recips = email_mock.call_args[0][2] - phone_recips = phone_mock.call_args[0][2] - - # 'far' with no label requirements should be triggered - for recips in [email_recips, phone_recips]: - self.assertEqual(recips.count(), 1) - for r in recips: - self.assertIn(r.description, ['far']) - - # Add label2 to event - should match now - self.event.labelling_set.create(label=self.label2, - creator=self.internal_user) - - # Issue alerts - EventAlertIssuer(self.event, alert_type='update').issue_alerts( - old_far=self.far_thresh*2) - - # Check recipients passed to alert functions - email_recips = email_mock.call_args[0][2] - phone_recips = phone_mock.call_args[0][2] - - # 'far' with no label requirements and 'far_labels' should be triggered - for recips in [email_recips, phone_recips]: - self.assertEqual(recips.count(), 2) - for r in recips: - self.assertIn(r.description, ['far', 'far_labels']) - - def test_labelq_update_with_lower_far(self, email_mock, phone_mock): - """Test alerts for event update with lower FAR and label_query match""" - # Add FAR to event - self.event.far = 1e-10 - self.event.save() - - # Add labels to event - this set of labels shouldn't match label query - self.event.labelling_set.create(label=self.label3, - creator=self.internal_user) - self.event.labelling_set.create(label=self.label4, - creator=self.internal_user) - - # Issue alerts - EventAlertIssuer(self.event, alert_type='update').issue_alerts( - old_far=self.far_thresh*2) - - # Check recipients passed to alert functions - email_recips = email_mock.call_args[0][2] - phone_recips = phone_mock.call_args[0][2] - - # 'far' with no label requirements should be triggered - for recips in [email_recips, phone_recips]: - self.assertEqual(recips.count(), 1) - for r in recips: - self.assertIn(r.description, ['far']) - - # Remove label4 and the label query should match - self.event.labelling_set.get(label=self.label4).delete() - - # Issue alerts - EventAlertIssuer(self.event, alert_type='update').issue_alerts( - old_far=self.far_thresh*2) - - # Check recipients passed to alert functions - email_recips = email_mock.call_args[0][2] - phone_recips = phone_mock.call_args[0][2] - - # 'far' with no label requirements should be triggered - # 'far_labelq' (with label query) should now be triggered - for recips in [email_recips, phone_recips]: - self.assertEqual(recips.count(), 2) - for r in recips: - self.assertIn(r.description, ['far', 'far_labelq']) - - def test_update_match_group_pipeline_search(self, email_mock, phone_mock): - """Test alerts for event update which match group-pipeline-search""" - # Change event group, pipeline, search - self.event.group = self.notification_dict['gps'].groups.first() - self.event.pipeline = self.notification_dict['gps'].pipelines.first() - self.event.search = self.notification_dict['gps'].searches.first() - - # Change GPS notification to have a far_threshold - self.notification_dict['gps'].far_threshold = self.far_thresh - - # Add FAR to event - self.event.far = 1e-10 - self.event.save() - - # Issue alerts - EventAlertIssuer(self.event, alert_type='update').issue_alerts( - old_far=self.far_thresh*2) - - # Check recipients passed to alert functions - email_recips = email_mock.call_args[0][2] - phone_recips = phone_mock.call_args[0][2] - - # 'far' with no label requirements should be triggered - for recips in [email_recips, phone_recips]: - self.assertEqual(recips.count(), 1) - for r in recips: - self.assertIn(r.description, ['far', 'far_gps']) - - def test_label_added(self, email_mock, phone_mock): - """Test adding label alert for event""" - # Add label1 to event - this shouldn't match any queries - lab1 = self.event.labelling_set.create(label=self.label1, - creator=self.internal_user) - - # Issue alerts - EventLabelAlertIssuer(lab1, alert_type='label_added').issue_alerts() - - # In this case, no recipients should match so the alert functions - # are not even called - email_mock.assert_not_called() - phone_mock.assert_not_called() - - # Add label2 to event - lab2 = self.event.labelling_set.create(label=self.label2, - creator=self.internal_user) - - # Issue alerts - EventLabelAlertIssuer(lab2, alert_type='label_added').issue_alerts() - - # Check recipients passed to alert functions - email_recips = email_mock.call_args[0][2] - phone_recips = phone_mock.call_args[0][2] - - # Only 'labels' trigger should be triggered - for recips in [email_recips, phone_recips]: - self.assertEqual(recips.count(), 1) - for r in recips: - self.assertIn(r.description, ['labels']) - - # Issue alert for label 1 now that it has both labels; should be - # the same result - EventLabelAlertIssuer(lab1, alert_type='label_added').issue_alerts() - - # Check recipients passed to alert functions - email_recips = email_mock.call_args[0][2] - phone_recips = phone_mock.call_args[0][2] - - # Only 'labels' trigger should be triggered - for recips in [email_recips, phone_recips]: - self.assertEqual(recips.count(), 1) - for r in recips: - self.assertIn(r.description, ['labels']) - - def test_label_added_extra_labels(self, email_mock, phone_mock): - """Test adding label alert for event with other labels""" - # Add label 1, 2, and 4 to event - lab1 = self.event.labelling_set.create(label=self.label1, - creator=self.internal_user) - lab4 = self.event.labelling_set.create(label=self.label4, - creator=self.internal_user) - lab2 = self.event.labelling_set.create(label=self.label2, - creator=self.internal_user) - - # Issue alerts for label 2 - EventLabelAlertIssuer(lab2, alert_type='label_added').issue_alerts() - - # Check recipients passed to alert functions - email_recips = email_mock.call_args[0][2] - phone_recips = phone_mock.call_args[0][2] - - # The 'labels' trigger only requires label1 and label2, but it should - # still trigger on label2 addition even though label4 is also present - for recips in [email_recips, phone_recips]: - self.assertEqual(recips.count(), 1) - for r in recips: - self.assertIn(r.description, ['labels']) - - def test_label_added_with_far(self, email_mock, phone_mock): - """Test adding label alert for event with FAR""" - # Set event FAR - self.event.far = 1e-10 - self.event.save() - - # Add label1 and label2 to event - lab1 = self.event.labelling_set.create(label=self.label1, - creator=self.internal_user) - lab2 = self.event.labelling_set.create(label=self.label2, - creator=self.internal_user) - - # Issue alerts - EventLabelAlertIssuer(lab2, alert_type='label_added').issue_alerts() - - # Check recipients passed to alert functions - email_recips = email_mock.call_args[0][2] - phone_recips = phone_mock.call_args[0][2] - - # Basic 'labels' trigger and labels w/ FAR ('far_labels') trigger - # should be triggered - for recips in [email_recips, phone_recips]: - self.assertEqual(recips.count(), 2) - for r in recips: - self.assertIn(r.description, ['labels', 'far_labels']) - - def test_label_added_with_nscand(self, email_mock, phone_mock): - """Test adding label alert for event with NSCAND""" - # Add NSCAND to event - self.event.singleinspiral_set.create(mass1=3, mass2=1) - - # Add label1 and label2 to event - lab1 = self.event.labelling_set.create(label=self.label1, - creator=self.internal_user) - lab2 = self.event.labelling_set.create(label=self.label2, - creator=self.internal_user) - - # Issue alerts - EventLabelAlertIssuer(lab2, alert_type='label_added').issue_alerts() - - # Check recipients passed to alert functions - email_recips = email_mock.call_args[0][2] - phone_recips = phone_mock.call_args[0][2] - - # Basic 'labels' trigger and labels w/ NSCAND ('nscand_labels') trigger - # should be triggered - for recips in [email_recips, phone_recips]: - self.assertEqual(recips.count(), 2) - for r in recips: - self.assertIn(r.description, ['labels', 'nscand_labels']) - - def test_label_added_with_far_nscand(self, email_mock, phone_mock): - """Test adding label alert for event with FAR threshold and NSCAND""" - # Set event FAR - self.event.far = 1e-10 - self.event.save() - - # Add NSCAND to event - self.event.singleinspiral_set.create(mass1=3, mass2=1) - - # Add label1 and label2 to event - lab1 = self.event.labelling_set.create(label=self.label1, - creator=self.internal_user) - lab2 = self.event.labelling_set.create(label=self.label2, - creator=self.internal_user) - - # Issue alerts - EventLabelAlertIssuer(lab2, alert_type='label_added').issue_alerts() - - # Check recipients passed to alert functions - email_recips = email_mock.call_args[0][2] - phone_recips = phone_mock.call_args[0][2] - - # Basic 'labels' trigger, labels with FAR, labels with NSCAND, and - # labels with FAR and NSCAND should all be triggered - # should be triggered - for recips in [email_recips, phone_recips]: - self.assertEqual(recips.count(), 4) - for r in recips: - self.assertIn(r.description, ['labels', 'far_labels', - 'nscand_labels', 'far_nscand_labels']) - - def test_label_added_with_gps(self, email_mock, phone_mock): - """ - Test adding label alert for event with group-pipeline-search - requirements - """ - # Change event group, pipeline, search - self.event.group = self.notification_dict['gps'].groups.first() - self.event.pipeline = self.notification_dict['gps'].pipelines.first() - self.event.search = self.notification_dict['gps'].searches.first() - - # Add label 1 and 2 to notification - self.notification_dict['gps'].labels.add(self.label1) - self.notification_dict['gps'].labels.add(self.label2) - - # Add label 1 and 2 to event - lab1 = self.event.labelling_set.create(label=self.label1, - creator=self.internal_user) - lab2 = self.event.labelling_set.create(label=self.label2, - creator=self.internal_user) - - # Issue alerts - EventLabelAlertIssuer(lab2, alert_type='label_added').issue_alerts() - - # Check recipients passed to alert functions - email_recips = email_mock.call_args[0][2] - phone_recips = phone_mock.call_args[0][2] - - # Basic 'labels' trigger and 'gps' trigger should match - for recips in [email_recips, phone_recips]: - self.assertEqual(recips.count(), 2) - for r in recips: - self.assertIn(r.description, ['labels', 'gps']) - - def test_label_added_labelq(self, email_mock, phone_mock): - """Test adding label alert for event with label query match""" - # Add label3 and label4 to event - lab3 = self.event.labelling_set.create(label=self.label3, - creator=self.internal_user) - lab4 = self.event.labelling_set.create(label=self.label4, - creator=self.internal_user) - - # Issue alerts - EventLabelAlertIssuer(lab4, alert_type='label_added').issue_alerts() - - # In this case, no recipients should match so the alert functions - # are not even called - email_mock.assert_not_called() - phone_mock.assert_not_called() - - # Remove label4 and the label query should match - self.event.labelling_set.get(label=self.label4).delete() - - # Issue alerts - EventLabelAlertIssuer(lab3, alert_type='label_added').issue_alerts() - - # Check recipients passed to alert functions - email_recips = email_mock.call_args[0][2] - phone_recips = phone_mock.call_args[0][2] - - # Basic label_query trigger should be only match - for recips in [email_recips, phone_recips]: - self.assertEqual(recips.count(), 1) - for r in recips: - self.assertIn(r.description, ['labelq']) - - def test_label_added_labelq_with_far(self, email_mock, phone_mock): - """Test adding label alert for event with FAR and label query match""" - # Set event FAR - self.event.far = 1e-10 - self.event.save() - - # Add label3 to event - lab3 = self.event.labelling_set.create(label=self.label3, - creator=self.internal_user) - - # Issue alerts - EventLabelAlertIssuer(lab3, alert_type='label_added').issue_alerts() - - # Check recipients passed to alert functions - email_recips = email_mock.call_args[0][2] - phone_recips = phone_mock.call_args[0][2] - - # Basic 'labelq' trigger and label query w/ FAR ('far_labelq') trigger - # should be triggered - for recips in [email_recips, phone_recips]: - self.assertEqual(recips.count(), 2) - for r in recips: - self.assertIn(r.description, ['labelq', 'far_labelq']) - - def test_label_added_labelq_with_nscand(self, email_mock, phone_mock): - """ - Test adding label alert for event with NSCAND and label query match - """ - # Add NSCAND to event - self.event.singleinspiral_set.create(mass1=3, mass2=1) - - # Add label3 to event - lab3 = self.event.labelling_set.create(label=self.label3, - creator=self.internal_user) - - # Issue alerts - EventLabelAlertIssuer(lab3, alert_type='label_added').issue_alerts() - - # Check recipients passed to alert functions - email_recips = email_mock.call_args[0][2] - phone_recips = phone_mock.call_args[0][2] - - # Basic 'labelq' trigger and label query w/ NSCAND ('nscand_labelq') - # trigger should be triggered - for recips in [email_recips, phone_recips]: - self.assertEqual(recips.count(), 2) - for r in recips: - self.assertIn(r.description, ['labelq', 'nscand_labelq']) - - def test_label_added_labelq_with_far_nscand(self, email_mock, phone_mock): - """ - Test adding label alert for event with FAR threshold and NSCAND and - label query match - """ - # Set event FAR - self.event.far = 1e-10 - self.event.save() - - # Add NSCAND to event - self.event.singleinspiral_set.create(mass1=3, mass2=1) - - # Add label3 to event - lab3 = self.event.labelling_set.create(label=self.label3, - creator=self.internal_user) - - # Issue alerts - EventLabelAlertIssuer(lab3, alert_type='label_added').issue_alerts() - - # Check recipients passed to alert functions - email_recips = email_mock.call_args[0][2] - phone_recips = phone_mock.call_args[0][2] - - # Basic 'labelq' trigger, label query with FAR, label query with - # NSCAND, and label query with FAR and NSCAND should all be triggered - for recips in [email_recips, phone_recips]: - self.assertEqual(recips.count(), 4) - for r in recips: - self.assertIn(r.description, ['labelq', 'far_labelq', - 'nscand_labelq', 'far_nscand_labelq']) - - def test_label_added_labelq_with_gps(self, email_mock, phone_mock): - """ - Test adding label alert for event with group-pipeline-search - requirements and label query match - """ - # Change event group, pipeline, search - self.event.group = self.notification_dict['gps'].groups.first() - self.event.pipeline = self.notification_dict['gps'].pipelines.first() - self.event.search = self.notification_dict['gps'].searches.first() - - # Add label query to notification - self.notification_dict['gps'].label_query = '{l3} & ~{l4}'.format( - l3=self.label3.name, l4=self.label4.name) - self.notification_dict['gps'].labels.add(self.label3) - self.notification_dict['gps'].labels.add(self.label4) - self.notification_dict['gps'].save() - - # Add label3 to event - lab3 = self.event.labelling_set.create(label=self.label3, - creator=self.internal_user) - - # Issue alerts - EventLabelAlertIssuer(lab3, alert_type='label_added').issue_alerts() - - # Check recipients passed to alert functions - email_recips = email_mock.call_args[0][2] - phone_recips = phone_mock.call_args[0][2] - - # Basic 'labelq' trigger and 'gps' trigger should match - for recips in [email_recips, phone_recips]: - self.assertEqual(recips.count(), 2) - for r in recips: - self.assertIn(r.description, ['labelq', 'gps']) - - def test_label_removed_match_labels(self, email_mock, phone_mock): - """ - Test label_removed alert for event where only triggers with - labels, not label queries are matched - """ - # Add labels 1 and 2 - lab1 = self.event.labelling_set.create(label=self.label1, - creator=self.internal_user) - lab2 = self.event.labelling_set.create(label=self.label2, - creator=self.internal_user) - - # Remove label 2 and issue alert for label 2 removal - self.event.labelling_set.get(label=self.label2).delete() - EventLabelAlertIssuer(lab2, alert_type='label_removed').issue_alerts() - - # In this case, no recipients should match so the alert functions - # are not even called - email_mock.assert_not_called() - phone_mock.assert_not_called() - - # Add labels 2 and 3 - lab2 = self.event.labelling_set.create(label=self.label2, - creator=self.internal_user) - lab3 = self.event.labelling_set.create(label=self.label3, - creator=self.internal_user) - - # Remove label 3 and issue alert - self.event.labelling_set.get(label=self.label3).delete() - EventLabelAlertIssuer(lab3, alert_type='label_removed').issue_alerts() - - # Although the event has label1 and label2 and matches the - # 'labels' trigger, this trigger was matched last time either - # label1 or label2 was added, and label3 being removed doesn't - # change that (i.e., label_removed alerts only trigger notifications - # with label queries) - email_mock.assert_not_called() - phone_mock.assert_not_called() - - def test_label_removed(self, email_mock, phone_mock): - """Test label_removed alert for event with label query match""" - # Add labels 3 and 4 - lab3 = self.event.labelling_set.create(label=self.label3, - creator=self.internal_user) - lab4 = self.event.labelling_set.create(label=self.label4, - creator=self.internal_user) - - # Remove label 4 and issue alert for label 4 removal - self.event.labelling_set.get(label=self.label4).delete() - EventLabelAlertIssuer(lab4, alert_type='label_removed').issue_alerts() - - # Check recipients passed to alert functions - email_recips = email_mock.call_args[0][2] - phone_recips = phone_mock.call_args[0][2] - - # Only 'labelq' trigger should be triggered - for recips in [email_recips, phone_recips]: - self.assertEqual(recips.count(), 1) - for r in recips: - self.assertIn(r.description, ['labelq']) - - def test_label_removed_with_far(self, email_mock, phone_mock): - """ - Test label_removed alert for event with label query match and - FAR threshold match""" - # Set event FAR - self.event.far = 1e-10 - self.event.save() - - # Add labels 3 and 4 - lab3 = self.event.labelling_set.create(label=self.label3, - creator=self.internal_user) - lab4 = self.event.labelling_set.create(label=self.label4, - creator=self.internal_user) - - # Remove label 4 and issue alert for label 4 removal - self.event.labelling_set.get(label=self.label4).delete() - EventLabelAlertIssuer(lab4, alert_type='label_removed').issue_alerts() - - # Check recipients passed to alert functions - email_recips = email_mock.call_args[0][2] - phone_recips = phone_mock.call_args[0][2] - - # Only 'labelq' and label_query with FAR should be triggered - for recips in [email_recips, phone_recips]: - self.assertEqual(recips.count(), 2) - for r in recips: - self.assertIn(r.description, ['labelq', 'far_labelq']) - - def test_label_removed_with_nscand(self, email_mock, phone_mock): - """Test label_removed alert with label query match and NSCAND match""" - # Add NSCAND to event - self.event.singleinspiral_set.create(mass1=3, mass2=1) - - # Add labels 3 and 4 - lab3 = self.event.labelling_set.create(label=self.label3, - creator=self.internal_user) - lab4 = self.event.labelling_set.create(label=self.label4, - creator=self.internal_user) - - # Remove label 4 and issue alert for label 4 removal - self.event.labelling_set.get(label=self.label4).delete() - EventLabelAlertIssuer(lab4, alert_type='label_removed').issue_alerts() - - # Check recipients passed to alert functions - email_recips = email_mock.call_args[0][2] - phone_recips = phone_mock.call_args[0][2] - - # Basic label query trigger and label query w/ NSCAND should be - # triggered - for recips in [email_recips, phone_recips]: - self.assertEqual(recips.count(), 2) - for r in recips: - self.assertIn(r.description, ['labelq', 'nscand_labelq']) - - def test_label_removed_with_far_nscand(self, email_mock, phone_mock): - """ - Test label_removed alert for event with FAR threshold and NSCAND - and label query match - """ - # Set event FAR - self.event.far = 1e-10 - self.event.save() - - # Add NSCAND to event - self.event.singleinspiral_set.create(mass1=3, mass2=1) - - # Add labels 3 and 4 - lab3 = self.event.labelling_set.create(label=self.label3, - creator=self.internal_user) - lab4 = self.event.labelling_set.create(label=self.label4, - creator=self.internal_user) - - # Remove label 4 and issue alert for label 4 removal - self.event.labelling_set.get(label=self.label4).delete() - EventLabelAlertIssuer(lab4, alert_type='label_removed').issue_alerts() - - # Check recipients passed to alert functions - email_recips = email_mock.call_args[0][2] - phone_recips = phone_mock.call_args[0][2] - - # Should match basic label query trigger, label query with FAR, - # label query with NSCAND, and label query with FAR and NSCAND - for recips in [email_recips, phone_recips]: - self.assertEqual(recips.count(), 4) - for r in recips: - self.assertIn(r.description, ['labelq', 'far_labelq', - 'nscand_labelq', 'far_nscand_labelq']) - - def test_label_removed_with_gps(self, email_mock, phone_mock): - """ - Test label_removed alert for event with group-pipeline-search - requirements and label query match - """ - # Change event group, pipeline, search - self.event.group = self.notification_dict['gps'].groups.first() - self.event.pipeline = self.notification_dict['gps'].pipelines.first() - self.event.search = self.notification_dict['gps'].searches.first() - - # Add labels 3 and 4 - lab3 = self.event.labelling_set.create(label=self.label3, - creator=self.internal_user) - lab4 = self.event.labelling_set.create(label=self.label4, - creator=self.internal_user) - - # Add label query to 'gps' trigger - self.notification_dict['gps'].label_query = '{l3} & ~{l4}'.format( - l3=self.label3.name, l4=self.label4.name) - self.notification_dict['gps'].labels.add(self.label3) - self.notification_dict['gps'].labels.add(self.label4) - self.notification_dict['gps'].save() - - # Remove label 4 and issue alert for label 4 removal - self.event.labelling_set.get(label=self.label4).delete() - EventLabelAlertIssuer(lab4, alert_type='label_removed').issue_alerts() - - # Check recipients passed to alert functions - email_recips = email_mock.call_args[0][2] - phone_recips = phone_mock.call_args[0][2] - - # Basic 'labels' trigger and 'gps' trigger should match - for recips in [email_recips, phone_recips]: - self.assertEqual(recips.count(), 2) - for r in recips: - self.assertIn(r.description, ['labelq', 'gps']) - - -@override_settings( - SEND_XMPP_ALERTS=False, - SEND_EMAIL_ALERTS=True, - SEND_PHONE_ALERTS=True, + +# NOTE: there are a *LOT* of tests in here. It's definitely overkill. +# I'm trying to test *every* possible situation because users tend +# to get worked up over problems with notifications. +# +# NOTE on debugging: because there are only a few actual test functions, but +# they are highly parametrized, it can be hard to debug failing tests. I +# suggest looking at the test label in the pytest output (i.e., you should +# see something like "notif_descs###", where ### is the test number). Then +# go and edit the dataset used in the parametrize decorator to be just that +# test. Ex: if test_event_update_alerts with notif_descs123 is failing, +# go and edit EVENT_UPDATE_ALERT_DATA -> EVENT_UPDATE_ALERT_DATA[123:124] + + +############################################################################### +# TEST DATA ################################################################### +############################################################################### +SUPEREVENT_CREATION_ALERT_DATA = [ + (None, False, ['all']), + (None, True, ['all', 'nscand_only']), + (DEFAULT_FAR_T*2.0, False, ['all']), + (DEFAULT_FAR_T*2.0, True, ['all', 'nscand_only']), + (DEFAULT_FAR_T/2.0, False, ['all', 'far_t_only']), + (DEFAULT_FAR_T/2.0, True, ['all', 'far_t_only', 'nscand_only', + 'far_t_and_nscand']), +] + +EVENT_CREATION_ALERT_DATA = [ + (None, False, False, ['all']), + (None, False, True, ['all', 'gps_only']), + (None, True, False, ['all', 'nscand_only']), + (None, True, True, ['all', 'nscand_only', 'gps_only', 'nscand_and_gps']), + (DEFAULT_FAR_T*2.0, False, False, ['all']), + (DEFAULT_FAR_T*2.0, False, True, ['all', 'gps_only']), + (DEFAULT_FAR_T*2.0, True, False, ['all', 'nscand_only']), + (DEFAULT_FAR_T*2.0, True, True, ['all', 'nscand_only', 'gps_only', + 'nscand_and_gps']), + (DEFAULT_FAR_T/2.0, False, False, ['all', 'far_t_only']), + (DEFAULT_FAR_T/2.0, False, True, ['all', 'far_t_only', 'gps_only', + 'far_t_and_gps']), + (DEFAULT_FAR_T/2.0, True, False, ['all', 'far_t_only', 'nscand_only', + 'far_t_and_nscand']), + (DEFAULT_FAR_T/2.0, True, True, + ['all', 'far_t_only', 'nscand_only', 'gps_only', 'far_t_and_nscand', + 'far_t_and_gps', 'nscand_and_gps', 'far_t_and_nscand_and_gps']), +] + +SUPEREVENT_UPDATE_ALERT_DATA = [ + # FAR = None and constant ------------------------- + ## NSCAND = constant ------------------------------ + ### No labels ------------------------------------- + (None, None, False, False, None, []), + (None, None, True, True, None, []), + ### With labels ----------------------------------- + (None, None, False, False, DEFAULT_LABELS, []), + (None, None, True, True, DEFAULT_LABELS, []), + ### With labels matching label query -------------- + (None, None, False, False, DEFAULT_LABEL_QUERY['labels'], []), + (None, None, True, True, DEFAULT_LABEL_QUERY['labels'], []), + ## NSCAND = changing ------------------------------ + ### No labels ------------------------------------- + (None, None, False, True, None, ['nscand_only']), + (None, None, True, False, None, []), + ### With labels ----------------------------------- + (None, None, False, True, DEFAULT_LABELS, + ['nscand_only', 'nscand_and_labels']), + (None, None, True, False, DEFAULT_LABELS, []), + ### With labels matching label query -------------- + (None, None, False, True, DEFAULT_LABEL_QUERY['labels'], + ['nscand_only', 'nscand_and_labelq']), + (None, None, True, False, DEFAULT_LABEL_QUERY['labels'], []), + # FAR = above threshold and constant -------------- + ## NSCAND = constant ------------------------------ + ### No labels ------------------------------------- + (DEFAULT_FAR_T*2, DEFAULT_FAR_T*2, False, False, None, []), + (DEFAULT_FAR_T*2, DEFAULT_FAR_T*2, True, True, None, []), + ### With labels ----------------------------------- + (DEFAULT_FAR_T*2, DEFAULT_FAR_T*2, False, False, DEFAULT_LABELS, []), + (DEFAULT_FAR_T*2, DEFAULT_FAR_T*2, True, True, DEFAULT_LABELS, []), + ### With labels matching label query -------------- + (DEFAULT_FAR_T*2, DEFAULT_FAR_T*2, False, False, + DEFAULT_LABEL_QUERY['labels'], []), + (DEFAULT_FAR_T*2, DEFAULT_FAR_T*2, True, True, + DEFAULT_LABEL_QUERY['labels'], []), + ## NSCAND = changing ------------------------------ + ### No labels ------------------------------------- + (DEFAULT_FAR_T*2, DEFAULT_FAR_T*2, False, True, None, ['nscand_only']), + (DEFAULT_FAR_T*2, DEFAULT_FAR_T*2, True, False, None, []), + ### With labels ----------------------------------- + (DEFAULT_FAR_T*2, DEFAULT_FAR_T*2, False, True, DEFAULT_LABELS, + ['nscand_only', 'nscand_and_labels']), + (DEFAULT_FAR_T*2, DEFAULT_FAR_T*2, True, False, DEFAULT_LABELS, []), + ### With labels matching label query -------------- + (DEFAULT_FAR_T*2, DEFAULT_FAR_T*2, False, True, + DEFAULT_LABEL_QUERY['labels'], ['nscand_only', + 'nscand_and_labelq']), + (DEFAULT_FAR_T*2, DEFAULT_FAR_T*2, True, False, + DEFAULT_LABEL_QUERY['labels'], []), + # FAR = above threshold and increases ------------- + ## NSCAND = constant ------------------------------ + ### No labels ------------------------------------- + (DEFAULT_FAR_T*2, DEFAULT_FAR_T*4, False, False, None, []), + (DEFAULT_FAR_T*2, DEFAULT_FAR_T*4, True, True, None, []), + ### With labels ----------------------------------- + (DEFAULT_FAR_T*2, DEFAULT_FAR_T*4, False, False, DEFAULT_LABELS, []), + (DEFAULT_FAR_T*2, DEFAULT_FAR_T*4, True, True, DEFAULT_LABELS, []), + ### With labels matching label query -------------- + (DEFAULT_FAR_T*2, DEFAULT_FAR_T*4, False, False, + DEFAULT_LABEL_QUERY['labels'], []), + (DEFAULT_FAR_T*2, DEFAULT_FAR_T*4, True, True, + DEFAULT_LABEL_QUERY['labels'], []), + ## NSCAND = changing ------------------------------ + ### No labels ------------------------------------- + (DEFAULT_FAR_T*2, DEFAULT_FAR_T*4, False, True, None, ['nscand_only']), + (DEFAULT_FAR_T*2, DEFAULT_FAR_T*4, True, False, None, []), + ### With labels ----------------------------------- + (DEFAULT_FAR_T*2, DEFAULT_FAR_T*4, False, True, DEFAULT_LABELS, + ['nscand_only', 'nscand_and_labels']), + (DEFAULT_FAR_T*2, DEFAULT_FAR_T*4, True, False, DEFAULT_LABELS, []), + ### With labels matching label query -------------- + (DEFAULT_FAR_T*2, DEFAULT_FAR_T*4, False, True, + DEFAULT_LABEL_QUERY['labels'], ['nscand_only', + 'nscand_and_labelq']), + (DEFAULT_FAR_T*2, DEFAULT_FAR_T*4, True, False, + DEFAULT_LABEL_QUERY['labels'], []), + # FAR = below threshold and constant -------------- + ## NSCAND = constant ------------------------------ + ### No labels ------------------------------------- + (DEFAULT_FAR_T/2.0, DEFAULT_FAR_T/2.0, False, False, None, []), + (DEFAULT_FAR_T/2.0, DEFAULT_FAR_T/2.0, True, True, None, []), + ### With labels ----------------------------------- + (DEFAULT_FAR_T/2.0, DEFAULT_FAR_T/2.0, False, False, DEFAULT_LABELS, []), + (DEFAULT_FAR_T/2.0, DEFAULT_FAR_T/2.0, True, True, DEFAULT_LABELS, []), + ### With labels matching label query -------------- + (DEFAULT_FAR_T/2.0, DEFAULT_FAR_T/2.0, False, False, + DEFAULT_LABEL_QUERY['labels'], []), + (DEFAULT_FAR_T/2.0, DEFAULT_FAR_T/2.0, True, True, + DEFAULT_LABEL_QUERY['labels'], []), + ## NSCAND = changing ------------------------------ + ### No labels ------------------------------------- + (DEFAULT_FAR_T/2.0, DEFAULT_FAR_T/2.0, False, True, None, + ['nscand_only', 'far_t_and_nscand']), + (DEFAULT_FAR_T/2.0, DEFAULT_FAR_T/2.0, True, False, None, []), + ### With labels ----------------------------------- + (DEFAULT_FAR_T/2.0, DEFAULT_FAR_T/2.0, False, True, DEFAULT_LABELS, + ['nscand_only', 'far_t_and_nscand', 'nscand_and_labels', + 'far_t_and_nscand_and_labels']), + (DEFAULT_FAR_T/2.0, DEFAULT_FAR_T/2.0, True, False, DEFAULT_LABELS, []), + ### With labels matching label query -------------- + (DEFAULT_FAR_T/2.0, DEFAULT_FAR_T/2.0, False, True, + DEFAULT_LABEL_QUERY['labels'], ['nscand_only', 'far_t_and_nscand', + 'nscand_and_labelq', + 'far_t_and_nscand_and_labelq']), + (DEFAULT_FAR_T/2.0, DEFAULT_FAR_T/2.0, True, False, + DEFAULT_LABEL_QUERY['labels'], []), + # FAR = below threshold and decreases ------------- + ## NSCAND = constant ------------------------------ + ### No labels ------------------------------------- + (DEFAULT_FAR_T/2.0, DEFAULT_FAR_T/4.0, False, False, None, []), + (DEFAULT_FAR_T/2.0, DEFAULT_FAR_T/4.0, True, True, None, []), + ### With labels ----------------------------------- + (DEFAULT_FAR_T/2.0, DEFAULT_FAR_T/4.0, False, False, DEFAULT_LABELS, []), + (DEFAULT_FAR_T/2.0, DEFAULT_FAR_T/4.0, True, True, DEFAULT_LABELS, []), + ### With labels matching label query -------------- + (DEFAULT_FAR_T/2.0, DEFAULT_FAR_T/4.0, False, False, + DEFAULT_LABEL_QUERY['labels'], []), + (DEFAULT_FAR_T/2.0, DEFAULT_FAR_T/4.0, True, True, + DEFAULT_LABEL_QUERY['labels'], []), + ## NSCAND = changing ------------------------------ + ### No labels ------------------------------------- + (DEFAULT_FAR_T/2.0, DEFAULT_FAR_T/4.0, False, True, None, + ['nscand_only', 'far_t_and_nscand']), + (DEFAULT_FAR_T/2.0, DEFAULT_FAR_T/4.0, True, False, None, []), + ### With labels ----------------------------------- + (DEFAULT_FAR_T/2.0, DEFAULT_FAR_T/4.0, False, True, DEFAULT_LABELS, + ['nscand_only', 'far_t_and_nscand', 'nscand_and_labels', + 'far_t_and_nscand_and_labels']), + (DEFAULT_FAR_T/2.0, DEFAULT_FAR_T/4.0, True, False, DEFAULT_LABELS, []), + ### With labels matching label query -------------- + (DEFAULT_FAR_T/2.0, DEFAULT_FAR_T/4.0, False, True, + DEFAULT_LABEL_QUERY['labels'], ['nscand_only', 'far_t_and_nscand', + 'nscand_and_labelq', + 'far_t_and_nscand_and_labelq']), + (DEFAULT_FAR_T/2.0, DEFAULT_FAR_T/4.0, True, False, + DEFAULT_LABEL_QUERY['labels'], []), + # FAR = below -> above threshold ------------------ + ## NSCAND = constant ------------------------------ + ### No labels ------------------------------------- + (DEFAULT_FAR_T/2.0, DEFAULT_FAR_T*2, False, False, None, []), + (DEFAULT_FAR_T/2.0, DEFAULT_FAR_T*2, True, True, None, []), + ### With labels ----------------------------------- + (DEFAULT_FAR_T/2.0, DEFAULT_FAR_T*2, False, False, DEFAULT_LABELS, []), + (DEFAULT_FAR_T/2.0, DEFAULT_FAR_T*2, True, True, DEFAULT_LABELS, []), + ### With labels matching label query -------------- + (DEFAULT_FAR_T/2.0, DEFAULT_FAR_T*2, False, False, + DEFAULT_LABEL_QUERY['labels'], []), + (DEFAULT_FAR_T/2.0, DEFAULT_FAR_T*2, True, True, + DEFAULT_LABEL_QUERY['labels'], []), + ## NSCAND = changing ------------------------------ + ### No labels ------------------------------------- + (DEFAULT_FAR_T/2.0, DEFAULT_FAR_T*2, False, True, None, + ['nscand_only']), + (DEFAULT_FAR_T/2.0, DEFAULT_FAR_T*2, True, False, None, []), + ### With labels ----------------------------------- + (DEFAULT_FAR_T/2.0, DEFAULT_FAR_T*2, False, True, DEFAULT_LABELS, + ['nscand_only', 'nscand_and_labels']), + (DEFAULT_FAR_T/2.0, DEFAULT_FAR_T*2, True, False, DEFAULT_LABELS, []), + ### With labels matching label query -------------- + (DEFAULT_FAR_T/2.0, DEFAULT_FAR_T*2, False, True, + DEFAULT_LABEL_QUERY['labels'], + ['nscand_only', 'nscand_and_labelq']), + (DEFAULT_FAR_T/2.0, DEFAULT_FAR_T*2, True, False, + DEFAULT_LABEL_QUERY['labels'], []), + # FAR = None -> above threshold ------------------- + ## NSCAND = constant ------------------------------ + ### No labels ------------------------------------- + (None, DEFAULT_FAR_T*2, False, False, None, []), + (None, DEFAULT_FAR_T*2, True, True, None, []), + ### With labels ----------------------------------- + (None, DEFAULT_FAR_T*2, False, False, DEFAULT_LABELS, []), + (None, DEFAULT_FAR_T*2, True, True, DEFAULT_LABELS, []), + ### With labels matching label query -------------- + (None, DEFAULT_FAR_T*2, False, False, DEFAULT_LABEL_QUERY['labels'], []), + (None, DEFAULT_FAR_T*2, True, True, DEFAULT_LABEL_QUERY['labels'], []), + ## NSCAND = changing ------------------------------ + ### No labels ------------------------------------- + (None, DEFAULT_FAR_T*2, False, True, None, ['nscand_only']), + (None, DEFAULT_FAR_T*2, True, False, None, []), + ### With labels ----------------------------------- + (None, DEFAULT_FAR_T*2, False, True, DEFAULT_LABELS, + ['nscand_only', 'nscand_and_labels']), + (None, DEFAULT_FAR_T*2, True, False, DEFAULT_LABELS, []), + ### With labels matching label query -------------- + (None, DEFAULT_FAR_T*2, False, True, DEFAULT_LABEL_QUERY['labels'], + ['nscand_only', 'nscand_and_labelq']), + (None, DEFAULT_FAR_T*2, True, False, DEFAULT_LABEL_QUERY['labels'], []), + # FAR = None -> below threshold ------------------- + ## NSCAND = constant ------------------------------ + ### No labels ------------------------------------- + (None, DEFAULT_FAR_T/2.0, False, False, None, ['far_t_only']), + (None, DEFAULT_FAR_T/2.0, True, True, None, + ['far_t_only', 'far_t_and_nscand']), + ### With labels ----------------------------------- + (None, DEFAULT_FAR_T/2.0, False, False, DEFAULT_LABELS, + ['far_t_only', 'far_t_and_labels']), + (None, DEFAULT_FAR_T/2.0, True, True, DEFAULT_LABELS, + ['far_t_only', 'far_t_and_nscand', 'far_t_and_labels', + 'far_t_and_nscand_and_labels']), + ### With labels matching label query -------------- + (None, DEFAULT_FAR_T/2.0, False, False, DEFAULT_LABEL_QUERY['labels'], + ['far_t_only', 'far_t_and_labelq']), + (None, DEFAULT_FAR_T/2.0, True, True, DEFAULT_LABEL_QUERY['labels'], + ['far_t_only', 'far_t_and_nscand', 'far_t_and_labelq', + 'far_t_and_nscand_and_labelq']), + # FAR = None -> below threshold ------------------- + ## NSCAND = changing ------------------------------ + ### No labels ------------------------------------- + (None, DEFAULT_FAR_T/2.0, False, True, None, + ['far_t_only', 'nscand_only', 'far_t_and_nscand']), + (None, DEFAULT_FAR_T/2.0, True, False, None, ['far_t_only']), + ### With labels ----------------------------------- + (None, DEFAULT_FAR_T/2.0, False, True, DEFAULT_LABELS, + ['far_t_only', 'nscand_only', 'far_t_and_nscand', + 'far_t_and_labels', 'nscand_and_labels', + 'far_t_and_nscand_and_labels']), + (None, DEFAULT_FAR_T/2.0, True, False, DEFAULT_LABELS, + ['far_t_only', 'far_t_and_labels']), + ### With labels matching label query -------------- + (None, DEFAULT_FAR_T/2.0, False, True, DEFAULT_LABEL_QUERY['labels'], + ['far_t_only', 'nscand_only', 'far_t_and_nscand', + 'far_t_and_labelq', 'nscand_and_labelq', + 'far_t_and_nscand_and_labelq']), + (None, DEFAULT_FAR_T/2.0, True, False, DEFAULT_LABEL_QUERY['labels'], + ['far_t_only', 'far_t_and_labelq']), + # FAR = above -> below threshold ------------------ + ## NSCAND = constant ------------------------------ + ### No labels ------------------------------------- + (DEFAULT_FAR_T*2, DEFAULT_FAR_T/2.0, False, False, None, + ['far_t_only']), + (DEFAULT_FAR_T*2, DEFAULT_FAR_T/2.0, True, True, None, + ['far_t_only', 'far_t_and_nscand']), + ### With labels ----------------------------------- + (DEFAULT_FAR_T*2, DEFAULT_FAR_T/2.0, False, False, DEFAULT_LABELS, + ['far_t_only', 'far_t_and_labels']), + (DEFAULT_FAR_T*2, DEFAULT_FAR_T/2.0, True, True, DEFAULT_LABELS, + ['far_t_only', 'far_t_and_nscand', 'far_t_and_labels', + 'far_t_and_nscand_and_labels']), + ### With labels matching label query -------------- + (DEFAULT_FAR_T*2, DEFAULT_FAR_T/2.0, False, False, + DEFAULT_LABEL_QUERY['labels'], + ['far_t_only', 'far_t_and_labelq']), + (DEFAULT_FAR_T*2, DEFAULT_FAR_T/2.0, True, True, + DEFAULT_LABEL_QUERY['labels'], + ['far_t_only', 'far_t_and_nscand', 'far_t_and_labelq', + 'far_t_and_nscand_and_labelq']), + ## NSCAND = changing ------------------------------ + ### No labels ------------------------------------- + (DEFAULT_FAR_T*2, DEFAULT_FAR_T/2.0, False, True, None, + ['far_t_only', 'nscand_only', 'far_t_and_nscand']), + (DEFAULT_FAR_T*2, DEFAULT_FAR_T/2.0, True, False, None, + ['far_t_only']), + ### With labels ----------------------------------- + (DEFAULT_FAR_T*2, DEFAULT_FAR_T/2.0, False, True, DEFAULT_LABELS, + ['far_t_only', 'nscand_only', 'far_t_and_nscand', + 'far_t_and_labels', 'nscand_and_labels', + 'far_t_and_nscand_and_labels']), + (DEFAULT_FAR_T*2, DEFAULT_FAR_T/2.0, True, False, DEFAULT_LABELS, + ['far_t_only', 'far_t_and_labels']), + ### With labels matching label query -------------- + (DEFAULT_FAR_T*2, DEFAULT_FAR_T/2.0, False, True, + DEFAULT_LABEL_QUERY['labels'], + ['far_t_only', 'nscand_only', 'far_t_and_nscand', + 'far_t_and_labelq', 'nscand_and_labelq', + 'far_t_and_nscand_and_labelq']), + (DEFAULT_FAR_T*2, DEFAULT_FAR_T/2.0, True, False, + DEFAULT_LABEL_QUERY['labels'], + ['far_t_only', 'far_t_and_labelq']), +] + +# Params: old_far,far,old_nscand,nscand,use_default_gps,labels,notif_descs +EVENT_UPDATE_ALERT_DATA = [ + # FAR = None and constant ------------------------- + ## No labels -------------------------------------- + ### NSCAND = constant ----------------------------- + (None, None, False, False, False, None, []), + (None, None, False, False, True, None, []), + (None, None, True, True, False, None, []), + (None, None, True, True, True, None, []), + ### With labels ----------------------------------- + (None, None, False, False, False, DEFAULT_LABELS, []), + (None, None, False, False, True, DEFAULT_LABELS, []), + (None, None, True, True, False, DEFAULT_LABELS, []), + (None, None, True, True, True, DEFAULT_LABELS, []), + ### With labels matching label query -------------- + (None, None, False, False, False, DEFAULT_LABEL_QUERY['labels'], []), + (None, None, False, False, True, DEFAULT_LABEL_QUERY['labels'], []), + (None, None, True, True, False, DEFAULT_LABEL_QUERY['labels'], []), + (None, None, True, True, True, DEFAULT_LABEL_QUERY['labels'], []), + ## NSCAND = changing ------------------------------ + ### No labels ------------------------------------- + (None, None, False, True, False, None, ['nscand_only']), + (None, None, False, True, True, None, ['nscand_only', 'nscand_and_gps']), + (None, None, True, False, False, None, []), + (None, None, True, False, True, None, []), + ### With labels ----------------------------------- + (None, None, False, True, False, DEFAULT_LABELS, + ['nscand_only', 'nscand_and_labels']), + (None, None, False, True, True, DEFAULT_LABELS, + ['nscand_only', 'nscand_and_labels', 'nscand_and_gps', + 'nscand_and_labels_and_gps']), + (None, None, True, False, False, DEFAULT_LABELS, []), + (None, None, True, False, True, DEFAULT_LABELS, []), + ### With labels matching label query -------------- + (None, None, False, True, False, DEFAULT_LABEL_QUERY['labels'], + ['nscand_only', 'nscand_and_labelq']), + (None, None, False, True, True, DEFAULT_LABEL_QUERY['labels'], + ['nscand_only', 'nscand_and_labelq', 'nscand_and_gps', + 'nscand_and_labelq_and_gps']), + (None, None, True, False, False, DEFAULT_LABEL_QUERY['labels'], []), + (None, None, True, False, True, DEFAULT_LABEL_QUERY['labels'], []), + # FAR = above threshold and constant -------------- + ## NSCAND = constant ------------------------------ + ### No labels ------------------------------------- + (DEFAULT_FAR_T*2, DEFAULT_FAR_T*2, False, False, False, None, []), + (DEFAULT_FAR_T*2, DEFAULT_FAR_T*2, False, False, True, None, []), + (DEFAULT_FAR_T*2, DEFAULT_FAR_T*2, True, True, False, None, []), + (DEFAULT_FAR_T*2, DEFAULT_FAR_T*2, True, True, True, None, []), + ### With labels ----------------------------------- + (DEFAULT_FAR_T*2, DEFAULT_FAR_T*2, False, False, False, DEFAULT_LABELS, + []), + (DEFAULT_FAR_T*2, DEFAULT_FAR_T*2, False, False, True, DEFAULT_LABELS, []), + (DEFAULT_FAR_T*2, DEFAULT_FAR_T*2, True, True, False, DEFAULT_LABELS, []), + (DEFAULT_FAR_T*2, DEFAULT_FAR_T*2, True, True, True, DEFAULT_LABELS, []), + ### With labels matching label query -------------- + (DEFAULT_FAR_T*2, DEFAULT_FAR_T*2, False, False, False, + DEFAULT_LABEL_QUERY['labels'], []), + (DEFAULT_FAR_T*2, DEFAULT_FAR_T*2, False, False, True, + DEFAULT_LABEL_QUERY['labels'], []), + (DEFAULT_FAR_T*2, DEFAULT_FAR_T*2, True, True, False, + DEFAULT_LABEL_QUERY['labels'], []), + (DEFAULT_FAR_T*2, DEFAULT_FAR_T*2, True, True, True, + DEFAULT_LABEL_QUERY['labels'], []), + ## NSCAND = changing ------------------------------ + ### No labels ------------------------------------- + (DEFAULT_FAR_T*2, DEFAULT_FAR_T*2, False, True, False, None, + ['nscand_only']), + (DEFAULT_FAR_T*2, DEFAULT_FAR_T*2, False, True, True, None, + ['nscand_only', 'nscand_and_gps']), + (DEFAULT_FAR_T*2, DEFAULT_FAR_T*2, True, False, False, None, []), + (DEFAULT_FAR_T*2, DEFAULT_FAR_T*2, True, False, True, None, []), + ### With labels ----------------------------------- + (DEFAULT_FAR_T*2, DEFAULT_FAR_T*2, False, True, False, DEFAULT_LABELS, + ['nscand_only', 'nscand_and_labels']), + (DEFAULT_FAR_T*2, DEFAULT_FAR_T*2, False, True, True, DEFAULT_LABELS, + ['nscand_only', 'nscand_and_labels', 'nscand_and_gps', + 'nscand_and_labels_and_gps']), + (DEFAULT_FAR_T*2, DEFAULT_FAR_T*2, True, False, False, DEFAULT_LABELS, []), + (DEFAULT_FAR_T*2, DEFAULT_FAR_T*2, True, False, True, DEFAULT_LABELS, []), + ### With labels matching label query -------------- + (DEFAULT_FAR_T*2, DEFAULT_FAR_T*2, False, True, False, + DEFAULT_LABEL_QUERY['labels'], + ['nscand_only', 'nscand_and_labelq']), + (DEFAULT_FAR_T*2, DEFAULT_FAR_T*2, False, True, True, + DEFAULT_LABEL_QUERY['labels'], + ['nscand_only', 'nscand_and_labelq', 'nscand_and_gps', + 'nscand_and_labelq_and_gps']), + (DEFAULT_FAR_T*2, DEFAULT_FAR_T*2, True, False, False, + DEFAULT_LABEL_QUERY['labels'], []), + (DEFAULT_FAR_T*2, DEFAULT_FAR_T*2, True, False, True, + DEFAULT_LABEL_QUERY['labels'], []), + # FAR = above threshold and increases ------------- + ## NSCAND = constant ------------------------------ + ### No labels ------------------------------------- + (DEFAULT_FAR_T*2, DEFAULT_FAR_T*4, False, False, False, None, []), + (DEFAULT_FAR_T*2, DEFAULT_FAR_T*4, False, False, True, None, []), + (DEFAULT_FAR_T*2, DEFAULT_FAR_T*4, True, True, False, None, []), + (DEFAULT_FAR_T*2, DEFAULT_FAR_T*4, True, True, True, None, []), + ### With labels ----------------------------------- + (DEFAULT_FAR_T*2, DEFAULT_FAR_T*4, False, False, False, DEFAULT_LABELS, + []), + (DEFAULT_FAR_T*2, DEFAULT_FAR_T*4, False, False, True, DEFAULT_LABELS, []), + (DEFAULT_FAR_T*2, DEFAULT_FAR_T*4, True, True, False, DEFAULT_LABELS, []), + (DEFAULT_FAR_T*2, DEFAULT_FAR_T*4, True, True, True, DEFAULT_LABELS, []), + ### With labels matching label query -------------- + (DEFAULT_FAR_T*2, DEFAULT_FAR_T*4, False, False, False, + DEFAULT_LABEL_QUERY['labels'], []), + (DEFAULT_FAR_T*2, DEFAULT_FAR_T*4, False, False, True, + DEFAULT_LABEL_QUERY['labels'], []), + (DEFAULT_FAR_T*2, DEFAULT_FAR_T*4, True, True, False, + DEFAULT_LABEL_QUERY['labels'], []), + (DEFAULT_FAR_T*2, DEFAULT_FAR_T*4, True, True, True, + DEFAULT_LABEL_QUERY['labels'], []), + ## NSCAND = changing ------------------------------ + ### No labels ------------------------------------- + (DEFAULT_FAR_T*2, DEFAULT_FAR_T*4, False, True, False, None, + ['nscand_only']), + (DEFAULT_FAR_T*2, DEFAULT_FAR_T*4, False, True, True, None, + ['nscand_only', 'nscand_and_gps']), + (DEFAULT_FAR_T*2, DEFAULT_FAR_T*4, True, False, False, None, []), + (DEFAULT_FAR_T*2, DEFAULT_FAR_T*4, True, False, True, None, []), + ### With labels ----------------------------------- + (DEFAULT_FAR_T*2, DEFAULT_FAR_T*4, False, True, False, DEFAULT_LABELS, + ['nscand_only', 'nscand_and_labels']), + (DEFAULT_FAR_T*2, DEFAULT_FAR_T*4, False, True, True, DEFAULT_LABELS, + ['nscand_only', 'nscand_and_labels', 'nscand_and_gps', + 'nscand_and_labels_and_gps']), + (DEFAULT_FAR_T*2, DEFAULT_FAR_T*4, True, False, False, DEFAULT_LABELS, []), + (DEFAULT_FAR_T*2, DEFAULT_FAR_T*4, True, False, True, DEFAULT_LABELS, []), + ### With labels matching label query -------------- + (DEFAULT_FAR_T*2, DEFAULT_FAR_T*4, False, True, False, + DEFAULT_LABEL_QUERY['labels'], + ['nscand_only', 'nscand_and_labelq']), + (DEFAULT_FAR_T*2, DEFAULT_FAR_T*4, False, True, True, + DEFAULT_LABEL_QUERY['labels'], + ['nscand_only', 'nscand_and_labelq', 'nscand_and_gps', + 'nscand_and_labelq_and_gps']), + (DEFAULT_FAR_T*2, DEFAULT_FAR_T*4, True, False, False, + DEFAULT_LABEL_QUERY['labels'], []), + (DEFAULT_FAR_T*2, DEFAULT_FAR_T*4, True, False, True, + DEFAULT_LABEL_QUERY['labels'], []), + # FAR = below threshold and constant -------------- + ## NSCAND = constant ------------------------------ + ### No labels ------------------------------------- + (DEFAULT_FAR_T/2.0, DEFAULT_FAR_T/2.0, False, False, False, None, []), + (DEFAULT_FAR_T/2.0, DEFAULT_FAR_T/2.0, False, False, True, None, []), + (DEFAULT_FAR_T/2.0, DEFAULT_FAR_T/2.0, True, True, False, None, []), + (DEFAULT_FAR_T/2.0, DEFAULT_FAR_T/2.0, True, True, True, None, []), + ### With labels ----------------------------------- + (DEFAULT_FAR_T/2.0, DEFAULT_FAR_T/2.0, False, False, False, DEFAULT_LABELS, + []), + (DEFAULT_FAR_T/2.0, DEFAULT_FAR_T/2.0, False, False, True, DEFAULT_LABELS, + []), + (DEFAULT_FAR_T/2.0, DEFAULT_FAR_T/2.0, True, True, False, DEFAULT_LABELS, + []), + (DEFAULT_FAR_T/2.0, DEFAULT_FAR_T/2.0, True, True, True, DEFAULT_LABELS, + []), + ### With labels matching label query -------------- + (DEFAULT_FAR_T/2.0, DEFAULT_FAR_T/2.0, False, False, False, + DEFAULT_LABEL_QUERY['labels'], []), + (DEFAULT_FAR_T/2.0, DEFAULT_FAR_T/2.0, False, False, True, + DEFAULT_LABEL_QUERY['labels'], []), + (DEFAULT_FAR_T/2.0, DEFAULT_FAR_T/2.0, True, True, False, + DEFAULT_LABEL_QUERY['labels'], []), + (DEFAULT_FAR_T/2.0, DEFAULT_FAR_T/2.0, True, True, True, + DEFAULT_LABEL_QUERY['labels'], []), + ## NSCAND = changing ------------------------------ + ### No labels ------------------------------------- + (DEFAULT_FAR_T/2.0, DEFAULT_FAR_T/2.0, False, True, False, None, + ['nscand_only', 'far_t_and_nscand']), + (DEFAULT_FAR_T/2.0, DEFAULT_FAR_T/2.0, False, True, True, None, + ['nscand_only', 'far_t_and_nscand', 'nscand_and_gps', + 'far_t_and_nscand_and_gps']), + (DEFAULT_FAR_T/2.0, DEFAULT_FAR_T/2.0, True, False, False, None, []), + (DEFAULT_FAR_T/2.0, DEFAULT_FAR_T/2.0, True, False, True, None, []), + ### With labels ----------------------------------- + (DEFAULT_FAR_T/2.0, DEFAULT_FAR_T/2.0, False, True, False, DEFAULT_LABELS, + ['nscand_only', 'far_t_and_nscand', 'nscand_and_labels', + 'far_t_and_nscand_and_labels']), + (DEFAULT_FAR_T/2.0, DEFAULT_FAR_T/2.0, False, True, True, DEFAULT_LABELS, + ['nscand_only', 'far_t_and_nscand', 'nscand_and_labels', + 'far_t_and_nscand_and_labels', 'nscand_and_gps', + 'far_t_and_nscand_and_gps', 'nscand_and_labels_and_gps', + 'far_t_and_nscand_and_labels_and_gps']), + (DEFAULT_FAR_T/2.0, DEFAULT_FAR_T/2.0, True, False, False, DEFAULT_LABELS, + []), + (DEFAULT_FAR_T/2.0, DEFAULT_FAR_T/2.0, True, False, True, DEFAULT_LABELS, + []), + ### With labels matching label query -------------- + (DEFAULT_FAR_T/2.0, DEFAULT_FAR_T/2.0, False, True, False, + DEFAULT_LABEL_QUERY['labels'], + ['nscand_only', 'far_t_and_nscand', 'nscand_and_labelq', + 'far_t_and_nscand_and_labelq']), + (DEFAULT_FAR_T/2.0, DEFAULT_FAR_T/2.0, False, True, True, + DEFAULT_LABEL_QUERY['labels'], + ['nscand_only', 'far_t_and_nscand', 'nscand_and_labelq', + 'far_t_and_nscand_and_labelq', 'nscand_and_gps', + 'far_t_and_nscand_and_gps', 'nscand_and_labelq_and_gps', + 'far_t_and_nscand_and_labelq_and_gps']), + (DEFAULT_FAR_T/2.0, DEFAULT_FAR_T/2.0, True, False, False, + DEFAULT_LABEL_QUERY['labels'], []), + (DEFAULT_FAR_T/2.0, DEFAULT_FAR_T/2.0, True, False, True, + DEFAULT_LABEL_QUERY['labels'], []), + # FAR = below threshold and decreases ------------- + ## NSCAND = constant ------------------------------ + ### No labels ------------------------------------- + (DEFAULT_FAR_T/2.0, DEFAULT_FAR_T/4.0, False, False, False, None, []), + (DEFAULT_FAR_T/2.0, DEFAULT_FAR_T/4.0, False, False, True, None, []), + (DEFAULT_FAR_T/2.0, DEFAULT_FAR_T/4.0, True, True, False, None, []), + (DEFAULT_FAR_T/2.0, DEFAULT_FAR_T/4.0, True, True, True, None, []), + ### With labels ----------------------------------- + (DEFAULT_FAR_T/2.0, DEFAULT_FAR_T/4.0, False, False, False, DEFAULT_LABELS, + []), + (DEFAULT_FAR_T/2.0, DEFAULT_FAR_T/4.0, False, False, True, DEFAULT_LABELS, + []), + (DEFAULT_FAR_T/2.0, DEFAULT_FAR_T/4.0, True, True, False, DEFAULT_LABELS, + []), + (DEFAULT_FAR_T/2.0, DEFAULT_FAR_T/4.0, True, True, True, DEFAULT_LABELS, + []), + ### With labels matching label query -------------- + (DEFAULT_FAR_T/2.0, DEFAULT_FAR_T/4.0, False, False, False, + DEFAULT_LABEL_QUERY['labels'], []), + (DEFAULT_FAR_T/2.0, DEFAULT_FAR_T/4.0, False, False, True, + DEFAULT_LABEL_QUERY['labels'], []), + (DEFAULT_FAR_T/2.0, DEFAULT_FAR_T/4.0, True, True, False, + DEFAULT_LABEL_QUERY['labels'], []), + (DEFAULT_FAR_T/2.0, DEFAULT_FAR_T/4.0, True, True, True, + DEFAULT_LABEL_QUERY['labels'], []), + ## NSCAND = changing ------------------------------ + ### No labels ------------------------------------- + (DEFAULT_FAR_T/2.0, DEFAULT_FAR_T/4.0, False, True, False, None, + ['nscand_only', 'far_t_and_nscand']), + (DEFAULT_FAR_T/2.0, DEFAULT_FAR_T/4.0, False, True, True, None, + ['nscand_only', 'far_t_and_nscand', 'nscand_and_gps', + 'far_t_and_nscand_and_gps']), + (DEFAULT_FAR_T/2.0, DEFAULT_FAR_T/4.0, True, False, False, None, []), + (DEFAULT_FAR_T/2.0, DEFAULT_FAR_T/4.0, True, False, True, None, []), + ### With labels ----------------------------------- + (DEFAULT_FAR_T/2.0, DEFAULT_FAR_T/4.0, False, True, False, DEFAULT_LABELS, + ['nscand_only', 'far_t_and_nscand', 'nscand_and_labels', + 'far_t_and_nscand_and_labels']), + (DEFAULT_FAR_T/2.0, DEFAULT_FAR_T/4.0, False, True, True, DEFAULT_LABELS, + ['nscand_only', 'far_t_and_nscand', 'nscand_and_labels', + 'far_t_and_nscand_and_labels', 'nscand_and_gps', + 'far_t_and_nscand_and_gps', 'nscand_and_labels_and_gps', + 'far_t_and_nscand_and_labels_and_gps']), + (DEFAULT_FAR_T/2.0, DEFAULT_FAR_T/4.0, True, False, False, DEFAULT_LABELS, + []), + (DEFAULT_FAR_T/2.0, DEFAULT_FAR_T/4.0, True, False, True, DEFAULT_LABELS, + []), + ### With labels matching label query -------------- + (DEFAULT_FAR_T/2.0, DEFAULT_FAR_T/4.0, False, True, False, + DEFAULT_LABEL_QUERY['labels'], + ['nscand_only', 'far_t_and_nscand', 'nscand_and_labelq', + 'far_t_and_nscand_and_labelq']), + (DEFAULT_FAR_T/2.0, DEFAULT_FAR_T/4.0, False, True, True, + DEFAULT_LABEL_QUERY['labels'], + ['nscand_only', 'far_t_and_nscand', 'nscand_and_labelq', + 'far_t_and_nscand_and_labelq', 'nscand_and_gps', + 'far_t_and_nscand_and_gps', 'nscand_and_labelq_and_gps', + 'far_t_and_nscand_and_labelq_and_gps']), + (DEFAULT_FAR_T/2.0, DEFAULT_FAR_T/4.0, True, False, False, + DEFAULT_LABEL_QUERY['labels'], []), + (DEFAULT_FAR_T/2.0, DEFAULT_FAR_T/4.0, True, False, True, + DEFAULT_LABEL_QUERY['labels'], []), + # FAR = below -> above threshold ------------------ + ## NSCAND = constant ------------------------------ + ### No labels ------------------------------------- + (DEFAULT_FAR_T/2.0, DEFAULT_FAR_T*2, False, False, False, None, []), + (DEFAULT_FAR_T/2.0, DEFAULT_FAR_T*2, False, False, True, None, []), + (DEFAULT_FAR_T/2.0, DEFAULT_FAR_T*2, True, True, False, None, []), + (DEFAULT_FAR_T/2.0, DEFAULT_FAR_T*2, True, True, True, None, []), + ### With labels ----------------------------------- + (DEFAULT_FAR_T/2.0, DEFAULT_FAR_T*2, False, False, False, DEFAULT_LABELS, + []), + (DEFAULT_FAR_T/2.0, DEFAULT_FAR_T*2, False, False, True, DEFAULT_LABELS, + []), + (DEFAULT_FAR_T/2.0, DEFAULT_FAR_T*2, True, True, False, DEFAULT_LABELS, + []), + (DEFAULT_FAR_T/2.0, DEFAULT_FAR_T*2, True, True, True, DEFAULT_LABELS, + []), + ### With labels matching label query -------------- + (DEFAULT_FAR_T/2.0, DEFAULT_FAR_T*2, False, False, False, + DEFAULT_LABEL_QUERY['labels'], []), + (DEFAULT_FAR_T/2.0, DEFAULT_FAR_T*2, False, False, True, + DEFAULT_LABEL_QUERY['labels'], []), + (DEFAULT_FAR_T/2.0, DEFAULT_FAR_T*2, True, True, False, + DEFAULT_LABEL_QUERY['labels'], []), + (DEFAULT_FAR_T/2.0, DEFAULT_FAR_T*2, True, True, True, + DEFAULT_LABEL_QUERY['labels'], []), + ## NSCAND = changing ------------------------------ + ### No labels ------------------------------------- + (DEFAULT_FAR_T/2.0, DEFAULT_FAR_T*2, False, True, False, None, + ['nscand_only']), + (DEFAULT_FAR_T/2.0, DEFAULT_FAR_T*2, False, True, True, None, + ['nscand_only', 'nscand_and_gps']), + (DEFAULT_FAR_T/2.0, DEFAULT_FAR_T*2, True, False, False, None, []), + (DEFAULT_FAR_T/2.0, DEFAULT_FAR_T*2, True, False, True, None, []), + ### With labels ----------------------------------- + (DEFAULT_FAR_T/2.0, DEFAULT_FAR_T*2, False, True, False, DEFAULT_LABELS, + ['nscand_only', 'nscand_and_labels']), + (DEFAULT_FAR_T/2.0, DEFAULT_FAR_T*2, False, True, True, DEFAULT_LABELS, + ['nscand_only', 'nscand_and_labels', 'nscand_and_gps', + 'nscand_and_labels_and_gps']), + (DEFAULT_FAR_T/2.0, DEFAULT_FAR_T*2, True, False, False, DEFAULT_LABELS, + []), + (DEFAULT_FAR_T/2.0, DEFAULT_FAR_T*2, True, False, True, DEFAULT_LABELS, + []), + ### With labels matching label query -------------- + (DEFAULT_FAR_T/2.0, DEFAULT_FAR_T*2, False, True, False, + DEFAULT_LABEL_QUERY['labels'], + ['nscand_only', 'nscand_and_labelq']), + (DEFAULT_FAR_T/2.0, DEFAULT_FAR_T*2, False, True, True, + DEFAULT_LABEL_QUERY['labels'], + ['nscand_only', 'nscand_and_labelq', 'nscand_and_gps', + 'nscand_and_labelq_and_gps']), + (DEFAULT_FAR_T/2.0, DEFAULT_FAR_T*2, True, False, False, + DEFAULT_LABEL_QUERY['labels'], []), + (DEFAULT_FAR_T/2.0, DEFAULT_FAR_T*2, True, False, True, + DEFAULT_LABEL_QUERY['labels'], []), + # FAR = None -> above threshold ------------------- + ## NSCAND = constant ------------------------------ + ### No labels ------------------------------------- + (None, DEFAULT_FAR_T*2, False, False, False, None, []), + (None, DEFAULT_FAR_T*2, False, False, True, None, []), + (None, DEFAULT_FAR_T*2, True, True, False, None, []), + (None, DEFAULT_FAR_T*2, True, True, True, None, []), + ### With labels ----------------------------------- + (None, DEFAULT_FAR_T*2, False, False, False, DEFAULT_LABELS, []), + (None, DEFAULT_FAR_T*2, False, False, True, DEFAULT_LABELS, []), + (None, DEFAULT_FAR_T*2, True, True, False, DEFAULT_LABELS, []), + (None, DEFAULT_FAR_T*2, True, True, True, DEFAULT_LABELS, []), + ### With labels matching label query -------------- + (None, DEFAULT_FAR_T*2, False, False, False, DEFAULT_LABEL_QUERY['labels'], + []), + (None, DEFAULT_FAR_T*2, False, False, True, DEFAULT_LABEL_QUERY['labels'], + []), + (None, DEFAULT_FAR_T*2, True, True, False, DEFAULT_LABEL_QUERY['labels'], + []), + (None, DEFAULT_FAR_T*2, True, True, True, DEFAULT_LABEL_QUERY['labels'], + []), + ## NSCAND = changing ------------------------------ + ### No labels ------------------------------------- + (None, DEFAULT_FAR_T*2, False, True, False, None, ['nscand_only']), + (None, DEFAULT_FAR_T*2, False, True, True, None, + ['nscand_only', 'nscand_and_gps']), + (None, DEFAULT_FAR_T*2, True, False, False, None, []), + (None, DEFAULT_FAR_T*2, True, False, True, None, []), + ### With labels ----------------------------------- + (None, DEFAULT_FAR_T*2, False, True, False, DEFAULT_LABELS, + ['nscand_only', 'nscand_and_labels']), + (None, DEFAULT_FAR_T*2, False, True, True, DEFAULT_LABELS, + ['nscand_only', 'nscand_and_labels', 'nscand_and_gps', + 'nscand_and_labels_and_gps']), + (None, DEFAULT_FAR_T*2, True, False, False, DEFAULT_LABELS, []), + (None, DEFAULT_FAR_T*2, True, False, True, DEFAULT_LABELS, []), + ### With labels matching label query -------------- + (None, DEFAULT_FAR_T*2, False, True, False, DEFAULT_LABEL_QUERY['labels'], + ['nscand_only', 'nscand_and_labelq']), + (None, DEFAULT_FAR_T*2, False, True, True, DEFAULT_LABEL_QUERY['labels'], + ['nscand_only', 'nscand_and_labelq', 'nscand_and_gps', + 'nscand_and_labelq_and_gps']), + (None, DEFAULT_FAR_T*2, True, False, False, DEFAULT_LABEL_QUERY['labels'], + []), + (None, DEFAULT_FAR_T*2, True, False, True, DEFAULT_LABEL_QUERY['labels'], + []), + # FAR = None -> below threshold ------------------- + ## NSCAND = constant ------------------------------ + ### No labels ------------------------------------- + (None, DEFAULT_FAR_T/2.0, False, False, False, None, ['far_t_only']), + (None, DEFAULT_FAR_T/2.0, False, False, True, None, + ['far_t_only', 'far_t_and_gps']), + (None, DEFAULT_FAR_T/2.0, True, True, False, None, + ['far_t_only', 'far_t_and_nscand']), + (None, DEFAULT_FAR_T/2.0, True, True, True, None, + ['far_t_only', 'far_t_and_nscand', 'far_t_and_gps', + 'far_t_and_nscand_and_gps']), + ### With labels ----------------------------------- + (None, DEFAULT_FAR_T/2.0, False, False, False, DEFAULT_LABELS, + ['far_t_only', 'far_t_and_labels']), + (None, DEFAULT_FAR_T/2.0, False, False, True, DEFAULT_LABELS, + ['far_t_only', 'far_t_and_labels', 'far_t_and_gps', + 'far_t_and_labels_and_gps']), + (None, DEFAULT_FAR_T/2.0, True, True, False, DEFAULT_LABELS, + ['far_t_only', 'far_t_and_nscand', 'far_t_and_labels', + 'far_t_and_nscand_and_labels']), + (None, DEFAULT_FAR_T/2.0, True, True, True, DEFAULT_LABELS, + ['far_t_only', 'far_t_and_nscand', 'far_t_and_labels', + 'far_t_and_nscand_and_labels', 'far_t_and_gps', + 'far_t_and_nscand_and_gps', 'far_t_and_labels_and_gps', + 'far_t_and_nscand_and_labels_and_gps']), + ### With labels matching label query -------------- + (None, DEFAULT_FAR_T/2.0, False, False, False, + DEFAULT_LABEL_QUERY['labels'], + ['far_t_only', 'far_t_and_labelq']), + (None, DEFAULT_FAR_T/2.0, False, False, True, + DEFAULT_LABEL_QUERY['labels'], + ['far_t_only', 'far_t_and_labelq', 'far_t_and_gps', + 'far_t_and_labelq_and_gps']), + (None, DEFAULT_FAR_T/2.0, True, True, False, DEFAULT_LABEL_QUERY['labels'], + ['far_t_only', 'far_t_and_nscand', 'far_t_and_labelq', + 'far_t_and_nscand_and_labelq']), + (None, DEFAULT_FAR_T/2.0, True, True, True, DEFAULT_LABEL_QUERY['labels'], + ['far_t_only', 'far_t_and_nscand', 'far_t_and_labelq', + 'far_t_and_nscand_and_labelq', 'far_t_and_gps', + 'far_t_and_nscand_and_gps', 'far_t_and_labelq_and_gps', + 'far_t_and_nscand_and_labelq_and_gps']), + # FAR = None -> below threshold ------------------- + ## NSCAND = changing ------------------------------ + ### No labels ------------------------------------- + (None, DEFAULT_FAR_T/2.0, False, True, False, None, + ['far_t_only', 'nscand_only', 'far_t_and_nscand']), + (None, DEFAULT_FAR_T/2.0, False, True, True, None, + ['far_t_only', 'nscand_only', 'far_t_and_nscand', 'far_t_and_gps', + 'nscand_and_gps', 'far_t_and_nscand_and_gps']), + (None, DEFAULT_FAR_T/2.0, True, False, False, None, ['far_t_only']), + (None, DEFAULT_FAR_T/2.0, True, False, True, None, + ['far_t_only', 'far_t_and_gps']), + ### With labels ----------------------------------- + (None, DEFAULT_FAR_T/2.0, False, True, False, DEFAULT_LABELS, + ['far_t_only', 'nscand_only', 'far_t_and_nscand', + 'far_t_and_labels', 'nscand_and_labels', + 'far_t_and_nscand_and_labels']), + (None, DEFAULT_FAR_T/2.0, False, True, True, DEFAULT_LABELS, + ['far_t_only', 'nscand_only', 'far_t_and_nscand', + 'far_t_and_labels', 'nscand_and_labels', + 'far_t_and_nscand_and_labels', 'far_t_and_gps', 'nscand_and_gps', + 'far_t_and_nscand_and_gps', 'far_t_and_labels_and_gps', + 'nscand_and_labels_and_gps', 'far_t_and_nscand_and_labels_and_gps']), + (None, DEFAULT_FAR_T/2.0, True, False, False, DEFAULT_LABELS, + ['far_t_only', 'far_t_and_labels']), + (None, DEFAULT_FAR_T/2.0, True, False, True, DEFAULT_LABELS, + ['far_t_only', 'far_t_and_labels', 'far_t_and_gps', + 'far_t_and_labels_and_gps']), + ### With labels matching label query -------------- + (None, DEFAULT_FAR_T/2.0, False, True, False, + DEFAULT_LABEL_QUERY['labels'], + ['far_t_only', 'nscand_only', 'far_t_and_nscand', 'far_t_and_labelq', + 'nscand_and_labelq', 'far_t_and_nscand_and_labelq']), + (None, DEFAULT_FAR_T/2.0, False, True, True, + DEFAULT_LABEL_QUERY['labels'], + ['far_t_only', 'nscand_only', 'far_t_and_nscand', 'far_t_and_labelq', + 'nscand_and_labelq', 'far_t_and_nscand_and_labelq', + 'far_t_and_gps', 'nscand_and_gps', 'far_t_and_nscand_and_gps', + 'far_t_and_labelq_and_gps', 'nscand_and_labelq_and_gps', + 'far_t_and_nscand_and_labelq_and_gps']), + (None, DEFAULT_FAR_T/2.0, True, False, False, + DEFAULT_LABEL_QUERY['labels'], + ['far_t_only', 'far_t_and_labelq']), + (None, DEFAULT_FAR_T/2.0, True, False, True, + DEFAULT_LABEL_QUERY['labels'], + ['far_t_only', 'far_t_and_labelq', 'far_t_and_gps', + 'far_t_and_labelq_and_gps']), + # FAR = above -> below threshold ------------------ + ## NSCAND = constant ------------------------------ + ### No labels ------------------------------------- + (DEFAULT_FAR_T*2, DEFAULT_FAR_T/2.0, False, False, False, None, + ['far_t_only']), + (DEFAULT_FAR_T*2, DEFAULT_FAR_T/2.0, False, False, True, None, + ['far_t_only', 'far_t_and_gps']), + (DEFAULT_FAR_T*2, DEFAULT_FAR_T/2.0, True, True, False, None, + ['far_t_only', 'far_t_and_nscand']), + (DEFAULT_FAR_T*2, DEFAULT_FAR_T/2.0, True, True, True, None, + ['far_t_only', 'far_t_and_nscand', 'far_t_and_gps', + 'far_t_and_nscand_and_gps']), + ### With labels ----------------------------------- + (DEFAULT_FAR_T*2, DEFAULT_FAR_T/2.0, False, False, False, DEFAULT_LABELS, + ['far_t_only', 'far_t_and_labels']), + (DEFAULT_FAR_T*2, DEFAULT_FAR_T/2.0, False, False, True, DEFAULT_LABELS, + ['far_t_only', 'far_t_and_labels', 'far_t_and_gps', + 'far_t_and_labels_and_gps']), + (DEFAULT_FAR_T*2, DEFAULT_FAR_T/2.0, True, True, False, DEFAULT_LABELS, + ['far_t_only', 'far_t_and_nscand', 'far_t_and_labels', + 'far_t_and_nscand_and_labels']), + (DEFAULT_FAR_T*2, DEFAULT_FAR_T/2.0, True, True, True, DEFAULT_LABELS, + ['far_t_only', 'far_t_and_nscand', 'far_t_and_labels', + 'far_t_and_nscand_and_labels', 'far_t_and_gps', + 'far_t_and_nscand_and_gps', 'far_t_and_labels_and_gps', + 'far_t_and_nscand_and_labels_and_gps']), + ### With labels matching label query -------------- + (DEFAULT_FAR_T*2, DEFAULT_FAR_T/2.0, False, False, False, + DEFAULT_LABEL_QUERY['labels'], + ['far_t_only', 'far_t_and_labelq']), + (DEFAULT_FAR_T*2, DEFAULT_FAR_T/2.0, False, False, True, + DEFAULT_LABEL_QUERY['labels'], + ['far_t_only', 'far_t_and_labelq', 'far_t_and_gps', + 'far_t_and_labelq_and_gps']), + (DEFAULT_FAR_T*2, DEFAULT_FAR_T/2.0, True, True, False, + DEFAULT_LABEL_QUERY['labels'], + ['far_t_only', 'far_t_and_nscand', 'far_t_and_labelq', + 'far_t_and_nscand_and_labelq']), + (DEFAULT_FAR_T*2, DEFAULT_FAR_T/2.0, True, True, True, + DEFAULT_LABEL_QUERY['labels'], + ['far_t_only', 'far_t_and_nscand', 'far_t_and_labelq', + 'far_t_and_nscand_and_labelq', 'far_t_and_gps', + 'far_t_and_nscand_and_gps', 'far_t_and_labelq_and_gps', + 'far_t_and_nscand_and_labelq_and_gps']), + ## NSCAND = changing ------------------------------ + ### No labels ------------------------------------- + (DEFAULT_FAR_T*2, DEFAULT_FAR_T/2.0, False, True, False, None, + ['far_t_only', 'nscand_only', 'far_t_and_nscand']), + (DEFAULT_FAR_T*2, DEFAULT_FAR_T/2.0, False, True, True, None, + ['far_t_only', 'nscand_only', 'far_t_and_nscand', + 'far_t_and_gps', 'nscand_and_gps', 'far_t_and_nscand_and_gps']), + (DEFAULT_FAR_T*2, DEFAULT_FAR_T/2.0, True, False, False, None, + ['far_t_only']), + (DEFAULT_FAR_T*2, DEFAULT_FAR_T/2.0, True, False, True, None, + ['far_t_only', 'far_t_and_gps']), + ### With labels ----------------------------------- + (DEFAULT_FAR_T*2, DEFAULT_FAR_T/2.0, False, True, False, DEFAULT_LABELS, + ['far_t_only', 'nscand_only', 'far_t_and_nscand', + 'far_t_and_labels', 'nscand_and_labels', + 'far_t_and_nscand_and_labels']), + (DEFAULT_FAR_T*2, DEFAULT_FAR_T/2.0, False, True, True, DEFAULT_LABELS, + ['far_t_only', 'nscand_only', 'far_t_and_nscand', 'far_t_and_labels', + 'nscand_and_labels', 'far_t_and_nscand_and_labels', 'far_t_and_gps', + 'nscand_and_gps', 'far_t_and_nscand_and_gps', + 'far_t_and_labels_and_gps', 'nscand_and_labels_and_gps', + 'far_t_and_nscand_and_labels_and_gps']), + (DEFAULT_FAR_T*2, DEFAULT_FAR_T/2.0, True, False, False, DEFAULT_LABELS, + ['far_t_only', 'far_t_and_labels']), + (DEFAULT_FAR_T*2, DEFAULT_FAR_T/2.0, True, False, True, DEFAULT_LABELS, + ['far_t_only', 'far_t_and_labels', 'far_t_and_gps', + 'far_t_and_labels_and_gps']), + ### With labels matching label query -------------- + (DEFAULT_FAR_T*2, DEFAULT_FAR_T/2.0, False, True, False, + DEFAULT_LABEL_QUERY['labels'], + ['far_t_only', 'nscand_only', 'far_t_and_nscand', 'far_t_and_labelq', + 'nscand_and_labelq', 'far_t_and_nscand_and_labelq']), + (DEFAULT_FAR_T*2, DEFAULT_FAR_T/2.0, False, True, True, + DEFAULT_LABEL_QUERY['labels'], + ['far_t_only', 'nscand_only', 'far_t_and_nscand', 'far_t_and_labelq', + 'nscand_and_labelq', 'far_t_and_nscand_and_labelq', 'far_t_and_gps', + 'nscand_and_gps', 'far_t_and_nscand_and_gps', + 'far_t_and_labelq_and_gps', 'nscand_and_labelq_and_gps', + 'far_t_and_nscand_and_labelq_and_gps']), + (DEFAULT_FAR_T*2, DEFAULT_FAR_T/2.0, True, False, False, + DEFAULT_LABEL_QUERY['labels'], + ['far_t_only', 'far_t_and_labelq']), + (DEFAULT_FAR_T*2, DEFAULT_FAR_T/2.0, True, False, True, + DEFAULT_LABEL_QUERY['labels'], + ['far_t_only', 'far_t_and_labelq', 'far_t_and_gps', + 'far_t_and_labelq_and_gps']), +] + +SUPEREVENT_LABEL_ADDED_ALERT_DATA = [ + # Label criteria not met -------------------------- + ## FAR = None ------------------------------------- + (None, False, DEFAULT_LABELS[0], None, []), + (None, True, DEFAULT_LABELS[0], None, []), + ## FAR > threshold -------------------------------- + (DEFAULT_FAR_T*2.0, False, DEFAULT_LABELS[0], None, []), + (DEFAULT_FAR_T*2.0, True, DEFAULT_LABELS[0], None, []), + ## FAR < threshold -------------------------------- + (DEFAULT_FAR_T/2.0, False, DEFAULT_LABELS[0], None, []), + (DEFAULT_FAR_T/2.0, True, DEFAULT_LABELS[0], None, []), + # Label criteria met ------------------------------ + ## FAR = None ------------------------------------- + (None, False, DEFAULT_LABELS[0], DEFAULT_LABELS[1:], + ['labels_only']), + (None, True, DEFAULT_LABELS[0], DEFAULT_LABELS[1:], + ['nscand_and_labels', 'labels_only']), + ## FAR > threshold -------------------------------- + (DEFAULT_FAR_T*2.0, False, DEFAULT_LABELS[0], DEFAULT_LABELS[1:], + ['labels_only']), + (DEFAULT_FAR_T*2.0, True, DEFAULT_LABELS[0], DEFAULT_LABELS[1:], + ['nscand_and_labels', 'labels_only']), + ## FAR < threshold -------------------------------- + (DEFAULT_FAR_T/2.0, False, DEFAULT_LABELS[0], DEFAULT_LABELS[1:], + ['far_t_and_labels', 'labels_only']), + (DEFAULT_FAR_T/2.0, True, DEFAULT_LABELS[0], DEFAULT_LABELS[1:], + ['far_t_and_labels', 'nscand_and_labels', 'labels_only', + 'far_t_and_nscand_and_labels']), + # Label criteria met, some additional labels previously added + ## FAR = None ------------------------------------- + (None, False, DEFAULT_LABELS[0], DEFAULT_LABELS[1:] + [RANDOM_LABEL], + ['labels_only']), + (None, True, DEFAULT_LABELS[0], DEFAULT_LABELS[1:] + [RANDOM_LABEL], + ['nscand_and_labels', 'labels_only']), + ## FAR > threshold -------------------------------- + (DEFAULT_FAR_T*2.0, False, DEFAULT_LABELS[0], + DEFAULT_LABELS[1:] + [RANDOM_LABEL], ['labels_only']), + (DEFAULT_FAR_T*2.0, True, DEFAULT_LABELS[0], + DEFAULT_LABELS[1:] + [RANDOM_LABEL], + ['nscand_and_labels', 'labels_only']), + ## FAR < threshold -------------------------------- + (DEFAULT_FAR_T/2.0, False, DEFAULT_LABELS[0], + DEFAULT_LABELS[1:] + [RANDOM_LABEL], + ['far_t_and_labels', 'labels_only']), + (DEFAULT_FAR_T/2.0, True, DEFAULT_LABELS[0], + DEFAULT_LABELS[1:] + [RANDOM_LABEL], + ['far_t_and_labels', 'nscand_and_labels', 'labels_only', + 'far_t_and_nscand_and_labels']), + # Label criteria previously met ------------------- + ## FAR = None ------------------------------------- + (None, False, RANDOM_LABEL, DEFAULT_LABELS, []), + (None, True, RANDOM_LABEL, DEFAULT_LABELS, []), + ## FAR > threshold -------------------------------- + (DEFAULT_FAR_T*2.0, False, RANDOM_LABEL, DEFAULT_LABELS, []), + (DEFAULT_FAR_T*2.0, True, RANDOM_LABEL, DEFAULT_LABELS, []), + ## FAR < threshold -------------------------------- + (DEFAULT_FAR_T/2.0, False, RANDOM_LABEL, DEFAULT_LABELS, []), + (DEFAULT_FAR_T/2.0, True, RANDOM_LABEL, DEFAULT_LABELS, []), + # Label query criteria not met -------------------- + ## FAR = None ------------------------------------- + (None, False, DEFAULT_LABEL_QUERY['labels'][0], None, []), + (None, True, DEFAULT_LABEL_QUERY['labels'][0], None, []), + ## FAR > threshold -------------------------------- + (DEFAULT_FAR_T*2.0, False, DEFAULT_LABEL_QUERY['labels'][0], None, []), + (DEFAULT_FAR_T*2.0, True, DEFAULT_LABEL_QUERY['labels'][0], None, []), + ## FAR < threshold -------------------------------- + (DEFAULT_FAR_T/2.0, False, DEFAULT_LABEL_QUERY['labels'][0], None, []), + (DEFAULT_FAR_T/2.0, True, DEFAULT_LABEL_QUERY['labels'][0], None, []), + # Label query criteria met ------------------------- + ## FAR = None ------------------------------------- + (None, False, DEFAULT_LABEL_QUERY['labels'][0], + DEFAULT_LABEL_QUERY['labels'][1:], ['labelq_only']), + (None, True, DEFAULT_LABEL_QUERY['labels'][0], + DEFAULT_LABEL_QUERY['labels'][1:], + ['nscand_and_labelq', 'labelq_only']), + ## FAR > threshold -------------------------------- + (DEFAULT_FAR_T*2.0, False, DEFAULT_LABEL_QUERY['labels'][0], + DEFAULT_LABEL_QUERY['labels'][1:], ['labelq_only']), + (DEFAULT_FAR_T*2.0, True, DEFAULT_LABEL_QUERY['labels'][0], + DEFAULT_LABEL_QUERY['labels'][1:], + ['nscand_and_labelq', 'labelq_only']), + ## FAR < threshold -------------------------------- + (DEFAULT_FAR_T/2.0, False, DEFAULT_LABEL_QUERY['labels'][0], + DEFAULT_LABEL_QUERY['labels'][1:], + ['far_t_and_labelq', 'labelq_only']), + (DEFAULT_FAR_T/2.0, True, DEFAULT_LABEL_QUERY['labels'][0], + DEFAULT_LABEL_QUERY['labels'][1:], + ['far_t_and_labelq', 'nscand_and_labelq', 'labelq_only', + 'far_t_and_nscand_and_labelq']), + # Label query criteria met, some additional labels previously added + ## FAR = None ------------------------------------- + (None, False, DEFAULT_LABEL_QUERY['labels'][0], + DEFAULT_LABEL_QUERY['labels'][1:] + [RANDOM_LABEL], + ['labelq_only']), + (None, True, DEFAULT_LABEL_QUERY['labels'][0], + DEFAULT_LABEL_QUERY['labels'][1:] + [RANDOM_LABEL], + ['nscand_and_labelq', 'labelq_only']), + ## FAR > threshold -------------------------------- + (DEFAULT_FAR_T*2.0, False, DEFAULT_LABEL_QUERY['labels'][0], + DEFAULT_LABEL_QUERY['labels'][1:] + [RANDOM_LABEL], + ['labelq_only']), + (DEFAULT_FAR_T*2.0, True, DEFAULT_LABEL_QUERY['labels'][0], + DEFAULT_LABEL_QUERY['labels'][1:] + [RANDOM_LABEL], + ['nscand_and_labelq', 'labelq_only']), + ## FAR < threshold -------------------------------- + (DEFAULT_FAR_T/2.0, False, DEFAULT_LABEL_QUERY['labels'][0], + DEFAULT_LABEL_QUERY['labels'][1:] + [RANDOM_LABEL], + ['far_t_and_labelq', 'labelq_only']), + (DEFAULT_FAR_T/2.0, True, DEFAULT_LABEL_QUERY['labels'][0], + DEFAULT_LABEL_QUERY['labels'][1:] + [RANDOM_LABEL], + ['far_t_and_labelq', 'nscand_and_labelq', 'labelq_only', + 'far_t_and_nscand_and_labelq']), + # Label query criteria previously met ------------- + ## FAR = None ------------------------------------- + (None, False, RANDOM_LABEL, DEFAULT_LABEL_QUERY['labels'], []), + (None, True, RANDOM_LABEL, DEFAULT_LABEL_QUERY['labels'], []), + ## FAR > threshold -------------------------------- + (DEFAULT_FAR_T*2.0, False, RANDOM_LABEL, DEFAULT_LABEL_QUERY['labels'], + []), + (DEFAULT_FAR_T*2.0, True, RANDOM_LABEL, DEFAULT_LABEL_QUERY['labels'], []), + ## FAR < threshold -------------------------------- + (DEFAULT_FAR_T/2.0, False, RANDOM_LABEL, DEFAULT_LABEL_QUERY['labels'], + []), + (DEFAULT_FAR_T/2.0, True, RANDOM_LABEL, DEFAULT_LABEL_QUERY['labels'], []), +] + +# Params: far,nscand,new_label,old_labels,use_default_gps,notif_descs +EVENT_LABEL_ADDED_ALERT_DATA = [ + # Label criteria not met -------------------------- + ## FAR = None ------------------------------------- + (None, False, DEFAULT_LABELS[0], None, False, []), + (None, False, DEFAULT_LABELS[0], None, True, []), + (None, True, DEFAULT_LABELS[0], None, False, []), + (None, True, DEFAULT_LABELS[0], None, True, []), + ## FAR > threshold -------------------------------- + (DEFAULT_FAR_T*2.0, False, DEFAULT_LABELS[0], None, False, []), + (DEFAULT_FAR_T*2.0, False, DEFAULT_LABELS[0], None, True, []), + (DEFAULT_FAR_T*2.0, True, DEFAULT_LABELS[0], None, False, []), + (DEFAULT_FAR_T*2.0, True, DEFAULT_LABELS[0], None, True, []), + ## FAR < threshold -------------------------------- + (DEFAULT_FAR_T/2.0, False, DEFAULT_LABELS[0], None, False, []), + (DEFAULT_FAR_T/2.0, False, DEFAULT_LABELS[0], None, True, []), + (DEFAULT_FAR_T/2.0, True, DEFAULT_LABELS[0], None, False, []), + (DEFAULT_FAR_T/2.0, True, DEFAULT_LABELS[0], None, True, []), + # Label criteria met ------------------------------ + ## FAR = None ------------------------------------- + (None, False, DEFAULT_LABELS[0], DEFAULT_LABELS[1:], False, + ['labels_only']), + (None, False, DEFAULT_LABELS[0], DEFAULT_LABELS[1:], True, + ['labels_only', 'labels_and_gps']), + (None, True, DEFAULT_LABELS[0], DEFAULT_LABELS[1:], False, + ['nscand_and_labels', 'labels_only']), + (None, True, DEFAULT_LABELS[0], DEFAULT_LABELS[1:], True, + ['nscand_and_labels', 'labels_only', 'nscand_and_labels_and_gps', + 'labels_and_gps']), + ## FAR > threshold -------------------------------- + (DEFAULT_FAR_T*2.0, False, DEFAULT_LABELS[0], DEFAULT_LABELS[1:], False, + ['labels_only']), + (DEFAULT_FAR_T*2.0, False, DEFAULT_LABELS[0], DEFAULT_LABELS[1:], True, + ['labels_only', 'labels_and_gps']), + (DEFAULT_FAR_T*2.0, True, DEFAULT_LABELS[0], DEFAULT_LABELS[1:], False, + ['nscand_and_labels', 'labels_only']), + (DEFAULT_FAR_T*2.0, True, DEFAULT_LABELS[0], DEFAULT_LABELS[1:], True, + ['nscand_and_labels', 'labels_only', 'nscand_and_labels_and_gps', + 'labels_and_gps']), + ## FAR < threshold -------------------------------- + (DEFAULT_FAR_T/2.0, False, DEFAULT_LABELS[0], DEFAULT_LABELS[1:], False, + ['far_t_and_labels', 'labels_only']), + (DEFAULT_FAR_T/2.0, False, DEFAULT_LABELS[0], DEFAULT_LABELS[1:], True, + ['far_t_and_labels', 'labels_only', 'far_t_and_labels_and_gps', + 'labels_and_gps']), + (DEFAULT_FAR_T/2.0, True, DEFAULT_LABELS[0], DEFAULT_LABELS[1:], False, + ['far_t_and_labels', 'nscand_and_labels', 'labels_only', + 'far_t_and_nscand_and_labels']), + (DEFAULT_FAR_T/2.0, True, DEFAULT_LABELS[0], DEFAULT_LABELS[1:], True, + ['far_t_and_labels', 'nscand_and_labels', 'labels_only', + 'far_t_and_nscand_and_labels', 'far_t_and_labels_and_gps', + 'nscand_and_labels_and_gps', 'labels_and_gps', + 'far_t_and_nscand_and_labels_and_gps']), + # Label criteria met, some additional labels previously added + ## FAR = None ------------------------------------- + (None, False, DEFAULT_LABELS[0], DEFAULT_LABELS[1:] + [RANDOM_LABEL], + False, ['labels_only']), + (None, False, DEFAULT_LABELS[0], DEFAULT_LABELS[1:] + [RANDOM_LABEL], + True, ['labels_only', 'labels_and_gps']), + (None, True, DEFAULT_LABELS[0], DEFAULT_LABELS[1:] + [RANDOM_LABEL], + False, ['nscand_and_labels', 'labels_only']), + (None, True, DEFAULT_LABELS[0], DEFAULT_LABELS[1:] + [RANDOM_LABEL], + True, ['nscand_and_labels', 'labels_only', 'labels_and_gps', + 'nscand_and_labels_and_gps']), + ## FAR > threshold -------------------------------- + (DEFAULT_FAR_T*2.0, False, DEFAULT_LABELS[0], + DEFAULT_LABELS[1:] + [RANDOM_LABEL], False, ['labels_only']), + (DEFAULT_FAR_T*2.0, False, DEFAULT_LABELS[0], + DEFAULT_LABELS[1:] + [RANDOM_LABEL], True, + ['labels_only', 'labels_and_gps']), + (DEFAULT_FAR_T*2.0, True, DEFAULT_LABELS[0], + DEFAULT_LABELS[1:] + [RANDOM_LABEL], False, + ['nscand_and_labels', 'labels_only']), + (DEFAULT_FAR_T*2.0, True, DEFAULT_LABELS[0], + DEFAULT_LABELS[1:] + [RANDOM_LABEL], True, + ['nscand_and_labels', 'labels_only', 'labels_and_gps', + 'nscand_and_labels_and_gps']), + ## FAR < threshold -------------------------------- + (DEFAULT_FAR_T/2.0, False, DEFAULT_LABELS[0], + DEFAULT_LABELS[1:] + [RANDOM_LABEL], False, + ['far_t_and_labels', 'labels_only']), + (DEFAULT_FAR_T/2.0, False, DEFAULT_LABELS[0], + DEFAULT_LABELS[1:] + [RANDOM_LABEL], True, + ['far_t_and_labels', 'labels_only', 'labels_and_gps', + 'far_t_and_labels_and_gps']), + (DEFAULT_FAR_T/2.0, True, DEFAULT_LABELS[0], + DEFAULT_LABELS[1:] + [RANDOM_LABEL], False, + ['far_t_and_labels', 'nscand_and_labels', 'labels_only', + 'far_t_and_nscand_and_labels']), + (DEFAULT_FAR_T/2.0, True, DEFAULT_LABELS[0], + DEFAULT_LABELS[1:] + [RANDOM_LABEL], True, + ['far_t_and_labels', 'nscand_and_labels', 'labels_only', + 'far_t_and_nscand_and_labels', 'far_t_and_labels_and_gps', + 'nscand_and_labels_and_gps', 'labels_and_gps', + 'far_t_and_nscand_and_labels_and_gps']), + # Label criteria previously met ------------------- + ## FAR = None ------------------------------------- + (None, False, RANDOM_LABEL, DEFAULT_LABELS, False, []), + (None, False, RANDOM_LABEL, DEFAULT_LABELS, True, []), + (None, True, RANDOM_LABEL, DEFAULT_LABELS, False, []), + (None, True, RANDOM_LABEL, DEFAULT_LABELS, True, []), + ## FAR > threshold -------------------------------- + (DEFAULT_FAR_T*2.0, False, RANDOM_LABEL, DEFAULT_LABELS, False, []), + (DEFAULT_FAR_T*2.0, False, RANDOM_LABEL, DEFAULT_LABELS, True, []), + (DEFAULT_FAR_T*2.0, True, RANDOM_LABEL, DEFAULT_LABELS, False, []), + (DEFAULT_FAR_T*2.0, True, RANDOM_LABEL, DEFAULT_LABELS, True, []), + ## FAR < threshold -------------------------------- + (DEFAULT_FAR_T/2.0, False, RANDOM_LABEL, DEFAULT_LABELS, False, []), + (DEFAULT_FAR_T/2.0, False, RANDOM_LABEL, DEFAULT_LABELS, True, []), + (DEFAULT_FAR_T/2.0, True, RANDOM_LABEL, DEFAULT_LABELS, False, []), + (DEFAULT_FAR_T/2.0, True, RANDOM_LABEL, DEFAULT_LABELS, True, []), + # Label query criteria not met -------------------- + ## FAR = None ------------------------------------- + (None, False, DEFAULT_LABEL_QUERY['labels'][0], None, False, []), + (None, False, DEFAULT_LABEL_QUERY['labels'][0], None, True, []), + (None, True, DEFAULT_LABEL_QUERY['labels'][0], None, False, []), + (None, True, DEFAULT_LABEL_QUERY['labels'][0], None, True, []), + ## FAR > threshold -------------------------------- + (DEFAULT_FAR_T*2.0, False, DEFAULT_LABEL_QUERY['labels'][0], None, False, + []), + (DEFAULT_FAR_T*2.0, False, DEFAULT_LABEL_QUERY['labels'][0], None, True, + []), + (DEFAULT_FAR_T*2.0, True, DEFAULT_LABEL_QUERY['labels'][0], None, False, + []), + (DEFAULT_FAR_T*2.0, True, DEFAULT_LABEL_QUERY['labels'][0], None, True, + []), + ## FAR < threshold -------------------------------- + (DEFAULT_FAR_T/2.0, False, DEFAULT_LABEL_QUERY['labels'][0], None, False, + []), + (DEFAULT_FAR_T/2.0, False, DEFAULT_LABEL_QUERY['labels'][0], None, True, + []), + (DEFAULT_FAR_T/2.0, True, DEFAULT_LABEL_QUERY['labels'][0], None, False, + []), + (DEFAULT_FAR_T/2.0, True, DEFAULT_LABEL_QUERY['labels'][0], None, True, + []), + # Label query criteria met ------------------------- + ## FAR = None ------------------------------------- + (None, False, DEFAULT_LABEL_QUERY['labels'][0], + DEFAULT_LABEL_QUERY['labels'][1:], False, ['labelq_only']), + (None, False, DEFAULT_LABEL_QUERY['labels'][0], + DEFAULT_LABEL_QUERY['labels'][1:], True, + ['labelq_only', 'labelq_and_gps']), + (None, True, DEFAULT_LABEL_QUERY['labels'][0], + DEFAULT_LABEL_QUERY['labels'][1:], False, + ['nscand_and_labelq', 'labelq_only']), + (None, True, DEFAULT_LABEL_QUERY['labels'][0], + DEFAULT_LABEL_QUERY['labels'][1:], True, + ['nscand_and_labelq', 'labelq_only', 'labelq_and_gps', + 'nscand_and_labelq_and_gps']), + ## FAR > threshold -------------------------------- + (DEFAULT_FAR_T*2.0, False, DEFAULT_LABEL_QUERY['labels'][0], + DEFAULT_LABEL_QUERY['labels'][1:], False, ['labelq_only']), + (DEFAULT_FAR_T*2.0, False, DEFAULT_LABEL_QUERY['labels'][0], + DEFAULT_LABEL_QUERY['labels'][1:], True, + ['labelq_only', 'labelq_and_gps']), + (DEFAULT_FAR_T*2.0, True, DEFAULT_LABEL_QUERY['labels'][0], + DEFAULT_LABEL_QUERY['labels'][1:], False, + ['nscand_and_labelq', 'labelq_only']), + (DEFAULT_FAR_T*2.0, True, DEFAULT_LABEL_QUERY['labels'][0], + DEFAULT_LABEL_QUERY['labels'][1:], True, + ['nscand_and_labelq', 'labelq_only', 'labelq_and_gps', + 'nscand_and_labelq_and_gps']), + ## FAR < threshold -------------------------------- + (DEFAULT_FAR_T/2.0, False, DEFAULT_LABEL_QUERY['labels'][0], + DEFAULT_LABEL_QUERY['labels'][1:], False, + ['far_t_and_labelq', 'labelq_only']), + (DEFAULT_FAR_T/2.0, False, DEFAULT_LABEL_QUERY['labels'][0], + DEFAULT_LABEL_QUERY['labels'][1:], True, + ['far_t_and_labelq', 'labelq_only', 'labelq_and_gps', + 'far_t_and_labelq_and_gps']), + (DEFAULT_FAR_T/2.0, True, DEFAULT_LABEL_QUERY['labels'][0], + DEFAULT_LABEL_QUERY['labels'][1:], False, + ['far_t_and_labelq', 'nscand_and_labelq', 'labelq_only', + 'far_t_and_nscand_and_labelq']), + (DEFAULT_FAR_T/2.0, True, DEFAULT_LABEL_QUERY['labels'][0], + DEFAULT_LABEL_QUERY['labels'][1:], True, + ['far_t_and_labelq', 'nscand_and_labelq', 'labelq_only', + 'far_t_and_nscand_and_labelq', 'far_t_and_labelq_and_gps', + 'nscand_and_labelq_and_gps', 'labelq_and_gps', + 'far_t_and_nscand_and_labelq_and_gps']), + # Label query criteria met, some additional labels previously added + ## FAR = None ------------------------------------- + (None, False, DEFAULT_LABEL_QUERY['labels'][0], + DEFAULT_LABEL_QUERY['labels'][1:] + [RANDOM_LABEL], False, + ['labelq_only']), + (None, False, DEFAULT_LABEL_QUERY['labels'][0], + DEFAULT_LABEL_QUERY['labels'][1:] + [RANDOM_LABEL], True, + ['labelq_only', 'labelq_and_gps']), + (None, True, DEFAULT_LABEL_QUERY['labels'][0], + DEFAULT_LABEL_QUERY['labels'][1:] + [RANDOM_LABEL], False, + ['nscand_and_labelq', 'labelq_only']), + (None, True, DEFAULT_LABEL_QUERY['labels'][0], + DEFAULT_LABEL_QUERY['labels'][1:] + [RANDOM_LABEL], True, + ['nscand_and_labelq', 'labelq_only', 'labelq_and_gps', + 'nscand_and_labelq_and_gps']), + ## FAR > threshold -------------------------------- + (DEFAULT_FAR_T*2.0, False, DEFAULT_LABEL_QUERY['labels'][0], + DEFAULT_LABEL_QUERY['labels'][1:] + [RANDOM_LABEL], False, + ['labelq_only']), + (DEFAULT_FAR_T*2.0, False, DEFAULT_LABEL_QUERY['labels'][0], + DEFAULT_LABEL_QUERY['labels'][1:] + [RANDOM_LABEL], True, + ['labelq_only', 'labelq_and_gps']), + (DEFAULT_FAR_T*2.0, True, DEFAULT_LABEL_QUERY['labels'][0], + DEFAULT_LABEL_QUERY['labels'][1:] + [RANDOM_LABEL], False, + ['nscand_and_labelq', 'labelq_only']), + (DEFAULT_FAR_T*2.0, True, DEFAULT_LABEL_QUERY['labels'][0], + DEFAULT_LABEL_QUERY['labels'][1:] + [RANDOM_LABEL], True, + ['nscand_and_labelq', 'labelq_only', 'labelq_and_gps', + 'nscand_and_labelq_and_gps']), + ## FAR < threshold -------------------------------- + (DEFAULT_FAR_T/2.0, False, DEFAULT_LABEL_QUERY['labels'][0], + DEFAULT_LABEL_QUERY['labels'][1:] + [RANDOM_LABEL], False, + ['far_t_and_labelq', 'labelq_only']), + (DEFAULT_FAR_T/2.0, False, DEFAULT_LABEL_QUERY['labels'][0], + DEFAULT_LABEL_QUERY['labels'][1:] + [RANDOM_LABEL], True, + ['far_t_and_labelq', 'labelq_only', 'labelq_and_gps', + 'far_t_and_labelq_and_gps']), + (DEFAULT_FAR_T/2.0, True, DEFAULT_LABEL_QUERY['labels'][0], + DEFAULT_LABEL_QUERY['labels'][1:] + [RANDOM_LABEL], False, + ['far_t_and_labelq', 'nscand_and_labelq', 'labelq_only', + 'far_t_and_nscand_and_labelq']), + (DEFAULT_FAR_T/2.0, True, DEFAULT_LABEL_QUERY['labels'][0], + DEFAULT_LABEL_QUERY['labels'][1:] + [RANDOM_LABEL], True, + ['far_t_and_labelq', 'nscand_and_labelq', 'labelq_only', + 'far_t_and_nscand_and_labelq', 'far_t_and_labelq_and_gps', + 'nscand_and_labelq_and_gps', 'labelq_and_gps', + 'far_t_and_nscand_and_labelq_and_gps']), + # Label query criteria previously met ------------- + ## FAR = None ------------------------------------- + (None, False, RANDOM_LABEL, DEFAULT_LABEL_QUERY['labels'], False, []), + (None, False, RANDOM_LABEL, DEFAULT_LABEL_QUERY['labels'], True, []), + (None, True, RANDOM_LABEL, DEFAULT_LABEL_QUERY['labels'], False, []), + (None, True, RANDOM_LABEL, DEFAULT_LABEL_QUERY['labels'], True, []), + ## FAR > threshold -------------------------------- + (DEFAULT_FAR_T*2.0, False, RANDOM_LABEL, DEFAULT_LABEL_QUERY['labels'], + False, []), + (DEFAULT_FAR_T*2.0, False, RANDOM_LABEL, DEFAULT_LABEL_QUERY['labels'], + True, []), + (DEFAULT_FAR_T*2.0, True, RANDOM_LABEL, DEFAULT_LABEL_QUERY['labels'], + False, []), + (DEFAULT_FAR_T*2.0, True, RANDOM_LABEL, DEFAULT_LABEL_QUERY['labels'], + True, []), + ## FAR < threshold -------------------------------- + (DEFAULT_FAR_T/2.0, False, RANDOM_LABEL, DEFAULT_LABEL_QUERY['labels'], + False, []), + (DEFAULT_FAR_T/2.0, False, RANDOM_LABEL, DEFAULT_LABEL_QUERY['labels'], + True, []), + (DEFAULT_FAR_T/2.0, True, RANDOM_LABEL, DEFAULT_LABEL_QUERY['labels'], + False, []), + (DEFAULT_FAR_T/2.0, True, RANDOM_LABEL, DEFAULT_LABEL_QUERY['labels'], + True, []), +] + +SUPEREVENT_LABEL_REMOVED_ALERT_DATA = [ + # Label criteria not met -------------------------- + ## FAR = None ------------------------------------- + (None, False, RANDOM_LABEL, None, []), + (None, True, RANDOM_LABEL, None, []), + ## FAR > threshold -------------------------------- + (DEFAULT_FAR_T*2.0, False, RANDOM_LABEL, None, []), + (DEFAULT_FAR_T*2.0, True, RANDOM_LABEL, None, []), + ## FAR < threshold -------------------------------- + (DEFAULT_FAR_T/2.0, False, RANDOM_LABEL, None, []), + (DEFAULT_FAR_T/2.0, True, RANDOM_LABEL, None, []), + # Label criteria met ------------------------------ + ## FAR = None ------------------------------------- + (None, False, 'L7', ['L5', 'L6'], ['labelq2_only']), + (None, True, 'L7', ['L5', 'L6'], + ['nscand_and_labelq2', 'labelq2_only']), + ## FAR > threshold -------------------------------- + (DEFAULT_FAR_T*2.0, False, 'L7', ['L5', 'L6'], + ['labelq2_only']), + (DEFAULT_FAR_T*2.0, True, 'L7', ['L5', 'L6'], + ['nscand_and_labelq2', 'labelq2_only']), + ## FAR < threshold -------------------------------- + (DEFAULT_FAR_T/2.0, False, 'L7', ['L5', 'L6'], + ['far_t_and_labelq2', 'labelq2_only']), + (DEFAULT_FAR_T/2.0, True, 'L7', ['L5', 'L6'], + ['far_t_and_labelq2', 'nscand_and_labelq2', 'labelq2_only', + 'far_t_and_nscand_and_labelq2']), + # Label criteria met, some additional labels previously added + ## FAR = None ------------------------------------- + (None, False, 'L7', ['L5', 'L6', RANDOM_LABEL], + ['labelq2_only']), + (None, True, 'L7', ['L5', 'L6', RANDOM_LABEL], + ['nscand_and_labelq2', 'labelq2_only']), + ## FAR > threshold -------------------------------- + (DEFAULT_FAR_T*2.0, False, 'L7', ['L5', 'L6', RANDOM_LABEL], + ['labelq2_only']), + (DEFAULT_FAR_T*2.0, True, 'L7', ['L5', 'L6', RANDOM_LABEL], + ['nscand_and_labelq2', 'labelq2_only']), + ## FAR < threshold -------------------------------- + (DEFAULT_FAR_T/2.0, False, 'L7', ['L5', 'L6', RANDOM_LABEL], + ['far_t_and_labelq2', 'labelq2_only']), + (DEFAULT_FAR_T/2.0, True, 'L7', ['L5', 'L6', RANDOM_LABEL], + ['far_t_and_labelq2', 'nscand_and_labelq2', 'labelq2_only', + 'far_t_and_nscand_and_labelq2']), + # Label criteria previously met ------------------- + ## FAR = None ------------------------------------- + (None, False, RANDOM_LABEL, ['L5', 'L6'], []), + (None, True, RANDOM_LABEL, ['L5', 'L6'], []), + ## FAR > threshold -------------------------------- + (DEFAULT_FAR_T*2.0, False, RANDOM_LABEL, ['L5', 'L6'], []), + (DEFAULT_FAR_T*2.0, True, RANDOM_LABEL, ['L5', 'L6'], []), + ## FAR < threshold -------------------------------- + (DEFAULT_FAR_T/2.0, False, RANDOM_LABEL, ['L5', 'L6'], []), + (DEFAULT_FAR_T/2.0, True, RANDOM_LABEL, ['L5', 'L6'], []), +] + +# Params: far,nscand,removed_label,labels,use_default_gps,notif_descs +EVENT_LABEL_REMOVED_ALERT_DATA = [ + # Label criteria not met -------------------------- + ## FAR = None ------------------------------------- + (None, False, RANDOM_LABEL, None, False, []), + (None, False, RANDOM_LABEL, None, True, []), + (None, True, RANDOM_LABEL, None, False, []), + (None, True, RANDOM_LABEL, None, True, []), + ## FAR > threshold -------------------------------- + (DEFAULT_FAR_T*2.0, False, RANDOM_LABEL, None, False, []), + (DEFAULT_FAR_T*2.0, False, RANDOM_LABEL, None, True, []), + (DEFAULT_FAR_T*2.0, True, RANDOM_LABEL, None, False, []), + (DEFAULT_FAR_T*2.0, True, RANDOM_LABEL, None, True, []), + ## FAR < threshold -------------------------------- + (DEFAULT_FAR_T/2.0, False, RANDOM_LABEL, None, False, []), + (DEFAULT_FAR_T/2.0, False, RANDOM_LABEL, None, True, []), + (DEFAULT_FAR_T/2.0, True, RANDOM_LABEL, None, False, []), + (DEFAULT_FAR_T/2.0, True, RANDOM_LABEL, None, True, []), + # Label criteria met ------------------------------ + ## FAR = None ------------------------------------- + (None, False, 'L7', ['L5', 'L6'], False, ['labelq2_only']), + (None, False, 'L7', ['L5', 'L6'], True, + ['labelq2_only', 'labelq2_and_gps']), + (None, True, 'L7', ['L5', 'L6'], False, + ['nscand_and_labelq2', 'labelq2_only']), + (None, True, 'L7', ['L5', 'L6'], True, + ['nscand_and_labelq2', 'labelq2_only', 'labelq2_and_gps', + 'nscand_and_labelq2_and_gps']), + ## FAR > threshold -------------------------------- + (DEFAULT_FAR_T*2.0, False, 'L7', ['L5', 'L6'], False, + ['labelq2_only']), + (DEFAULT_FAR_T*2.0, False, 'L7', ['L5', 'L6'], True, + ['labelq2_only', 'labelq2_and_gps']), + (DEFAULT_FAR_T*2.0, True, 'L7', ['L5', 'L6'], False, + ['nscand_and_labelq2', 'labelq2_only']), + (DEFAULT_FAR_T*2.0, True, 'L7', ['L5', 'L6'], True, + ['nscand_and_labelq2', 'labelq2_only', 'labelq2_and_gps', + 'nscand_and_labelq2_and_gps']), + ## FAR < threshold -------------------------------- + (DEFAULT_FAR_T/2.0, False, 'L7', ['L5', 'L6'], False, + ['far_t_and_labelq2', 'labelq2_only']), + (DEFAULT_FAR_T/2.0, False, 'L7', ['L5', 'L6'], True, + ['far_t_and_labelq2', 'labelq2_only', 'labelq2_and_gps', + 'far_t_and_labelq2_and_gps']), + (DEFAULT_FAR_T/2.0, True, 'L7', ['L5', 'L6'], False, + ['far_t_and_labelq2', 'nscand_and_labelq2', 'labelq2_only', + 'far_t_and_nscand_and_labelq2']), + (DEFAULT_FAR_T/2.0, True, 'L7', ['L5', 'L6'], True, + ['far_t_and_labelq2', 'nscand_and_labelq2', 'labelq2_only', + 'far_t_and_nscand_and_labelq2', 'labelq2_and_gps', + 'far_t_and_labelq2_and_gps', 'nscand_and_labelq2_and_gps', + 'far_t_and_nscand_and_labelq2_and_gps']), + # Label criteria met, some additional labels previously added + ## FAR = None ------------------------------------- + (None, False, 'L7', ['L5', 'L6', RANDOM_LABEL], False, + ['labelq2_only']), + (None, False, 'L7', ['L5', 'L6', RANDOM_LABEL], True, + ['labelq2_only', 'labelq2_and_gps',]), + (None, True, 'L7', ['L5', 'L6', RANDOM_LABEL], False, + ['nscand_and_labelq2', 'labelq2_only']), + (None, True, 'L7', ['L5', 'L6', RANDOM_LABEL], True, + ['nscand_and_labelq2', 'labelq2_only', 'labelq2_and_gps', + 'nscand_and_labelq2_and_gps']), + ## FAR > threshold -------------------------------- + (DEFAULT_FAR_T*2.0, False, 'L7', ['L5', 'L6', RANDOM_LABEL], False, + ['labelq2_only']), + (DEFAULT_FAR_T*2.0, False, 'L7', ['L5', 'L6', RANDOM_LABEL], True, + ['labelq2_only', 'labelq2_and_gps']), + (DEFAULT_FAR_T*2.0, True, 'L7', ['L5', 'L6', RANDOM_LABEL], False, + ['nscand_and_labelq2', 'labelq2_only']), + (DEFAULT_FAR_T*2.0, True, 'L7', ['L5', 'L6', RANDOM_LABEL], True, + ['nscand_and_labelq2', 'labelq2_only', 'labelq2_and_gps', + 'nscand_and_labelq2_and_gps']), + ## FAR < threshold -------------------------------- + (DEFAULT_FAR_T/2.0, False, 'L7', ['L5', 'L6', RANDOM_LABEL], False, + ['far_t_and_labelq2', 'labelq2_only']), + (DEFAULT_FAR_T/2.0, False, 'L7', ['L5', 'L6', RANDOM_LABEL], True, + ['far_t_and_labelq2', 'labelq2_only', 'labelq2_and_gps', + 'far_t_and_labelq2_and_gps']), + (DEFAULT_FAR_T/2.0, True, 'L7', ['L5', 'L6', RANDOM_LABEL], False, + ['far_t_and_labelq2', 'nscand_and_labelq2', 'labelq2_only', + 'far_t_and_nscand_and_labelq2']), + (DEFAULT_FAR_T/2.0, True, 'L7', ['L5', 'L6', RANDOM_LABEL], True, + ['far_t_and_labelq2', 'nscand_and_labelq2', 'labelq2_only', + 'far_t_and_nscand_and_labelq2', 'labelq2_and_gps', + 'far_t_and_labelq2_and_gps', 'nscand_and_labelq2_and_gps', + 'far_t_and_nscand_and_labelq2_and_gps']), + # Label criteria previously met ------------------- + ## FAR = None ------------------------------------- + (None, False, RANDOM_LABEL, ['L5', 'L6'], False, []), + (None, False, RANDOM_LABEL, ['L5', 'L6'], True, []), + (None, True, RANDOM_LABEL, ['L5', 'L6'], False, []), + (None, True, RANDOM_LABEL, ['L5', 'L6'], True, []), + ## FAR > threshold -------------------------------- + (DEFAULT_FAR_T*2.0, False, RANDOM_LABEL, ['L5', 'L6'], False, []), + (DEFAULT_FAR_T*2.0, False, RANDOM_LABEL, ['L5', 'L6'], True, []), + (DEFAULT_FAR_T*2.0, True, RANDOM_LABEL, ['L5', 'L6'], False, []), + (DEFAULT_FAR_T*2.0, True, RANDOM_LABEL, ['L5', 'L6'], True, []), + ## FAR < threshold -------------------------------- + (DEFAULT_FAR_T/2.0, False, RANDOM_LABEL, ['L5', 'L6'], False, []), + (DEFAULT_FAR_T/2.0, False, RANDOM_LABEL, ['L5', 'L6'], True, []), + (DEFAULT_FAR_T/2.0, True, RANDOM_LABEL, ['L5', 'L6'], False, []), + (DEFAULT_FAR_T/2.0, True, RANDOM_LABEL, ['L5', 'L6'], True, []), +] + + +############################################################################### +# TESTS ####################################################################### +############################################################################### +# Superevent tests ------------------------------------------------------------ +@pytest.mark.parametrize("far,nscand,notif_descs", + SUPEREVENT_CREATION_ALERT_DATA) +@pytest.mark.django_db +def test_superevent_creation_alerts( + superevent, superevent_notifications, + far, nscand, notif_descs, +): + + # Set up superevent state + superevent.preferred_event.far = far + superevent.preferred_event.is_ns_candidate = \ + types.MethodType(lambda self: nscand, superevent.preferred_event) + + # Set up recipient getter + recipient_getter = CreationRecipientGetter(superevent) + recipient_getter.queryset = superevent_notifications + + # Get notifications + matched_notifications = recipient_getter.get_notifications() + + # Test results + for desc in notif_descs: + assert matched_notifications.filter(description=desc).exists() + assert matched_notifications.count() == len(notif_descs) + + +@pytest.mark.parametrize("old_far,far,old_nscand,nscand,labels,notif_descs", + SUPEREVENT_UPDATE_ALERT_DATA) +@pytest.mark.django_db +def test_superevent_update_alerts( + superevent, superevent_notifications, + old_far, far, old_nscand, nscand, labels, notif_descs, +): + + # Set up superevent state + superevent.preferred_event.far = far + superevent.preferred_event.is_ns_candidate = \ + types.MethodType(lambda self: nscand, superevent.preferred_event) + if labels is not None: + for label_name in labels: + label, _ = Label.objects.get_or_create(name=label_name) + superevent.labelling_set.create(label=label, + creator=superevent.submitter) + + # Set up recipient getter + recipient_getter = UpdateRecipientGetter(superevent, old_far=old_far, + old_nscand=old_nscand) + recipient_getter.queryset = superevent_notifications + + # Get notifications + matched_notifications = recipient_getter.get_notifications() + + # Test results + assert matched_notifications.count() == len(notif_descs) + for desc in notif_descs: + assert matched_notifications.filter(description=desc).exists() + + +@pytest.mark.parametrize("far,nscand,new_label,old_labels,notif_descs", + SUPEREVENT_LABEL_ADDED_ALERT_DATA) +@pytest.mark.django_db +def test_superevent_label_added_alerts( + superevent, superevent_notifications, + far, nscand, new_label, old_labels, notif_descs, +): + + # Set up superevent state + superevent.preferred_event.far = far + superevent.preferred_event.is_ns_candidate = \ + types.MethodType(lambda self: nscand, superevent.preferred_event) + if old_labels is not None: + for label_name in old_labels: + label, _ = Label.objects.get_or_create(name=label_name) + superevent.labelling_set.create(label=label, + creator=superevent.submitter) + # Add new label + label_obj, _ = Label.objects.get_or_create(name=new_label) + superevent.labelling_set.create(label=label_obj, + creator=superevent.submitter) + + # Set up recipient getter + recipient_getter = LabelAddedRecipientGetter(superevent, label=label_obj) + recipient_getter.queryset = superevent_notifications + + # Get notifications + matched_notifications = recipient_getter.get_notifications() + + # Test results + assert matched_notifications.count() == len(notif_descs) + for desc in notif_descs: + assert matched_notifications.filter(description=desc).exists() + + +@pytest.mark.parametrize("far,nscand,removed_label,labels,notif_descs", + SUPEREVENT_LABEL_REMOVED_ALERT_DATA) +@pytest.mark.django_db +def test_superevent_label_removed_alerts( + superevent, superevent_notifications, + far, nscand, removed_label, labels, notif_descs, +): + # NOTE: labels being removed should never result in alerts for + # notifications which specify a label criteria (instead of a label query) + + # Set up superevent state + superevent.preferred_event.far = far + superevent.preferred_event.is_ns_candidate = \ + types.MethodType(lambda self: nscand, superevent.preferred_event) + if labels is not None: + for label_name in labels: + label, _ = Label.objects.get_or_create(name=label_name) + superevent.labelling_set.create(label=label, + creator=superevent.submitter) + + # Add new label + label_obj, _ = Label.objects.get_or_create(name=removed_label) + + # Set up recipient getter + recipient_getter = LabelRemovedRecipientGetter(superevent, label=label_obj) + recipient_getter.queryset = superevent_notifications + + # Get notifications + matched_notifications = recipient_getter.get_notifications() + + # Test results + assert matched_notifications.count() == len(notif_descs) + for desc in notif_descs: + assert matched_notifications.filter(description=desc).exists() + + +# Event tests ----------------------------------------------------------------- +@pytest.mark.parametrize("far,nscand,use_default_gps,notif_descs", + EVENT_CREATION_ALERT_DATA) +@pytest.mark.django_db +def test_event_creation_alerts( + event, event_notifications, far, nscand, use_default_gps, notif_descs, +): + + # Set up event state + event.far = far + event.is_ns_candidate = types.MethodType(lambda self: nscand, event) + if use_default_gps: + event.group = Group.objects.get(name=DEFAULT_GROUP) + event.pipeline = Pipeline.objects.get(name=DEFAULT_PIPELINE) + event.search = Search.objects.get(name=DEFAULT_SEARCH) + event.save() + + # Set up recipient getter + recipient_getter = CreationRecipientGetter(event) + recipient_getter.queryset = event_notifications + + # Get notifications + matched_notifications = recipient_getter.get_notifications() + + # Test results + for desc in notif_descs: + assert matched_notifications.filter(description=desc).exists() + assert matched_notifications.count() == len(notif_descs) + + +@pytest.mark.parametrize( + "old_far,far,old_nscand,nscand,use_default_gps,labels,notif_descs", + EVENT_UPDATE_ALERT_DATA ) -@mock.patch('alerts.main.issue_phone_alerts') -@mock.patch('alerts.main.issue_email_alerts') -class TestSupereventRecipients(GraceDbTestBase, SupereventCreateMixin): - - @classmethod - def setUpTestData(cls): - super(TestSupereventRecipients, cls).setUpTestData() - - # Create a superevent - cls.superevent = cls.create_superevent(cls.internal_user, - 'fake_group', 'fake_pipeline', 'fake_search') - - # References to cls/self.event will refer to the superevent's - # preferred event - cls.event = cls.superevent.preferred_event - - # Create a bunch of notifications - cls.far_thresh = 0.01 - cls.notification_dict = {} - cls.notification_dict['basic'] = Notification.objects.create( - user=cls.internal_user, description='basic', - category=Notification.NOTIFICATION_CATEGORY_SUPEREVENT) - cls.notification_dict['far'] = Notification.objects.create( - user=cls.internal_user, description='far', - far_threshold=cls.far_thresh, - category=Notification.NOTIFICATION_CATEGORY_SUPEREVENT) - cls.notification_dict['nscand'] = Notification.objects.create( - user=cls.internal_user, description='nscand', - ns_candidate=True, - category=Notification.NOTIFICATION_CATEGORY_SUPEREVENT) - cls.notification_dict['far_nscand'] = Notification.objects.create( - user=cls.internal_user, description='far_nscand', - far_threshold=cls.far_thresh, ns_candidate=True, - category=Notification.NOTIFICATION_CATEGORY_SUPEREVENT) - cls.notification_dict['labels'] = Notification.objects.create( - user=cls.internal_user, description='labels', - category=Notification.NOTIFICATION_CATEGORY_SUPEREVENT) - cls.notification_dict['far_labels'] = Notification.objects.create( - user=cls.internal_user, description='far_labels', - far_threshold=cls.far_thresh, - category=Notification.NOTIFICATION_CATEGORY_SUPEREVENT) - cls.notification_dict['nscand_labels'] = Notification.objects.create( - user=cls.internal_user, description='nscand_labels', - ns_candidate=True, - category=Notification.NOTIFICATION_CATEGORY_SUPEREVENT) - cls.notification_dict['far_nscand_labels'] = \ - Notification.objects.create(user=cls.internal_user, - description='far_nscand_labels', far_threshold=cls.far_thresh, - ns_candidate=True, - category=Notification.NOTIFICATION_CATEGORY_SUPEREVENT) - cls.notification_dict['labelq'] = Notification.objects.create( - label_query='TEST_LABEL3 & ~TEST_LABEL4', - user=cls.internal_user, description='labelq', - category=Notification.NOTIFICATION_CATEGORY_SUPEREVENT) - cls.notification_dict['far_labelq'] = Notification.objects.create( - user=cls.internal_user, description='far_labelq', - far_threshold=cls.far_thresh, - label_query='TEST_LABEL3 & ~TEST_LABEL4', - category=Notification.NOTIFICATION_CATEGORY_SUPEREVENT) - cls.notification_dict['nscand_labelq'] = Notification.objects.create( - user=cls.internal_user, description='nscand_labelq', - ns_candidate=True, label_query='TEST_LABEL3 & ~TEST_LABEL4', - category=Notification.NOTIFICATION_CATEGORY_SUPEREVENT) - cls.notification_dict['far_nscand_labelq'] = \ - Notification.objects.create(user=cls.internal_user, - description='far_nscand_labelq', far_threshold=cls.far_thresh, - ns_candidate=True, label_query='TEST_LABEL3 & ~TEST_LABEL4', - category=Notification.NOTIFICATION_CATEGORY_SUPEREVENT) - - # Add label stuff - cls.label1, _ = Label.objects.get_or_create(name='TEST_LABEL1') - cls.label2, _ = Label.objects.get_or_create(name='TEST_LABEL2') - cls.label3, _ = Label.objects.get_or_create(name='TEST_LABEL3') - cls.label4, _ = Label.objects.get_or_create(name='TEST_LABEL4') - for k in cls.notification_dict: - if 'labels' in k: - cls.notification_dict[k].labels.add(cls.label1) - cls.notification_dict[k].labels.add(cls.label2) - elif 'labelq' in k: - cls.notification_dict[k].labels.add(cls.label3) - cls.notification_dict[k].labels.add(cls.label4) - - # Create an email and phone contact for each notification - for k in cls.notification_dict: - n = cls.notification_dict[k] - n.contacts.create(user=cls.internal_user, - description=n.description, email='test@test.com', - verified=True) - n.contacts.create(user=cls.internal_user, - description=n.description, phone='12345678901', - phone_method=Contact.CONTACT_PHONE_BOTH, verified=True) - - def test_new(self, email_mock, phone_mock): - """Test alerts for superevent creation - no FAR, no NSCAND""" - SupereventAlertIssuer(self.superevent, alert_type='new').issue_alerts() - - # Check recipients passed to alert functions - email_recips = email_mock.call_args[0][2] - phone_recips = phone_mock.call_args[0][2] - - # Should just be the "basic" notification being triggered - self.assertEqual(email_recips.count(), 1) - self.assertEqual(phone_recips.count(), 1) - self.assertEqual(email_recips.first().description, 'basic') - self.assertEqual(phone_recips.first().description, 'basic') - - def test_new_with_far(self, email_mock, phone_mock): - """Test alerts for superevent creation with FAR""" - # Add FAR to preferred event - self.event.far = 1e-10 - self.event.save() - - # Create a new notification with too low of a FAR threshold - n = Notification.objects.create(user=self.internal_user, - description='far_low', far_threshold=1e-20, - category=Notification.NOTIFICATION_CATEGORY_SUPEREVENT) - n.contacts.create(user=self.internal_user, - description=n.description, email='test@test.com', - verified=True) - n.contacts.create(user=self.internal_user, - description=n.description, phone='12345678901', - phone_method=Contact.CONTACT_PHONE_BOTH, verified=True) - - # Issue alerts - SupereventAlertIssuer(self.superevent, alert_type='new').issue_alerts() - - # Check recipients passed to alert functions - email_recips = email_mock.call_args[0][2] - phone_recips = phone_mock.call_args[0][2] - - # Basic and FAR alerts should be triggered, but not the "new" FAR - # one we defined with a really low threshold - for recips in [email_recips, phone_recips]: - self.assertEqual(recips.count(), 2) - for r in recips: - self.assertIn(r.description, ['basic', 'far']) - - # Ensure that "new" FAR alert is not in the lists - self.assertFalse(email_recips.filter( - description=n.description).exists()) - self.assertFalse(phone_recips.filter( - description=n.description).exists()) - - def test_new_with_nscand(self, email_mock, phone_mock): - """Test alerts for superevent creation with NS candidate""" - # Add NSCAND to preferred event - self.event.singleinspiral_set.create(mass1=3, mass2=1) - - # Issue alerts - SupereventAlertIssuer(self.superevent, alert_type='new').issue_alerts() - - # Check recipients passed to alert functions - email_recips = email_mock.call_args[0][2] - phone_recips = phone_mock.call_args[0][2] - - # Only basic and NSCAND alerts should be triggered - for recips in [email_recips, phone_recips]: - self.assertEqual(recips.count(), 2) - for r in recips: - self.assertIn(r.description, ['basic', 'nscand']) - - def test_new_with_far_nscand(self, email_mock, phone_mock): - """Test alerts for superevent creation with FAR and NS candidate""" - # Add FAR to preferred event - self.event.far = 1e-10 - self.event.save() - # Add NSCAND to preferred event - self.event.singleinspiral_set.create(mass1=3, mass2=1) - - # Create a new notification with too low of a FAR threshold - n = Notification.objects.create(user=self.internal_user, - description='far_low', far_threshold=1e-20, - category=Notification.NOTIFICATION_CATEGORY_SUPEREVENT) - n.contacts.create(user=self.internal_user, - description=n.description, email='test@test.com', - verified=True) - n.contacts.create(user=self.internal_user, - description=n.description, phone='12345678901', - phone_method=Contact.CONTACT_PHONE_BOTH, verified=True) - - # Issue alerts - SupereventAlertIssuer(self.superevent, alert_type='new').issue_alerts() - - # Check recipients passed to alert functions - email_recips = email_mock.call_args[0][2] - phone_recips = phone_mock.call_args[0][2] - - # Basic and FAR alerts should be triggered, but not the "new" FAR - # one we defined with a really low threshold - for recips in [email_recips, phone_recips]: - self.assertEqual(recips.count(), 4) - for r in recips: - self.assertIn(r.description, - ['basic', 'far', 'nscand', 'far_nscand']) - - # Ensure that "new" FAR alert is not in the lists - self.assertFalse(email_recips.filter( - description=n.description).exists()) - self.assertFalse(phone_recips.filter( - description=n.description).exists()) - - def test_update_with_no_change(self, email_mock, phone_mock): - """Test alerts for superevent update with no FAR or NSCAND change""" - # Issue alerts - SupereventAlertIssuer(self.superevent, alert_type='update') \ - .issue_alerts() - - # In this case, no recipients should match so the alert functions - # are not even called - email_mock.assert_not_called() - phone_mock.assert_not_called() - - def test_update_with_same_far(self, email_mock, phone_mock): - """Test alerts for superevent update with no FAR or NSCAND change""" - # Add FAR to preferred event - self.event.far = 1e-10 - self.event.save() - - # Issue alerts - SupereventAlertIssuer(self.superevent, alert_type='update') \ - .issue_alerts(old_far=self.event.far) - - # In this case, no recipients should match so the alert functions - # are not even called - email_mock.assert_not_called() - phone_mock.assert_not_called() - - def test_update_with_lower_far(self, email_mock, phone_mock): - """Test alerts for superevent update with lower FAR""" - # Add FAR to preferred event - self.event.far = 1e-10 - self.event.save() - - # Create a new notification with too low of a FAR threshold - n_low = Notification.objects.create(user=self.internal_user, - description='far_low', far_threshold=1e-20, - category=Notification.NOTIFICATION_CATEGORY_SUPEREVENT) - n_low.contacts.create(user=self.internal_user, - description=n_low.description, email='test@test.com', - verified=True) - n_low.contacts.create(user=self.internal_user, - description=n_low.description, phone='12345678901', - phone_method=Contact.CONTACT_PHONE_BOTH, verified=True) - - # Create a new notification with too high of a FAR threshold - n_high = Notification.objects.create(user=self.internal_user, - description='far_high', far_threshold=1, - category=Notification.NOTIFICATION_CATEGORY_SUPEREVENT) - n_high.contacts.create(user=self.internal_user, - description=n_high.description, email='test@test.com', - verified=True) - n_high.contacts.create(user=self.internal_user, - description=n_high.description, phone='12345678901', - phone_method=Contact.CONTACT_PHONE_BOTH, verified=True) - - # Issue alerts - SupereventAlertIssuer(self.superevent, alert_type='update') \ - .issue_alerts(old_far=self.far_thresh*2) - - # Check recipients passed to alert functions - email_recips = email_mock.call_args[0][2] - phone_recips = phone_mock.call_args[0][2] - - # Only the original 'far' alert should be triggered and not the - # n_low or n_high that we defined here - self.assertEqual(email_recips.count(), 1) - self.assertEqual(phone_recips.count(), 1) - self.assertEqual(email_recips.first().description, 'far') - self.assertEqual(phone_recips.first().description, 'far') - - def test_update_with_new_preferred_event_no_far(self, email_mock, - phone_mock): - """ - Test alerts for superevent update with new preferred_event with - no FAR - """ - # Update preferred event - ev = self.create_event('fake_group', 'fake_pipeline', - 'fake_search', user=self.internal_user) - self.superevent.preferred_event = ev - - # Issue alerts - SupereventAlertIssuer(self.superevent, alert_type='update') \ - .issue_alerts(old_far=self.event.far) - - # In this case, no recipients should match so the alert functions - # are not even called - email_mock.assert_not_called() - phone_mock.assert_not_called() - - def test_update_with_new_preferred_event_same_far(self, email_mock, - phone_mock): - """ - Test alerts for superevent update with new preferred_event with - same FAR - """ - # Update preferred event - ev = self.create_event('fake_group', 'fake_pipeline', - 'fake_search', user=self.internal_user) - ev.far = 1e-10 - ev.save() - self.superevent.preferred_event = ev - - # Add FAR to old preferred event - self.event.far = 1e-10 - self.event.save() - - # Issue alerts - SupereventAlertIssuer(self.superevent, alert_type='update') \ - .issue_alerts(old_far=self.event.far) - - # In this case, no recipients should match so the alert functions - # are not even called - email_mock.assert_not_called() - phone_mock.assert_not_called() - - def test_update_with_nscand(self, email_mock, phone_mock): - """Test alerts for superevent update with NSCAND trigger""" - # Add NSCAND to preferred event - self.event.singleinspiral_set.create(mass1=3, mass2=1) - - # Issue alerts - SupereventAlertIssuer(self.superevent, alert_type='update') \ - .issue_alerts(old_nscand=False) - - # Check recipients passed to alert functions - email_recips = email_mock.call_args[0][2] - phone_recips = phone_mock.call_args[0][2] - - # Only the original 'nscand' alert should be triggered - self.assertEqual(email_recips.count(), 1) - self.assertEqual(phone_recips.count(), 1) - self.assertEqual(email_recips.first().description, 'nscand') - self.assertEqual(phone_recips.first().description, 'nscand') - - def test_update_with_far_no_prev_far(self, email_mock, phone_mock): - """Test alerts for superevent update with new FAR, no previous FAR""" - # Add FAR to preferred event - self.event.far = 1e-10 - self.event.save() - - # Create a new notification with too low of a FAR threshold - n_low = Notification.objects.create(user=self.internal_user, - description='far_low', far_threshold=1e-20, - category=Notification.NOTIFICATION_CATEGORY_SUPEREVENT) - n_low.contacts.create(user=self.internal_user, - description=n_low.description, email='test@test.com', - verified=True) - n_low.contacts.create(user=self.internal_user, - description=n_low.description, phone='12345678901', - phone_method=Contact.CONTACT_PHONE_BOTH, verified=True) - - # Create a new notification with too high of a FAR threshold - n_high = Notification.objects.create(user=self.internal_user, - description='far_high', far_threshold=1, - category=Notification.NOTIFICATION_CATEGORY_SUPEREVENT) - n_high.contacts.create(user=self.internal_user, - description=n_high.description, email='test@test.com', - verified=True) - n_high.contacts.create(user=self.internal_user, - description=n_high.description, phone='12345678901', - phone_method=Contact.CONTACT_PHONE_BOTH, verified=True) - - # Issue alerts - SupereventAlertIssuer(self.superevent, alert_type='update') \ - .issue_alerts(old_far=None) - - # Check recipients passed to alert functions - email_recips = email_mock.call_args[0][2] - phone_recips = phone_mock.call_args[0][2] - - # Only the original 'far' alert and 'far_high' should be in here - for recips in [email_recips, phone_recips]: - self.assertEqual(recips.count(), 2) - for r in recips: - self.assertIn(r.description, ['far', 'far_high']) - - def test_update_with_far_no_old_far_passed(self, email_mock, - phone_mock): - """Test alerts for superevent update with new FAR, no old FAR passed""" - # Add FAR to preferred event - self.event.far = 1e-10 - self.event.save() - - # Create a new notification with too low of a FAR threshold - n_low = Notification.objects.create(user=self.internal_user, - description='far_low', far_threshold=1e-20, - category=Notification.NOTIFICATION_CATEGORY_SUPEREVENT) - n_low.contacts.create(user=self.internal_user, - description=n_low.description, email='test@test.com', - verified=True) - n_low.contacts.create(user=self.internal_user, - description=n_low.description, phone='12345678901', - phone_method=Contact.CONTACT_PHONE_BOTH, verified=True) - - # Create a new notification with too high of a FAR threshold - n_high = Notification.objects.create(user=self.internal_user, - description='far_high', far_threshold=1, - category=Notification.NOTIFICATION_CATEGORY_SUPEREVENT) - n_high.contacts.create(user=self.internal_user, - description=n_high.description, email='test@test.com', - verified=True) - n_high.contacts.create(user=self.internal_user, - description=n_high.description, phone='12345678901', - phone_method=Contact.CONTACT_PHONE_BOTH, verified=True) - - # Issue alerts - SupereventAlertIssuer(self.superevent, alert_type='update') \ - .issue_alerts() - - # Check recipients passed to alert functions - email_recips = email_mock.call_args[0][2] - phone_recips = phone_mock.call_args[0][2] - - # Only the original 'far' alert and 'far_high' should be in here - for recips in [email_recips, phone_recips]: - self.assertEqual(recips.count(), 2) - for r in recips: - self.assertIn(r.description, ['far', 'far_high']) - - def test_update_with_nscand_still_true(self, email_mock, phone_mock): - """Test alerts for superevent update where NSCAND was and is true""" - # Add NSCAND to preferred event - self.event.singleinspiral_set.create(mass1=3, mass2=1) - - # Issue alerts - SupereventAlertIssuer(self.superevent, alert_type='update') \ - .issue_alerts(old_nscand=True) - - # In this case, no recipients should match so the alert functions - # are not even called - email_mock.assert_not_called() - phone_mock.assert_not_called() - - def test_update_with_lower_far_nscand(self, email_mock, phone_mock): - """Test alerts for superevent update with lower FAR and NSCAND""" - # Add FAR to preferred event - self.event.far = 1e-10 - self.event.save() - # Add NSCAND to preferred event - self.event.singleinspiral_set.create(mass1=3, mass2=1) - - # Create a new notification with too low of a FAR threshold - n_low = Notification.objects.create(user=self.internal_user, - description='far_low', far_threshold=1e-20, - category=Notification.NOTIFICATION_CATEGORY_SUPEREVENT) - n_low.contacts.create(user=self.internal_user, - description=n_low.description, email='test@test.com', - verified=True) - n_low.contacts.create(user=self.internal_user, - description=n_low.description, phone='12345678901', - phone_method=Contact.CONTACT_PHONE_BOTH, verified=True) - - # Create a new notification with too high of a FAR threshold - n_high = Notification.objects.create(user=self.internal_user, - description='far_high', far_threshold=1, - category=Notification.NOTIFICATION_CATEGORY_SUPEREVENT) - n_high.contacts.create(user=self.internal_user, - description=n_high.description, email='test@test.com', - verified=True) - n_high.contacts.create(user=self.internal_user, - description=n_high.description, phone='12345678901', - phone_method=Contact.CONTACT_PHONE_BOTH, verified=True) - - # Issue alerts - SupereventAlertIssuer(self.superevent, alert_type='update') \ - .issue_alerts(old_far=self.far_thresh*2, old_nscand=False) - - # Check recipients passed to alert functions - email_recips = email_mock.call_args[0][2] - phone_recips = phone_mock.call_args[0][2] - - # Only the original 'far' alert should be triggered and not the - # n_low or n_high that we defined here - for recips in [email_recips, phone_recips]: - self.assertEqual(recips.count(), 3) - for r in recips: - self.assertIn(r.description, ['far', 'nscand', 'far_nscand']) - - def test_labeled_update_with_lower_far(self, email_mock, phone_mock): - """ - Test alerts for a superevent which has labels and updated with lower - FAR - """ - # Add FAR to preferred event - self.event.far = 1e-10 - self.event.save() - - # Add label1 to superevent - not enough to match labels yet - self.superevent.labelling_set.create(label=self.label1, - creator=self.internal_user) - - # Issue alerts - SupereventAlertIssuer(self.superevent, alert_type='update') \ - .issue_alerts(old_far=self.far_thresh*2) - - # Check recipients passed to alert functions - email_recips = email_mock.call_args[0][2] - phone_recips = phone_mock.call_args[0][2] - - # 'far' with no label requirements should be triggered - for recips in [email_recips, phone_recips]: - self.assertEqual(recips.count(), 1) - for r in recips: - self.assertIn(r.description, ['far']) - - # Add label2 to event - should match now - self.superevent.labelling_set.create(label=self.label2, - creator=self.internal_user) - - # Issue alerts - SupereventAlertIssuer(self.superevent, alert_type='update') \ - .issue_alerts(old_far=self.far_thresh*2) - - # Check recipients passed to alert functions - email_recips = email_mock.call_args[0][2] - phone_recips = phone_mock.call_args[0][2] - - # 'far' with no label requirements and 'far_labels' should be triggered - for recips in [email_recips, phone_recips]: - self.assertEqual(recips.count(), 2) - for r in recips: - self.assertIn(r.description, ['far', 'far_labels']) - - def test_labelq_update_with_lower_far(self, email_mock, phone_mock): - """ - Test alerts for superevent update with lower FAR and label_query - match - """ - # Add FAR to preferred event - self.event.far = 1e-10 - self.event.save() - - # Add labels to superevent - this set of labels shouldn't match label - # query - self.superevent.labelling_set.create(label=self.label3, - creator=self.internal_user) - self.superevent.labelling_set.create(label=self.label4, - creator=self.internal_user) - - # Issue alerts - SupereventAlertIssuer(self.superevent, alert_type='update') \ - .issue_alerts(old_far=self.far_thresh*2) - - # Check recipients passed to alert functions - email_recips = email_mock.call_args[0][2] - phone_recips = phone_mock.call_args[0][2] - - # 'far' with no label requirements should be triggered - for recips in [email_recips, phone_recips]: - self.assertEqual(recips.count(), 1) - for r in recips: - self.assertIn(r.description, ['far']) - - # Remove label4 and the label query should match - self.superevent.labelling_set.get(label=self.label4).delete() - - # Issue alerts - SupereventAlertIssuer(self.superevent, alert_type='update') \ - .issue_alerts(old_far=self.far_thresh*2) - - # Check recipients passed to alert functions - email_recips = email_mock.call_args[0][2] - phone_recips = phone_mock.call_args[0][2] - - # 'far' with no label requirements should be triggered - # 'far_labelq' (with label query) should now be triggered - for recips in [email_recips, phone_recips]: - self.assertEqual(recips.count(), 2) - for r in recips: - self.assertIn(r.description, ['far', 'far_labelq']) - - def test_label_added(self, email_mock, phone_mock): - """Test adding label alert for superevent""" - # Add label1 to superevent - this shouldn't match any queries - lab1 = self.superevent.labelling_set.create(label=self.label1, - creator=self.internal_user) - - # Issue alerts - SupereventLabelAlertIssuer(lab1, alert_type='label_added') \ - .issue_alerts() - - # In this case, no recipients should match so the alert functions - # are not even called - email_mock.assert_not_called() - phone_mock.assert_not_called() - - # Add label2 to superevent - lab2 = self.superevent.labelling_set.create(label=self.label2, - creator=self.internal_user) - - # Issue alerts - SupereventLabelAlertIssuer(lab2, alert_type='label_added') \ - .issue_alerts() - - # Check recipients passed to alert functions - email_recips = email_mock.call_args[0][2] - phone_recips = phone_mock.call_args[0][2] - - # Only 'labels' trigger should be triggered - for recips in [email_recips, phone_recips]: - self.assertEqual(recips.count(), 1) - for r in recips: - self.assertIn(r.description, ['labels']) - - # Issue alert for label 1 now that it has both labels; should be - # the same result - SupereventLabelAlertIssuer(lab1, alert_type='label_added') \ - .issue_alerts() - - # Check recipients passed to alert functions - email_recips = email_mock.call_args[0][2] - phone_recips = phone_mock.call_args[0][2] - - # Only 'labels' trigger should be triggered - for recips in [email_recips, phone_recips]: - self.assertEqual(recips.count(), 1) - for r in recips: - self.assertIn(r.description, ['labels']) - - def test_label_added_extra_labels(self, email_mock, phone_mock): - """Test adding label alert for superevent with other labels""" - # Add label 1, 2, and 4 to superevent - lab1 = self.superevent.labelling_set.create(label=self.label1, - creator=self.internal_user) - lab4 = self.superevent.labelling_set.create(label=self.label4, - creator=self.internal_user) - lab2 = self.superevent.labelling_set.create(label=self.label2, - creator=self.internal_user) - - # Issue alerts for label 2 - SupereventLabelAlertIssuer(lab2, alert_type='label_added')\ - .issue_alerts() - - # Check recipients passed to alert functions - email_recips = email_mock.call_args[0][2] - phone_recips = phone_mock.call_args[0][2] - - # The 'labels' trigger only requires label1 and label2, but it should - # still trigger on label2 addition even though label4 is also present - for recips in [email_recips, phone_recips]: - self.assertEqual(recips.count(), 1) - for r in recips: - self.assertIn(r.description, ['labels']) - - def test_label_added_with_far(self, email_mock, phone_mock): - """Test adding label alert for superevent with FAR""" - # Set preferred event FAR - self.event.far = 1e-10 - self.event.save() - - # Add label1 and label2 to superevent - lab1 = self.superevent.labelling_set.create(label=self.label1, - creator=self.internal_user) - lab2 = self.superevent.labelling_set.create(label=self.label2, - creator=self.internal_user) - - # Issue alerts - SupereventLabelAlertIssuer(lab2, alert_type='label_added')\ - .issue_alerts() - - # Check recipients passed to alert functions - email_recips = email_mock.call_args[0][2] - phone_recips = phone_mock.call_args[0][2] - - # Basic 'labels' trigger and labels w/ FAR ('far_labels') trigger - # should be triggered - for recips in [email_recips, phone_recips]: - self.assertEqual(recips.count(), 2) - for r in recips: - self.assertIn(r.description, ['labels', 'far_labels']) - - def test_label_added_with_nscand(self, email_mock, phone_mock): - """Test adding label alert for superevent with NSCAND""" - # Add NSCAND to preferred event - self.event.singleinspiral_set.create(mass1=3, mass2=1) - - # Add label1 and label2 to event - lab1 = self.superevent.labelling_set.create(label=self.label1, - creator=self.internal_user) - lab2 = self.superevent.labelling_set.create(label=self.label2, - creator=self.internal_user) - - # Issue alerts - SupereventLabelAlertIssuer(lab2, alert_type='label_added') \ - .issue_alerts() - - # Check recipients passed to alert functions - email_recips = email_mock.call_args[0][2] - phone_recips = phone_mock.call_args[0][2] - - # Basic 'labels' trigger and labels w/ NSCAND ('nscand_labels') trigger - # should be triggered - for recips in [email_recips, phone_recips]: - self.assertEqual(recips.count(), 2) - for r in recips: - self.assertIn(r.description, ['labels', 'nscand_labels']) - - def test_label_added_with_far_nscand(self, email_mock, phone_mock): - """ - Test adding label alert for superevent with FAR threshold and NSCAND - """ - # Set preferred event FAR - self.event.far = 1e-10 - self.event.save() - - # Add NSCAND to preferred event - self.event.singleinspiral_set.create(mass1=3, mass2=1) - - # Add label1 and label2 to superevent - lab1 = self.superevent.labelling_set.create(label=self.label1, - creator=self.internal_user) - lab2 = self.superevent.labelling_set.create(label=self.label2, - creator=self.internal_user) - - # Issue alerts - SupereventLabelAlertIssuer(lab2, alert_type='label_added') \ - .issue_alerts() - - # Check recipients passed to alert functions - email_recips = email_mock.call_args[0][2] - phone_recips = phone_mock.call_args[0][2] - - # Basic 'labels' trigger, labels with FAR, labels with NSCAND, and - # labels with FAR and NSCAND should all be triggered - # should be triggered - for recips in [email_recips, phone_recips]: - self.assertEqual(recips.count(), 4) - for r in recips: - self.assertIn(r.description, ['labels', 'far_labels', - 'nscand_labels', 'far_nscand_labels']) - - def test_label_added_labelq(self, email_mock, phone_mock): - """Test adding label alert for superevent with label query match""" - # Add label3 and label4 to superevent - lab3 = self.superevent.labelling_set.create(label=self.label3, - creator=self.internal_user) - lab4 = self.superevent.labelling_set.create(label=self.label4, - creator=self.internal_user) - - # Issue alerts - SupereventLabelAlertIssuer(lab4, alert_type='label_added') \ - .issue_alerts() - - # In this case, no recipients should match so the alert functions - # are not even called - email_mock.assert_not_called() - phone_mock.assert_not_called() - - # Remove label4 and the label query should match - self.superevent.labelling_set.get(label=self.label4).delete() - - # Issue alerts - SupereventLabelAlertIssuer(lab3, alert_type='label_added') \ - .issue_alerts() - - # Check recipients passed to alert functions - email_recips = email_mock.call_args[0][2] - phone_recips = phone_mock.call_args[0][2] - - # Basic label_query trigger should be only match - for recips in [email_recips, phone_recips]: - self.assertEqual(recips.count(), 1) - for r in recips: - self.assertIn(r.description, ['labelq']) - - def test_label_added_labelq_with_far(self, email_mock, phone_mock): - """ - Test adding label alert for superevent with FAR and label query match - """ - # Set preferred event FAR - self.event.far = 1e-10 - self.event.save() - - # Add label3 to superevent - lab3 = self.superevent.labelling_set.create(label=self.label3, - creator=self.internal_user) - - # Issue alerts - SupereventLabelAlertIssuer(lab3, alert_type='label_added') \ - .issue_alerts() - - # Check recipients passed to alert functions - email_recips = email_mock.call_args[0][2] - phone_recips = phone_mock.call_args[0][2] - - # Basic 'labelq' trigger and label query w/ FAR ('far_labelq') trigger - # should be triggered - for recips in [email_recips, phone_recips]: - self.assertEqual(recips.count(), 2) - for r in recips: - self.assertIn(r.description, ['labelq', 'far_labelq']) - - def test_label_added_labelq_with_nscand(self, email_mock, phone_mock): - """ - Test adding label alert for superevent with NSCAND and label query - match - """ - # Add NSCAND to preferred event - self.event.singleinspiral_set.create(mass1=3, mass2=1) - - # Add label3 to superevent - lab3 = self.superevent.labelling_set.create(label=self.label3, - creator=self.internal_user) - - # Issue alerts - SupereventLabelAlertIssuer(lab3, alert_type='label_added') \ - .issue_alerts() - - # Check recipients passed to alert functions - email_recips = email_mock.call_args[0][2] - phone_recips = phone_mock.call_args[0][2] - - # Basic 'labelq' trigger and label query w/ NSCAND ('nscand_labelq') - # trigger should be triggered - for recips in [email_recips, phone_recips]: - self.assertEqual(recips.count(), 2) - for r in recips: - self.assertIn(r.description, ['labelq', 'nscand_labelq']) - - def test_label_added_labelq_with_far_nscand(self, email_mock, phone_mock): - """ - Test adding label alert for superevent with FAR threshold and NSCAND - and label query match - """ - # Set preferred event FAR - self.event.far = 1e-10 - self.event.save() - - # Add NSCAND to preferred event - self.event.singleinspiral_set.create(mass1=3, mass2=1) - - # Add label3 to superevent - lab3 = self.superevent.labelling_set.create(label=self.label3, - creator=self.internal_user) - - # Issue alerts - SupereventLabelAlertIssuer(lab3, alert_type='label_added') \ - .issue_alerts() - - # Check recipients passed to alert functions - email_recips = email_mock.call_args[0][2] - phone_recips = phone_mock.call_args[0][2] - - # Basic 'labelq' trigger, label query with FAR, label query with - # NSCAND, and label query with FAR and NSCAND should all be triggered - for recips in [email_recips, phone_recips]: - self.assertEqual(recips.count(), 4) - for r in recips: - self.assertIn(r.description, ['labelq', 'far_labelq', - 'nscand_labelq', 'far_nscand_labelq']) - - def test_label_removed_match_labels(self, email_mock, phone_mock): - """ - Test label_removed alert for superevent where only triggers with - labels, not label queries are matched - """ - # Add labels 1 and 2 - lab1 = self.superevent.labelling_set.create(label=self.label1, - creator=self.internal_user) - lab2 = self.superevent.labelling_set.create(label=self.label2, - creator=self.internal_user) - - # Remove label 2 and issue alert for label 2 removal - self.superevent.labelling_set.get(label=self.label2).delete() - SupereventLabelAlertIssuer(lab2, alert_type='label_removed') \ - .issue_alerts() - - # In this case, no recipients should match so the alert functions - # are not even called - email_mock.assert_not_called() - phone_mock.assert_not_called() - - # Add labels 2 and 3 - lab2 = self.superevent.labelling_set.create(label=self.label2, - creator=self.internal_user) - lab3 = self.superevent.labelling_set.create(label=self.label3, - creator=self.internal_user) - - # Remove label 3 and issue alert - self.superevent.labelling_set.get(label=self.label3).delete() - SupereventLabelAlertIssuer(lab3, alert_type='label_removed') \ - .issue_alerts() - - # Although the event has label1 and label2 and matches the - # 'labels' trigger, this trigger was matched last time either - # label1 or label2 was added, and label3 being removed doesn't - # change that (i.e., label_removed alerts only trigger notifications - # with label queries) - email_mock.assert_not_called() - phone_mock.assert_not_called() - - def test_label_removed(self, email_mock, phone_mock): - """Test label_removed alert for superevent with label query match""" - # Add labels 3 and 4 - lab3 = self.superevent.labelling_set.create(label=self.label3, - creator=self.internal_user) - lab4 = self.superevent.labelling_set.create(label=self.label4, - creator=self.internal_user) - - # Remove label 4 and issue alert for label 4 removal - self.superevent.labelling_set.get(label=self.label4).delete() - SupereventLabelAlertIssuer(lab4, alert_type='label_removed') \ - .issue_alerts() - - # Check recipients passed to alert functions - email_recips = email_mock.call_args[0][2] - phone_recips = phone_mock.call_args[0][2] - - # Only 'labelq' trigger should be triggered - for recips in [email_recips, phone_recips]: - self.assertEqual(recips.count(), 1) - for r in recips: - self.assertIn(r.description, ['labelq']) - - def test_label_removed_with_far(self, email_mock, phone_mock): - """ - Test label_removed alert for superevent with label query match and - FAR threshold match""" - # Set preferred event FAR - self.event.far = 1e-10 - self.event.save() - - # Add labels 3 and 4 - lab3 = self.superevent.labelling_set.create(label=self.label3, - creator=self.internal_user) - lab4 = self.superevent.labelling_set.create(label=self.label4, - creator=self.internal_user) - - # Remove label 4 and issue alert for label 4 removal - self.superevent.labelling_set.get(label=self.label4).delete() - SupereventLabelAlertIssuer(lab4, alert_type='label_removed') \ - .issue_alerts() - - # Check recipients passed to alert functions - email_recips = email_mock.call_args[0][2] - phone_recips = phone_mock.call_args[0][2] - - # Only 'labelq' and label_query with FAR should be triggered - for recips in [email_recips, phone_recips]: - self.assertEqual(recips.count(), 2) - for r in recips: - self.assertIn(r.description, ['labelq', 'far_labelq']) - - def test_label_removed_with_nscand(self, email_mock, phone_mock): - """Test label_removed alert with label query match and NSCAND match""" - # Add NSCAND to preferred event - self.event.singleinspiral_set.create(mass1=3, mass2=1) - - # Add labels 3 and 4 - lab3 = self.superevent.labelling_set.create(label=self.label3, - creator=self.internal_user) - lab4 = self.superevent.labelling_set.create(label=self.label4, - creator=self.internal_user) - - # Remove label 4 and issue alert for label 4 removal - self.superevent.labelling_set.get(label=self.label4).delete() - SupereventLabelAlertIssuer(lab4, alert_type='label_removed') \ - .issue_alerts() - - # Check recipients passed to alert functions - email_recips = email_mock.call_args[0][2] - phone_recips = phone_mock.call_args[0][2] - - # Basic label query trigger and label query w/ NSCAND should be - # triggered - for recips in [email_recips, phone_recips]: - self.assertEqual(recips.count(), 2) - for r in recips: - self.assertIn(r.description, ['labelq', 'nscand_labelq']) - - def test_label_removed_with_far_nscand(self, email_mock, phone_mock): - """ - Test label_removed alert for superevent with FAR threshold and NSCAND - and label query match - """ - # Set preferred event FAR - self.event.far = 1e-10 - self.event.save() - - # Add NSCAND to preferred event - self.event.singleinspiral_set.create(mass1=3, mass2=1) - - # Add labels 3 and 4 - lab3 = self.superevent.labelling_set.create(label=self.label3, - creator=self.internal_user) - lab4 = self.superevent.labelling_set.create(label=self.label4, - creator=self.internal_user) - - # Remove label 4 and issue alert for label 4 removal - self.superevent.labelling_set.get(label=self.label4).delete() - SupereventLabelAlertIssuer(lab4, alert_type='label_removed') \ - .issue_alerts() - - # Check recipients passed to alert functions - email_recips = email_mock.call_args[0][2] - phone_recips = phone_mock.call_args[0][2] - - # Should match basic label query trigger, label query with FAR, - # label query with NSCAND, and label query with FAR and NSCAND - for recips in [email_recips, phone_recips]: - self.assertEqual(recips.count(), 4) - for r in recips: - self.assertIn(r.description, ['labelq', 'far_labelq', - 'nscand_labelq', 'far_nscand_labelq']) - - -@override_settings( - SEND_XMPP_ALERTS=False, - SEND_EMAIL_ALERTS=True, - SEND_PHONE_ALERTS=True, +@pytest.mark.django_db +def test_event_update_alerts( + event, event_notifications, old_far, far, old_nscand, nscand, + use_default_gps, labels, notif_descs, +): + # Set up event state + event.far = far + event.is_ns_candidate = types.MethodType(lambda self: nscand, event) + if use_default_gps: + event.group = Group.objects.get(name=DEFAULT_GROUP) + event.pipeline = Pipeline.objects.get(name=DEFAULT_PIPELINE) + event.search = Search.objects.get(name=DEFAULT_SEARCH) + event.save() + if labels is not None: + for label_name in labels: + label, _ = Label.objects.get_or_create(name=label_name) + event.labelling_set.create(label=label, creator=event.submitter) + + # Set up recipient getter + recipient_getter = UpdateRecipientGetter(event, old_far=old_far, + old_nscand=old_nscand) + recipient_getter.queryset = event_notifications + + # Get notifications + matched_notifications = recipient_getter.get_notifications() + + # Test results + assert matched_notifications.count() == len(notif_descs) + for desc in notif_descs: + assert matched_notifications.filter(description=desc).exists() + + +@pytest.mark.parametrize( + "far,nscand,new_label,old_labels,use_default_gps,notif_descs", + EVENT_LABEL_ADDED_ALERT_DATA ) -@mock.patch('alerts.main.issue_phone_alerts') -@mock.patch('alerts.main.issue_email_alerts') -class TestSupereventUnverifiedRecipients(GraceDbTestBase, SupereventCreateMixin): - - @classmethod - def setUpTestData(cls): - super(TestSupereventUnverifiedRecipients, cls).setUpTestData() - - # Create a superevent - cls.superevent = cls.create_superevent(cls.internal_user, - 'fake_group', 'fake_pipeline', 'fake_search') - - # References to cls/self.event will refer to the superevent's - # preferred event - cls.event = cls.superevent.preferred_event - # Set FAR - cls.event.far = 1e-6 - - # Create labaels - cls.label1, _ = Label.objects.get_or_create(name='TEST_LABEL1') - cls.label2, _ = Label.objects.get_or_create(name='TEST_LABEL2') - - # Create a basic notification and a label query notification - cls.notification = Notification.objects.create( - user=cls.internal_user, description='basic', far_threshold=0.01, - category=Notification.NOTIFICATION_CATEGORY_SUPEREVENT) - cls.label_notification = Notification.objects.create( - user=cls.internal_user, description='labels', - category=Notification.NOTIFICATION_CATEGORY_SUPEREVENT) - cls.label_notification.labels.add(cls.label1) - cls.label_notification.labels.add(cls.label2) - cls.label_notification.label_query = '{l1} & ~{l2}'.format( - l1=cls.label1.name, l2=cls.label2.name) - cls.label_notification.save() - - # Create an email and phone contact for notification - cls.notification.contacts.create(user=cls.internal_user, - description='basic email', email='test@test.com', - verified=True) - cls.notification.contacts.create(user=cls.internal_user, - description='basic phone', phone='12345678901', - phone_method=Contact.CONTACT_PHONE_BOTH, verified=False) - - # Create contacts for label_query notification - cls.label_notification.contacts.create(user=cls.internal_user, - description='label email', email='test@test.com', - verified=True) - cls.label_notification.contacts.create(user=cls.internal_user, - description='label phone', phone='12345678901', - phone_method=Contact.CONTACT_PHONE_BOTH, verified=False) - - def test_new_superevent(self, email_mock, phone_mock): - """Test verified recipients for new superevent alert""" - SupereventAlertIssuer(self.superevent, alert_type='new').issue_alerts() - - # Check recipients passed to email alert function - email_recips = email_mock.call_args[0][2] - self.assertEqual(email_recips.count(), 1) - self.assertEqual(email_recips.first().description, 'basic email') - - # Phone alerts should be called at all since there were no - # verified recipients - phone_mock.assert_not_called() - - def test_update_superevent(self, email_mock, phone_mock): - """Test verified recipients for update superevent alert""" - SupereventAlertIssuer(self.superevent, alert_type='update') \ - .issue_alerts(old_far=0.02) - - # Check recipients passed to email alert function - email_recips = email_mock.call_args[0][2] - self.assertEqual(email_recips.count(), 1) - self.assertEqual(email_recips.first().description, 'basic email') - - # Phone alerts should be called at all since there were no - # verified recipients - phone_mock.assert_not_called() - - def test_label_added_superevent(self, email_mock, phone_mock): - """Test verified recipients for label_added superevent alert""" - # Add label - lab1 = self.superevent.labelling_set.create(label=self.label1, - creator=self.internal_user) - - # Issue alerts - SupereventLabelAlertIssuer(lab1, alert_type='label_added') \ - .issue_alerts() - - # Check recipients passed to email alert function - email_recips = email_mock.call_args[0][2] - self.assertEqual(email_recips.count(), 1) - self.assertEqual(email_recips.first().description, 'label email') - - # Phone alerts should be called at all since there were no - # verified recipients - phone_mock.assert_not_called() - - def test_label_removed_superevent(self, email_mock, phone_mock): - """Test verified recipients for label_removed superevent alert""" - # Add labels - lab1 = self.superevent.labelling_set.create(label=self.label1, - creator=self.internal_user) - lab2 = self.superevent.labelling_set.create(label=self.label2, - creator=self.internal_user) - - # Remove label 2 and issue alerts - lab2.delete() - SupereventLabelAlertIssuer(lab2, alert_type='label_removed') \ - .issue_alerts() - - # Check recipients passed to email alert function - email_recips = email_mock.call_args[0][2] - self.assertEqual(email_recips.count(), 1) - self.assertEqual(email_recips.first().description, 'label email') - - # Phone alerts should be called at all since there were no - # verified recipients - phone_mock.assert_not_called() - - -@override_settings( - SEND_XMPP_ALERTS=False, - SEND_EMAIL_ALERTS=True, - SEND_PHONE_ALERTS=True, +@pytest.mark.django_db +def test_event_label_added_alerts( + event, event_notifications, far, nscand, new_label, old_labels, + use_default_gps, notif_descs, +): + # Set up event state + event.far = far + event.is_ns_candidate = types.MethodType(lambda self: nscand, event) + if use_default_gps: + event.group = Group.objects.get(name=DEFAULT_GROUP) + event.pipeline = Pipeline.objects.get(name=DEFAULT_PIPELINE) + event.search = Search.objects.get(name=DEFAULT_SEARCH) + event.save() + if old_labels is not None: + for label_name in old_labels: + label, _ = Label.objects.get_or_create(name=label_name) + event.labelling_set.create(label=label, creator=event.submitter) + + # Add new label + label_obj, _ = Label.objects.get_or_create(name=new_label) + event.labelling_set.create(label=label_obj, creator=event.submitter) + + # Set up recipient getter + recipient_getter = LabelAddedRecipientGetter(event, label=label_obj) + recipient_getter.queryset = event_notifications + + # Get notifications + matched_notifications = recipient_getter.get_notifications() + + # Test results + assert matched_notifications.count() == len(notif_descs) + for desc in notif_descs: + assert matched_notifications.filter(description=desc).exists() + + +@pytest.mark.parametrize( + "far,nscand,removed_label,labels,use_default_gps,notif_descs", + EVENT_LABEL_REMOVED_ALERT_DATA ) -@mock.patch('alerts.main.issue_phone_alerts') -@mock.patch('alerts.main.issue_email_alerts') -class TestEventUnverifiedRecipients(GraceDbTestBase, EventCreateMixin): - - @classmethod - def setUpTestData(cls): - super(TestEventUnverifiedRecipients, cls).setUpTestData() - - # Create an event - cls.event = cls.create_event('fake_group', 'fake_pipeline', - search_name='fake_search', user=cls.internal_user) - # Set FAR - cls.event.far = 1e-6 - - # Create labaels - cls.label1, _ = Label.objects.get_or_create(name='TEST_LABEL1') - cls.label2, _ = Label.objects.get_or_create(name='TEST_LABEL2') - - # Create a basic notification and a label query notification - cls.notification = Notification.objects.create( - user=cls.internal_user, description='basic', far_threshold=0.01, - category=Notification.NOTIFICATION_CATEGORY_EVENT) - cls.label_notification = Notification.objects.create( - user=cls.internal_user, description='labels', - category=Notification.NOTIFICATION_CATEGORY_EVENT) - cls.label_notification.labels.add(cls.label1) - cls.label_notification.labels.add(cls.label2) - cls.label_notification.label_query = '{l1} & ~{l2}'.format( - l1=cls.label1.name, l2=cls.label2.name) - cls.label_notification.save() - - # Create an email and phone contact for notification - cls.notification.contacts.create(user=cls.internal_user, - description='basic email', email='test@test.com', - verified=True) - cls.notification.contacts.create(user=cls.internal_user, - description='basic phone', phone='12345678901', - phone_method=Contact.CONTACT_PHONE_BOTH, verified=False) - - # Create contacts for label_query notification - cls.label_notification.contacts.create(user=cls.internal_user, - description='label email', email='test@test.com', - verified=True) - cls.label_notification.contacts.create(user=cls.internal_user, - description='label phone', phone='12345678901', - phone_method=Contact.CONTACT_PHONE_BOTH, verified=False) - - def test_new_event(self, email_mock, phone_mock): - """Test verified recipients for new event alert""" - EventAlertIssuer(self.event, alert_type='new').issue_alerts() - - # Check recipients passed to email alert function - email_recips = email_mock.call_args[0][2] - self.assertEqual(email_recips.count(), 1) - self.assertEqual(email_recips.first().description, 'basic email') - - # Phone alerts should be called at all since there were no - # verified recipients - phone_mock.assert_not_called() - - def test_update_event(self, email_mock, phone_mock): - """Test verified recipients for update event alert""" - EventAlertIssuer(self.event, alert_type='update') \ - .issue_alerts(old_far=0.02) - - # Check recipients passed to email alert function - email_recips = email_mock.call_args[0][2] - self.assertEqual(email_recips.count(), 1) - self.assertEqual(email_recips.first().description, 'basic email') - - # Phone alerts should be called at all since there were no - # verified recipients - phone_mock.assert_not_called() - - def test_label_added_event(self, email_mock, phone_mock): - """Test verified recipients for label_added event alert""" - # Add label - lab1 = self.event.labelling_set.create(label=self.label1, - creator=self.internal_user) - - # Issue alerts - EventLabelAlertIssuer(lab1, alert_type='label_added') \ - .issue_alerts() - - # Check recipients passed to email alert function - email_recips = email_mock.call_args[0][2] - self.assertEqual(email_recips.count(), 1) - self.assertEqual(email_recips.first().description, 'label email') - - # Phone alerts should be called at all since there were no - # verified recipients - phone_mock.assert_not_called() - - def test_label_removed_event(self, email_mock, phone_mock): - """Test verified recipients for label_removed event alert""" - # Add labels - lab1 = self.event.labelling_set.create(label=self.label1, - creator=self.internal_user) - lab2 = self.event.labelling_set.create(label=self.label2, - creator=self.internal_user) - - # Remove label 2 and issue alerts - lab2.delete() - EventLabelAlertIssuer(lab2, alert_type='label_removed') \ - .issue_alerts() - - # Check recipients passed to email alert function - email_recips = email_mock.call_args[0][2] - self.assertEqual(email_recips.count(), 1) - self.assertEqual(email_recips.first().description, 'label email') - - # Phone alerts should be called at all since there were no - # verified recipients - phone_mock.assert_not_called() +@pytest.mark.django_db +def test_event_label_removed_alerts( + event, event_notifications, far, nscand, removed_label, labels, + use_default_gps, notif_descs, +): + # Set up event state + event.far = far + event.is_ns_candidate = types.MethodType(lambda self: nscand, event) + if use_default_gps: + event.group = Group.objects.get(name=DEFAULT_GROUP) + event.pipeline = Pipeline.objects.get(name=DEFAULT_PIPELINE) + event.search = Search.objects.get(name=DEFAULT_SEARCH) + event.save() + if labels is not None: + for label_name in labels: + label, _ = Label.objects.get_or_create(name=label_name) + event.labelling_set.create(label=label, creator=event.submitter) + + # Add new label + label_obj, _ = Label.objects.get_or_create(name=removed_label) + + # Set up recipient getter + recipient_getter = LabelRemovedRecipientGetter(event, label=label_obj) + recipient_getter.queryset = event_notifications + + # Get notifications + matched_notifications = recipient_getter.get_notifications() + + # Test results + assert matched_notifications.count() == len(notif_descs) + for desc in notif_descs: + assert matched_notifications.filter(description=desc).exists() + + +# Other tests ----------------------------------------------------------------- +@pytest.mark.django_db +def test_complex_label_query(superevent): + # NOTE: L1 & ~L2 | L3 == L1 & (~L2 | L3) + n = Notification.objects.create( + label_query='L1 & ~L2 | L3', + user=superevent.submitter, + description='test notification' + ) + + # Set up superevent labels + for label_name in ['L1', 'L2', 'L3']: + l, _ = Label.objects.get_or_create(name=label_name) + n.labels.add(l) + + # Test label added recipients for L1 being added + l1 = Label.objects.get(name='L1') + superevent.labelling_set.create(creator=superevent.submitter, label=l1) + recipient_getter = LabelAddedRecipientGetter(superevent, label=l1) + matched_notifications = recipient_getter.get_notifications() + assert matched_notifications.count() == 1 + assert matched_notifications.first().description == n.description + + # Test label added recipients for L3 being added (L1 already added) + l3 = Label.objects.get(name='L3') + superevent.labelling_set.create(creator=superevent.submitter, label=l3) + recipient_getter = LabelAddedRecipientGetter(superevent, label=l3) + matched_notifications = recipient_getter.get_notifications() + assert matched_notifications.count() == 1 + assert matched_notifications.first().description == n.description + + # Test label added recipients for L3 being added (no L1) + l3 = Label.objects.get(name='L3') + superevent.labels.clear() + superevent.labelling_set.create(creator=superevent.submitter, label=l3) + recipient_getter = LabelAddedRecipientGetter(superevent, label=l3) + matched_notifications = recipient_getter.get_notifications() + assert matched_notifications.count() == 0 + + # Test label added recipients for L1 being added (with L2) + l1 = Label.objects.get(name='L1') + l2 = Label.objects.get(name='L2') + superevent.labels.clear() + superevent.labelling_set.create(creator=superevent.submitter, label=l2) + superevent.labelling_set.create(creator=superevent.submitter, label=l1) + recipient_getter = LabelAddedRecipientGetter(superevent, label=l1) + matched_notifications = recipient_getter.get_notifications() + assert matched_notifications.count() == 0 + + # Test label added recipients for L3 being added (with L1 and L2) + l1 = Label.objects.get(name='L1') + l2 = Label.objects.get(name='L2') + l3 = Label.objects.get(name='L3') + superevent.labels.clear() + superevent.labelling_set.create(creator=superevent.submitter, label=l1) + superevent.labelling_set.create(creator=superevent.submitter, label=l2) + superevent.labelling_set.create(creator=superevent.submitter, label=l3) + recipient_getter = LabelAddedRecipientGetter(superevent, label=l3) + matched_notifications = recipient_getter.get_notifications() + assert matched_notifications.count() == 1 + assert matched_notifications.first().description == n.description + + # Test label removed recipients for L2 being removed (with L1) + l1 = Label.objects.get(name='L1') + l2 = Label.objects.get(name='L2') + superevent.labels.clear() + superevent.labelling_set.create(creator=superevent.submitter, label=l1) + recipient_getter = LabelRemovedRecipientGetter(superevent, label=l2) + matched_notifications = recipient_getter.get_notifications() + assert matched_notifications.count() == 1 + assert matched_notifications.first().description == n.description + + # Test label removed recipients for L3 being removed (with L1) + l1 = Label.objects.get(name='L1') + l3 = Label.objects.get(name='L3') + superevent.labels.clear() + superevent.labelling_set.create(creator=superevent.submitter, label=l1) + recipient_getter = LabelRemovedRecipientGetter(superevent, label=l3) + matched_notifications = recipient_getter.get_notifications() + assert matched_notifications.count() == 1 + assert matched_notifications.first().description == n.description + + # Test label removed recipients for L2 being removed (with L1 and L3) + l1 = Label.objects.get(name='L1') + l2 = Label.objects.get(name='L2') + l3 = Label.objects.get(name='L3') + superevent.labels.clear() + superevent.labelling_set.create(creator=superevent.submitter, label=l1) + superevent.labelling_set.create(creator=superevent.submitter, label=l3) + recipient_getter = LabelAddedRecipientGetter(superevent, label=l2) + matched_notifications = recipient_getter.get_notifications() + # TODO: this shouldn't match, since it was already triggered on the + # previous state, where L1, L2, and L3 were all applied (but it does) + #assert matched_notifications.count() == 0 + + +@pytest.mark.django_db +def test_label_removal_with_only_labels_list(superevent): + n = Notification.objects.create( + user=superevent.submitter, + description='test notification' + ) + + # Set up superevent labels + for label_name in ['L1', 'L2']: + l, _ = Label.objects.get_or_create(name=label_name) + n.labels.add(l) + + # Test label added recipients for L1 being added + l1 = Label.objects.get(name='L1') + l2 = Label.objects.get(name='L2') + l3, _ = Label.objects.get_or_create(name='L3') + superevent.labelling_set.create(creator=superevent.submitter, label=l1) + superevent.labelling_set.create(creator=superevent.submitter, label=l2) + recipient_getter = LabelRemovedRecipientGetter(superevent, label=l3) + matched_notifications = recipient_getter.get_notifications() + assert matched_notifications.count() == 0 + + +@pytest.mark.parametrize("old_far,new_far,far_t,match", + [(1, 0.5, 0.5, False), (0.5, 0.25, 0.5, True)]) +@pytest.mark.django_db +def test_update_alert_far_threshold_edge(superevent, old_far, new_far, far_t, + match): + n = Notification.objects.create( + user=superevent.submitter, + far_threshold=far_t, + description='test notification' + ) + + # Set up superevent state + superevent.preferred_event.far = new_far + + # Set up recipient getter + recipient_getter = UpdateRecipientGetter(superevent, old_far=old_far, + old_nscand=False) + + # Get notifications + matched_notifications = recipient_getter.get_notifications() + if match: + assert matched_notifications.count() == 1 + assert matched_notifications.first().description == n.description + else: + assert matched_notifications.count() == 0 -- GitLab