From 54bcf618da2c9320174f4784e96cc47c50a48b0e Mon Sep 17 00:00:00 2001 From: Chris Hill-Scott Date: Mon, 18 Oct 2021 11:42:18 +0100 Subject: [PATCH] Store the `event` field from CAP XML broadcasts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We don’t store everything that comes in the CAP XML when someone creates a broadcast via the API. One thing we do store is `` (in a column called `reference`) which is a unique (to the external system) identifier for the broadcast. We show this in the front end instead of the template name, because broadcasts created from the API don’t use templates. However this ID isn’t very friendly – the Environment Agency just supply a UUID. The Environment Agency also populate the `` field with some human readable text, for example: > 013 Issue Severe Flood Warning EA (013 is an area code which will be meaningful to the Flood Warning Service team) We should show this in the UI instead of the reference. The first step towards this is storing it in the database and returning it in the REST endpoints. Later we can have the admin app prefer `cap_event` over `reference`, where `cap_event` is present. We can’t backfill this data because we don’t keep a copy of the original XML. Seems like `` is a mandatory property of ``, so we don’t need to worry about the field being missing (`` is optional in CAP but we require it because it contains stuff like the areas which we need in order to send out the broadcast`). *** https://www.pivotaltracker.com/story/show/176927060 --- app/broadcast_message/translators.py | 1 + app/models.py | 2 ++ app/v2/broadcast/broadcast_schemas.py | 7 +++++++ app/v2/broadcast/post_broadcast.py | 1 + .../versions/0362_broadcast_msg_event.py | 21 +++++++++++++++++++ tests/app/broadcast_message/test_rest.py | 21 +++++++++++++++++++ tests/app/db.py | 6 ++++-- tests/app/v2/broadcast/test_post_broadcast.py | 1 + 8 files changed, 58 insertions(+), 2 deletions(-) create mode 100644 migrations/versions/0362_broadcast_msg_event.py diff --git a/app/broadcast_message/translators.py b/app/broadcast_message/translators.py index 8461d8649..b572462d7 100644 --- a/app/broadcast_message/translators.py +++ b/app/broadcast_message/translators.py @@ -7,6 +7,7 @@ def cap_xml_to_dict(cap_xml): return { "msgType": cap.alert.msgType.text, "reference": cap.alert.identifier.text, + "cap_event": cap.alert.info.event.text, "category": cap.alert.info.category.text, "expires": cap.alert.info.expires.text, "content": cap.alert.info.description.text, diff --git a/app/models.py b/app/models.py index 3ed4fa936..c6ebb282a 100644 --- a/app/models.py +++ b/app/models.py @@ -2319,6 +2319,7 @@ class BroadcastMessage(db.Model): api_key = db.relationship('ApiKey') reference = db.Column(db.String(255), nullable=True) + cap_event = db.Column(db.String(255), nullable=True) stubbed = db.Column(db.Boolean, nullable=False) @@ -2338,6 +2339,7 @@ class BroadcastMessage(db.Model): return { 'id': str(self.id), 'reference': self.reference, + 'cap_event': self.cap_event, 'service_id': str(self.service_id), diff --git a/app/v2/broadcast/broadcast_schemas.py b/app/v2/broadcast/broadcast_schemas.py index 99dd572d7..e69c6bf91 100644 --- a/app/v2/broadcast/broadcast_schemas.py +++ b/app/v2/broadcast/broadcast_schemas.py @@ -4,6 +4,7 @@ post_broadcast_schema = { "required": [ "msgType", "reference", + "cap_event", "category", "content", "areas", @@ -16,6 +17,12 @@ post_broadcast_schema = { "null", ], }, + "cap_event": { + "type": [ + "string", + "null", + ], + }, "category": { "type": "string", "enum": [ diff --git a/app/v2/broadcast/post_broadcast.py b/app/v2/broadcast/post_broadcast.py index 0a04bd26d..c52f68e65 100644 --- a/app/v2/broadcast/post_broadcast.py +++ b/app/v2/broadcast/post_broadcast.py @@ -51,6 +51,7 @@ def create_broadcast(): service_id=authenticated_service.id, content=broadcast_json['content'], reference=broadcast_json['reference'], + cap_event=broadcast_json['cap_event'], areas={ 'names': [ area['name'] for area in broadcast_json['areas'] diff --git a/migrations/versions/0362_broadcast_msg_event.py b/migrations/versions/0362_broadcast_msg_event.py new file mode 100644 index 000000000..04146958c --- /dev/null +++ b/migrations/versions/0362_broadcast_msg_event.py @@ -0,0 +1,21 @@ +""" + +Revision ID: 0362_broadcast_msg_event +Revises: 0361_new_user_bcast_permissions +Create Date: 2020-12-04 15:06:22.544803 + +""" +from alembic import op +import sqlalchemy as sa +from sqlalchemy.dialects import postgresql + +revision = '0362_broadcast_msg_event' +down_revision = '0361_new_user_bcast_permissions' + + +def upgrade(): + op.add_column('broadcast_message', sa.Column('cap_event', sa.String(length=255), nullable=True)) + + +def downgrade(): + op.drop_column('broadcast_message', 'cap_event') diff --git a/tests/app/broadcast_message/test_rest.py b/tests/app/broadcast_message/test_rest.py index 0bf97dc2e..fff5a814b 100644 --- a/tests/app/broadcast_message/test_rest.py +++ b/tests/app/broadcast_message/test_rest.py @@ -91,6 +91,26 @@ def test_get_broadcast_message_without_template( assert response['personalisation'] is None +def test_get_broadcast_message_with_event( + admin_request, + sample_broadcast_service +): + bm = create_broadcast_message( + service=sample_broadcast_service, + content='emergency broadcast content', + cap_event='001 example event', + ) + + response = admin_request.get( + 'broadcast_message.get_broadcast_message', + service_id=sample_broadcast_service.id, + broadcast_message_id=bm.id, + _expected_status=200 + ) + + assert response['cap_event'] == '001 example event' + + def test_get_broadcast_message_404s_if_message_doesnt_exist(admin_request, sample_broadcast_service): err = admin_request.get( 'broadcast_message.get_broadcast_message', @@ -255,6 +275,7 @@ def test_create_broadcast_message_can_be_created_from_content(admin_request, sam assert response['content'] == 'Some content\n€ŷŵ~\n\'\'""---' assert response['reference'] == 'abc123' assert response['template_id'] is None + assert response['cap_event'] is None def test_create_broadcast_message_400s_if_content_and_template_provided( diff --git a/tests/app/db.py b/tests/app/db.py index 227d305a2..1cb4d3d5e 100644 --- a/tests/app/db.py +++ b/tests/app/db.py @@ -1119,7 +1119,8 @@ def create_broadcast_message( starts_at=None, finishes_at=None, areas=None, - stubbed=False + stubbed=False, + cap_event=None, ): if template: service = template.service @@ -1148,7 +1149,8 @@ def create_broadcast_message( created_by_id=created_by.id if created_by else service.created_by_id, areas=areas or {'ids': [], 'simple_polygons': []}, content=content, - stubbed=stubbed + stubbed=stubbed, + cap_event=cap_event, ) db.session.add(broadcast_message) db.session.commit() diff --git a/tests/app/v2/broadcast/test_post_broadcast.py b/tests/app/v2/broadcast/test_post_broadcast.py index aa70ca2c7..7b43897c2 100644 --- a/tests/app/v2/broadcast/test_post_broadcast.py +++ b/tests/app/v2/broadcast/test_post_broadcast.py @@ -96,6 +96,7 @@ def test_valid_post_cap_xml_broadcast_returns_201( 'closely monitoring the situation throughout the night. ' ) assert response_json['reference'] == '50385fcb0ab7aa447bbd46d848ce8466E' + assert response_json['cap_event'] == '053/055 Issue Severe Flood Warning EA' assert response_json['created_at'] # datetime generated by the DB so can’t freeze it assert response_json['created_by_id'] is None assert response_json['finishes_at'] is None