From 79271cd63439360b770c637a4519dd7c590641ff Mon Sep 17 00:00:00 2001 From: "Adam.Dybbroe" Date: Mon, 28 Aug 2023 21:08:28 +0200 Subject: [PATCH 1/2] Add option to control which product(name)s should trigger a notification Signed-off-by: Adam.Dybbroe --- activefires_pp/fire_notifications.py | 18 +++++++++++------- .../tests/test_fire_notifications.py | 8 +++++++- examples/fire_notifier.yaml | 4 ++++ 3 files changed, 22 insertions(+), 8 deletions(-) diff --git a/activefires_pp/fire_notifications.py b/activefires_pp/fire_notifications.py index 426d665..1db2a20 100644 --- a/activefires_pp/fire_notifications.py +++ b/activefires_pp/fire_notifications.py @@ -1,7 +1,7 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- -# Copyright (c) 2021, 2022 Adam Dybbroe +# Copyright (c) 2021 - 2023 Adam Dybbroe # Author(s): @@ -20,8 +20,7 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . -"""Creating and sending notifications for detected forest fires. -""" +"""Creating and sending notifications for detected forest fires.""" import socket from netrc import netrc @@ -56,8 +55,10 @@ class RecipientDataStruct(object): - def __init__(self): + """A data structure to control the list of configured recipients.""" + def __init__(self): + """Initialize the recipient data structure.""" self.recipients_with_attachment = [] self.recipients_without_attachment = [] self.recipients_all = [] @@ -140,7 +141,6 @@ def _setup_and_start_communication(self): def _set_options_from_config(self, config): """From the configuration on disk set the option dictionary, holding all metadata for processing.""" - for item in config: self.options[item] = config[item] @@ -184,6 +184,12 @@ def run(self): LOG.debug("Message type not supported: %s", str(msg.type)) continue + product_name = msg.data.get('product') + product_list = self.options.get('products') + if product_list and product_name and product_name not in product_list: + LOG.info('Product %s will not generate a notification!', product_name) + continue + output_msg = self.notify_end_users(msg) if output_msg: LOG.debug("Sending message: %s", str(output_msg)) @@ -218,7 +224,6 @@ def notify_end_users(self, msg): def _send_notifications_with_attachments(self, server, recipients, full_message, filename, platform_name): """Send notifications with attachments.""" - notification = MIMEMultipart() notification['From'] = self.sender if platform_name: @@ -252,7 +257,6 @@ def _send_notifications_with_attachments(self, server, recipients, full_message, def _send_notifications_without_attachments(self, server, recipients, sub_messages, platform_name): """Send notifications without attachments.""" - for submsg in sub_messages: notification = MIMEMultipart() notification['From'] = self.sender diff --git a/activefires_pp/tests/test_fire_notifications.py b/activefires_pp/tests/test_fire_notifications.py index f8f32f0..91306ac 100644 --- a/activefires_pp/tests/test_fire_notifications.py +++ b/activefires_pp/tests/test_fire_notifications.py @@ -40,6 +40,10 @@ publish_topic: VIIRS/L2/MSB/National subscribe_topics: VIIRS/L2/Fires/PP/National +products: + - afimg + - afimg_some_other_geoid + smtp_server: smtp.mydomain.se domain: mydomain.se @@ -137,7 +141,9 @@ def test_get_options_national_filtering(self, setup_comm, read_config, gethostna expected = {'publish_topic': 'VIIRS/L2/MSB/National', 'subscribe_topics': ['VIIRS/L2/Fires/PP/National'], - 'smtp_server': 'smtp.mydomain.se', 'domain': 'mydomain.se', 'sender': 'active-fires@mydomain.se', + 'products': ['afimg', 'afimg_some_other_geoid'], + 'smtp_server': 'smtp.mydomain.se', + 'domain': 'mydomain.se', 'sender': 'active-fires@mydomain.se', 'recipients': ['recipient1@recipients.se', 'recipient2@recipients.se', 'recipient3@recipients.se'], 'recipients_attachment': ['recipient1@recipients.se', 'recipient2@recipients.se'], 'subject': 'My subject', 'max_number_of_fires_in_sms': 3, diff --git a/examples/fire_notifier.yaml b/examples/fire_notifier.yaml index eddb68c..ca33244 100644 --- a/examples/fire_notifier.yaml +++ b/examples/fire_notifier.yaml @@ -2,6 +2,10 @@ publish_topic: VIIRS/L2/MSB/National subscribe_topics: VIIRS/L2/Fires/PP/National +products: + - afimg + - afimg_some_other_geoid + smtp_server: smtp.mydomain.se domain: mydomain.se From 18357538353e958eeb6fe117044846f2f5cf80d9 Mon Sep 17 00:00:00 2001 From: "Adam.Dybbroe" Date: Mon, 28 Aug 2023 21:28:34 +0200 Subject: [PATCH 2/2] Refactor and add test Signed-off-by: Adam.Dybbroe --- activefires_pp/fire_notifications.py | 15 ++++-- .../tests/test_fire_notifications.py | 50 ++++++++++++++++++- activefires_pp/tests/test_messaging.py | 4 +- 3 files changed, 62 insertions(+), 7 deletions(-) diff --git a/activefires_pp/fire_notifications.py b/activefires_pp/fire_notifications.py index 1db2a20..416ee84 100644 --- a/activefires_pp/fire_notifications.py +++ b/activefires_pp/fire_notifications.py @@ -184,10 +184,7 @@ def run(self): LOG.debug("Message type not supported: %s", str(msg.type)) continue - product_name = msg.data.get('product') - product_list = self.options.get('products') - if product_list and product_name and product_name not in product_list: - LOG.info('Product %s will not generate a notification!', product_name) + if not self._product_name_supported(msg): continue output_msg = self.notify_end_users(msg) @@ -197,6 +194,16 @@ def run(self): else: LOG.debug("No message to send") + def _product_name_supported(self, incoming_msg): + """Check that the product name is supported via the configuration.""" + product_name = incoming_msg.data.get('product') + product_list = self.options.get('products') + if product_list and product_name and product_name not in product_list: + LOG.info('Product %s will not generate a notification!', product_name) + return False + + return True + def notify_end_users(self, msg): """Send notifications to configured end users (mail and text messages).""" LOG.debug("Start sending notifications to configured end users.") diff --git a/activefires_pp/tests/test_fire_notifications.py b/activefires_pp/tests/test_fire_notifications.py index 91306ac..4cce314 100644 --- a/activefires_pp/tests/test_fire_notifications.py +++ b/activefires_pp/tests/test_fire_notifications.py @@ -26,7 +26,7 @@ from unittest.mock import patch import yaml import io -# from posttroll.message import Message +from posttroll.message import Message from activefires_pp.fire_notifications import EndUserNotifier from activefires_pp.fire_notifications import EndUserNotifierRegional @@ -106,6 +106,8 @@ NATIONAL_TEST_MESSAGE = """pytroll://VIIRS/L2/Fires/PP/National file safusr.u@lxserv1043.smhi.se 2021-04-19T11:16:49.519087 v1.01 application/json {"start_time": "2021-04-16T12:29:53", "end_time": "2021-04-16T12:31:18", "orbit_number": 1, "platform_name": "NOAA-20", "sensor": "viirs", "data_processing_level": "2", "variant": "DR", "orig_orbit_number": 17666, "uri": "ssh://lxserv1043.smhi.se//san1/polar_out/direct_readout/viirs_active_fires/filtered/AFIMG_j01_d20210416_t122953.geojson", "uid": "AFIMG_j01_d20210416_t122953.geojson", "type": "GEOJSON-filtered", "format": "geojson", "product": "afimg"}""" # noqa +NATIONAL_TEST_MESSAGE2 = """pytroll://VIIRS/L2/Fires/PP/National file safusr.u@lxserv1043.smhi.se 2021-04-19T11:16:49.519087 v1.01 application/json {"start_time": "2021-04-16T12:29:53", "end_time": "2021-04-16T12:31:18", "orbit_number": 1, "platform_name": "NOAA-20", "sensor": "viirs", "data_processing_level": "2", "variant": "DR", "orig_orbit_number": 17666, "uri": "ssh://lxserv1043.smhi.se//san1/polar_out/direct_readout/viirs_active_fires/filtered/AFIMG_j01_d20210416_t122953.geojson", "uid": "AFIMG_j01_d20210416_t122953_somegeoid.geojson", "type": "GEOJSON-filtered", "format": "geojson", "product": "afimg_somegeoid"}""" # noqa + class MyNetrcMock(object): """Mocking the handling of secrets via the .netrc file.""" @@ -122,6 +124,52 @@ def authenticators(self, host): class TestNotifyEndUsers(unittest.TestCase): """Test notifications on National fires.""" + @patch('activefires_pp.fire_notifications.netrc') + @patch('activefires_pp.fire_notifications.socket.gethostname') + @patch('activefires_pp.fire_notifications.read_config') + @patch('activefires_pp.fire_notifications.EndUserNotifier._setup_and_start_communication') + def test_check_incoming_message_product_name_ok(self, setup_comm, read_config, gethostname, netrc): + """Test the incoming message for the 'right' product (name).""" + secrets = MyNetrcMock() + netrc.return_value = secrets + gethostname.return_value = 'default' + + myconfigfile = "/my/config/file/path" + natstream = io.StringIO(NAT_CONFIG) + + read_config.return_value = yaml.load(natstream, Loader=yaml.UnsafeLoader) + + this = EndUserNotifier(myconfigfile) + + input_msg = Message.decode(rawstr=NATIONAL_TEST_MESSAGE) + + result = this._product_name_supported(input_msg) + + assert result is True + + @patch('activefires_pp.fire_notifications.netrc') + @patch('activefires_pp.fire_notifications.socket.gethostname') + @patch('activefires_pp.fire_notifications.read_config') + @patch('activefires_pp.fire_notifications.EndUserNotifier._setup_and_start_communication') + def test_check_incoming_message_product_name_not_ok(self, setup_comm, read_config, gethostname, netrc): + """Test the incoming message for the 'right' product (name).""" + secrets = MyNetrcMock() + netrc.return_value = secrets + gethostname.return_value = 'default' + + myconfigfile = "/my/config/file/path" + natstream = io.StringIO(NAT_CONFIG) + + read_config.return_value = yaml.load(natstream, Loader=yaml.UnsafeLoader) + + this = EndUserNotifier(myconfigfile) + + input_msg = Message.decode(rawstr=NATIONAL_TEST_MESSAGE2) + + result = this._product_name_supported(input_msg) + + assert result is False + @patch('activefires_pp.fire_notifications.netrc') @patch('activefires_pp.fire_notifications.socket.gethostname') @patch('activefires_pp.fire_notifications.read_config') diff --git a/activefires_pp/tests/test_messaging.py b/activefires_pp/tests/test_messaging.py index 5bda7d1..ce65d61 100644 --- a/activefires_pp/tests/test_messaging.py +++ b/activefires_pp/tests/test_messaging.py @@ -1,11 +1,11 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- -# Copyright (c) 2021, 2022, 2023 Adam.Dybbroe +# Copyright (c) 2021 - 2023 Adam.Dybbroe # Author(s): -# Adam.Dybbroe +# Adam Dybbroe # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by