From de9d1f991b73a1d271b9303d1218e1c29b1cdaf8 Mon Sep 17 00:00:00 2001 From: Ben Thorner Date: Thu, 26 Aug 2021 10:42:20 +0100 Subject: [PATCH 1/7] Stop saying "areas" when we mean "area_ids" "areas" normally means an instance of BroadcastArea or similar, so we should be more accurate to avoid confusion. --- app/broadcast_areas/models.py | 4 +-- app/models/broadcast_message.py | 29 ++++++++++------------ tests/__init__.py | 4 +-- tests/app/main/views/test_broadcast.py | 12 ++++----- tests/app/models/test_broadcast_message.py | 4 +-- 5 files changed, 25 insertions(+), 28 deletions(-) diff --git a/app/broadcast_areas/models.py b/app/broadcast_areas/models.py index d7776c4ed..6084cd7c9 100644 --- a/app/broadcast_areas/models.py +++ b/app/broadcast_areas/models.py @@ -184,8 +184,8 @@ class CustomBroadcastArea(BaseBroadcastArea): class CustomBroadcastAreas(SerialisedModelCollection): model = CustomBroadcastArea - def __init__(self, *, areas, polygons): - self.items = areas + def __init__(self, *, area_ids, polygons): + self.items = area_ids self._polygons = polygons def __getitem__(self, index): diff --git a/app/models/broadcast_message.py b/app/models/broadcast_message.py index 112e234f7..4397e9593 100644 --- a/app/models/broadcast_message.py +++ b/app/models/broadcast_message.py @@ -88,7 +88,7 @@ class BroadcastMessage(JSONModel): @property def areas(self): - library_areas = self.get_areas(areas=self._dict['areas']) + library_areas = self.get_areas(self._dict['areas']) if library_areas: if len(library_areas) != len(self._dict['areas']): @@ -99,7 +99,7 @@ class BroadcastMessage(JSONModel): return library_areas return CustomBroadcastAreas( - areas=self._dict['areas'], + area_ids=self._dict['areas'], polygons=self._dict['simple_polygons'], ) @@ -201,9 +201,9 @@ class BroadcastMessage(JSONModel): return round_to_significant_figures(count, 1) - def get_areas(self, areas): + def get_areas(self, area_ids): return broadcast_area_libraries.get_areas( - areas + area_ids ) def get_simple_polygons(self, areas): @@ -216,20 +216,17 @@ class BroadcastMessage(JSONModel): # combined shapes to keep the point count down return polygons.smooth.simplify if len(areas) > 1 else polygons - def add_areas(self, *new_areas): - areas = list(OrderedSet( - self._dict['areas'] + list(new_areas) + def add_areas(self, *new_area_ids): + area_ids = list(OrderedSet( + self._dict['areas'] + list(new_area_ids) )) - simple_polygons = self.get_simple_polygons(areas=self.get_areas(areas=areas)) - self._update(areas=areas, simple_polygons=simple_polygons.as_coordinate_pairs_lat_long) + simple_polygons = self.get_simple_polygons(areas=self.get_areas(area_ids)) + self._update(areas=area_ids, simple_polygons=simple_polygons.as_coordinate_pairs_lat_long) - def remove_area(self, area_to_remove): - areas = [ - area for area in self._dict['areas'] - if area != area_to_remove - ] - simple_polygons = self.get_simple_polygons(areas=self.get_areas(areas=areas)) - self._update(areas=areas, simple_polygons=simple_polygons.as_coordinate_pairs_lat_long) + def remove_area(self, area_id): + area_ids = list(set(self._dict['areas']) - {area_id}) + simple_polygons = self.get_simple_polygons(areas=self.get_areas(area_ids)) + self._update(areas=area_ids, simple_polygons=simple_polygons.as_coordinate_pairs_lat_long) def _set_status_to(self, status): broadcast_message_api_client.update_broadcast_message_status( diff --git a/tests/__init__.py b/tests/__init__.py index 9763c0eef..5a209e27e 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -678,7 +678,7 @@ def broadcast_message_json( updated_at=None, approved_by_id=None, cancelled_by_id=None, - areas=None, + area_ids=None, simple_polygons=None, content=None, reference=None, @@ -696,7 +696,7 @@ def broadcast_message_json( 'reference': reference, 'personalisation': {}, - 'areas': areas or [ + 'areas': area_ids or [ 'ctry19-E92000001', 'ctry19-S92000003', ], 'simple_polygons': simple_polygons or [], diff --git a/tests/app/main/views/test_broadcast.py b/tests/app/main/views/test_broadcast.py index 589bc9205..31fc3f913 100644 --- a/tests/app/main/views/test_broadcast.py +++ b/tests/app/main/views/test_broadcast.py @@ -904,7 +904,7 @@ def test_preview_broadcast_areas_page( created_by_id=fake_uuid, service_id=SERVICE_ONE_ID, status='draft', - areas=areas_selected, + area_ids=areas_selected, ), ) client_request.login(active_user_create_broadcasts_permission) @@ -974,7 +974,7 @@ def test_preview_broadcast_areas_page_with_custom_polygons( created_by_id=fake_uuid, service_id=SERVICE_ONE_ID, status='draft', - areas=['Area one', 'Area two', 'Area three'], + area_ids=['Area one', 'Area two', 'Area three'], simple_polygons=polygons, ), ) @@ -1000,7 +1000,7 @@ def test_preview_broadcast_areas_page_with_custom_polygons( ] == expected_list_items -@pytest.mark.parametrize('areas, expected_list', ( +@pytest.mark.parametrize('area_ids, expected_list', ( ([], [ 'Countries', 'Demo areas', @@ -1053,7 +1053,7 @@ def test_choose_broadcast_library_page( service_one, fake_uuid, active_user_create_broadcasts_permission, - areas, + area_ids, expected_list, ): service_one['permissions'] += ['broadcast'] @@ -1065,7 +1065,7 @@ def test_choose_broadcast_library_page( created_by_id=fake_uuid, service_id=SERVICE_ONE_ID, status='draft', - areas=areas, + area_ids=area_ids, ), ) client_request.login(active_user_create_broadcasts_permission) @@ -1108,7 +1108,7 @@ def test_suggested_area_has_correct_link( created_by_id=fake_uuid, service_id=SERVICE_ONE_ID, status='draft', - areas=[ + area_ids=[ 'wd20-E05004299', # Pitville, a ward of Cheltenham ], ), diff --git a/tests/app/models/test_broadcast_message.py b/tests/app/models/test_broadcast_message.py index f15a7c721..6eacc3f25 100644 --- a/tests/app/models/test_broadcast_message.py +++ b/tests/app/models/test_broadcast_message.py @@ -11,7 +11,7 @@ def test_simple_polygons(fake_uuid): template_id=fake_uuid, status='draft', created_by_id=fake_uuid, - areas=[ + area_ids=[ # Hackney Central 'wd20-E05009372', # Hackney Wick @@ -56,7 +56,7 @@ def test_raises_for_missing_areas(fake_uuid): template_id=fake_uuid, status='draft', created_by_id=fake_uuid, - areas=[ + area_ids=[ 'wd20-E05009372', 'something else', ], From 11cbee5843ee457d0100b2fe416ebf322c35b503 Mon Sep 17 00:00:00 2001 From: Ben Thorner Date: Wed, 25 Aug 2021 17:17:05 +0100 Subject: [PATCH 2/7] Switch to using temporary "areas_2" API field Depends on: https://github.com/alphagov/notifications-api/pull/3312 This is part of a multi-stage migration where we want to repurpose the "areas" field in the existing API to something like "areas_2". --- app/models/broadcast_message.py | 28 +++++++++++------ tests/__init__.py | 8 ++--- tests/app/main/views/test_broadcast.py | 43 ++++++++++++++++---------- 3 files changed, 49 insertions(+), 30 deletions(-) diff --git a/app/models/broadcast_message.py b/app/models/broadcast_message.py index 4397e9593..409d338cf 100644 --- a/app/models/broadcast_message.py +++ b/app/models/broadcast_message.py @@ -88,19 +88,21 @@ class BroadcastMessage(JSONModel): @property def areas(self): - library_areas = self.get_areas(self._dict['areas']) + area_ids = self._dict['areas_2']['ids'] + polygons = self._dict['areas_2']['simple_polygons'] + library_areas = self.get_areas(area_ids) if library_areas: - if len(library_areas) != len(self._dict['areas']): + if len(library_areas) != len(area_ids): raise RuntimeError( - f'BroadcastMessage has {len(self._dict["areas"])} areas ' + f'BroadcastMessage has {len(area_ids)} areas ' f'but {len(library_areas)} found in the library' ) return library_areas return CustomBroadcastAreas( - area_ids=self._dict['areas'], - polygons=self._dict['simple_polygons'], + area_ids=area_ids, + polygons=polygons, ) @property @@ -218,15 +220,23 @@ class BroadcastMessage(JSONModel): def add_areas(self, *new_area_ids): area_ids = list(OrderedSet( - self._dict['areas'] + list(new_area_ids) + self._dict['areas_2']['ids'] + list(new_area_ids) )) simple_polygons = self.get_simple_polygons(areas=self.get_areas(area_ids)) - self._update(areas=area_ids, simple_polygons=simple_polygons.as_coordinate_pairs_lat_long) + + self._update(areas_2={ + 'ids': area_ids, + 'simple_polygons': simple_polygons.as_coordinate_pairs_lat_long + }) def remove_area(self, area_id): - area_ids = list(set(self._dict['areas']) - {area_id}) + area_ids = list(set(self._dict['areas_2']['ids']) - {area_id}) simple_polygons = self.get_simple_polygons(areas=self.get_areas(area_ids)) - self._update(areas=area_ids, simple_polygons=simple_polygons.as_coordinate_pairs_lat_long) + + self._update(areas_2={ + 'ids': area_ids, + 'simple_polygons': simple_polygons.as_coordinate_pairs_lat_long + }) def _set_status_to(self, status): broadcast_message_api_client.update_broadcast_message_status( diff --git a/tests/__init__.py b/tests/__init__.py index 5a209e27e..e296d0c1f 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -696,10 +696,10 @@ def broadcast_message_json( 'reference': reference, 'personalisation': {}, - 'areas': area_ids or [ - 'ctry19-E92000001', 'ctry19-S92000003', - ], - 'simple_polygons': simple_polygons or [], + 'areas_2': { + 'ids': area_ids or ['ctry19-E92000001', 'ctry19-S92000003'], + 'simple_polygons': simple_polygons or [], + }, 'status': status, diff --git a/tests/app/main/views/test_broadcast.py b/tests/app/main/views/test_broadcast.py index 31fc3f913..32f7c7315 100644 --- a/tests/app/main/views/test_broadcast.py +++ b/tests/app/main/views/test_broadcast.py @@ -1395,7 +1395,10 @@ def test_add_broadcast_area( service_id=SERVICE_ONE_ID, broadcast_message_id=fake_uuid, data={ - 'areas': ['ctry19-E92000001', 'ctry19-S92000003', 'ctry19-W92000004'], 'simple_polygons': coordinates + 'areas_2': { + 'ids': ['ctry19-E92000001', 'ctry19-S92000003', 'ctry19-W92000004'], + 'simple_polygons': coordinates + } }, ) @@ -1450,12 +1453,14 @@ def test_add_broadcast_sub_area_district_view( service_id=SERVICE_ONE_ID, broadcast_message_id=fake_uuid, data={ - 'simple_polygons': coordinates, - 'areas': [ - # These two areas are on the broadcast already - 'ctry19-E92000001', - 'ctry19-S92000003', - ] + expected_selected + 'areas_2': { + 'simple_polygons': coordinates, + 'ids': [ + # These two areas are on the broadcast already + 'ctry19-E92000001', + 'ctry19-S92000003', + ] + expected_selected + } }, ) @@ -1488,14 +1493,16 @@ def test_add_broadcast_sub_area_county_view( service_id=SERVICE_ONE_ID, broadcast_message_id=fake_uuid, data={ - 'simple_polygons': coordinates, - 'areas': [ - # These two areas are on the broadcast already - 'ctry19-E92000001', - 'ctry19-S92000003', - ] + [ - 'ctyua19-E10000016' - ] + 'areas_2': { + 'simple_polygons': coordinates, + 'ids': [ + # These two areas are on the broadcast already + 'ctry19-E92000001', + 'ctry19-S92000003', + ] + [ + 'ctyua19-E10000016' + ] + } }, ) @@ -1532,8 +1539,10 @@ def test_remove_broadcast_area_page( service_id=SERVICE_ONE_ID, broadcast_message_id=fake_uuid, data={ - 'simple_polygons': coordinates, - 'areas': ['ctry19-S92000003'] + 'areas_2': { + 'simple_polygons': coordinates, + 'ids': ['ctry19-S92000003'] + }, }, ) From 73f31ef2fde856db8ef88c14e1a9de2e9db3d9fb Mon Sep 17 00:00:00 2001 From: Ben Thorner Date: Thu, 26 Aug 2021 10:56:04 +0100 Subject: [PATCH 3/7] DRY-up getting and setting area_ids Previously we just held the new area_ids in memory. Setting them on the object means we can reuse its functionality to get polygons and also avoids confusion if in future we try to continue using the object after calling "add_areas" or "remove_areas". --- app/models/broadcast_message.py | 33 ++++++++++++++++++--------------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/app/models/broadcast_message.py b/app/models/broadcast_message.py index 409d338cf..f8fdb8ba9 100644 --- a/app/models/broadcast_message.py +++ b/app/models/broadcast_message.py @@ -88,23 +88,30 @@ class BroadcastMessage(JSONModel): @property def areas(self): - area_ids = self._dict['areas_2']['ids'] polygons = self._dict['areas_2']['simple_polygons'] - library_areas = self.get_areas(area_ids) + library_areas = self.get_areas(self.area_ids) if library_areas: - if len(library_areas) != len(area_ids): + if len(library_areas) != len(self.area_ids): raise RuntimeError( - f'BroadcastMessage has {len(area_ids)} areas ' + f'BroadcastMessage has {len(self.area_ids)} areas ' f'but {len(library_areas)} found in the library' ) return library_areas return CustomBroadcastAreas( - area_ids=area_ids, + area_ids=self.area_ids, polygons=polygons, ) + @property + def area_ids(self): + return self._dict['areas_2']['ids'] + + @area_ids.setter + def area_ids(self, value): + self._dict['areas_2']['ids'] = value + @property def ancestor_areas(self): return sorted(set(self._ancestor_areas_iterator)) @@ -219,23 +226,19 @@ class BroadcastMessage(JSONModel): return polygons.smooth.simplify if len(areas) > 1 else polygons def add_areas(self, *new_area_ids): - area_ids = list(OrderedSet( - self._dict['areas_2']['ids'] + list(new_area_ids) - )) - simple_polygons = self.get_simple_polygons(areas=self.get_areas(area_ids)) + self.area_ids = list(OrderedSet(self.area_ids + list(new_area_ids))) self._update(areas_2={ - 'ids': area_ids, - 'simple_polygons': simple_polygons.as_coordinate_pairs_lat_long + 'ids': self.area_ids, + 'simple_polygons': self.simple_polygons.as_coordinate_pairs_lat_long }) def remove_area(self, area_id): - area_ids = list(set(self._dict['areas_2']['ids']) - {area_id}) - simple_polygons = self.get_simple_polygons(areas=self.get_areas(area_ids)) + self.area_ids = list(set(self._dict['areas_2']['ids']) - {area_id}) self._update(areas_2={ - 'ids': area_ids, - 'simple_polygons': simple_polygons.as_coordinate_pairs_lat_long + 'ids': self.area_ids, + 'simple_polygons': self.simple_polygons.as_coordinate_pairs_lat_long }) def _set_status_to(self, status): From ae7f23d4cbe9a679eb89e931a98f9d690463e319 Mon Sep 17 00:00:00 2001 From: Ben Thorner Date: Thu, 26 Aug 2021 11:53:55 +0100 Subject: [PATCH 4/7] DRY-up sending area updates to the API --- app/models/broadcast_message.py | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/app/models/broadcast_message.py b/app/models/broadcast_message.py index f8fdb8ba9..f2582b805 100644 --- a/app/models/broadcast_message.py +++ b/app/models/broadcast_message.py @@ -227,19 +227,11 @@ class BroadcastMessage(JSONModel): def add_areas(self, *new_area_ids): self.area_ids = list(OrderedSet(self.area_ids + list(new_area_ids))) - - self._update(areas_2={ - 'ids': self.area_ids, - 'simple_polygons': self.simple_polygons.as_coordinate_pairs_lat_long - }) + self._update_areas() def remove_area(self, area_id): self.area_ids = list(set(self._dict['areas_2']['ids']) - {area_id}) - - self._update(areas_2={ - 'ids': self.area_ids, - 'simple_polygons': self.simple_polygons.as_coordinate_pairs_lat_long - }) + self._update_areas() def _set_status_to(self, status): broadcast_message_api_client.update_broadcast_message_status( @@ -248,6 +240,12 @@ class BroadcastMessage(JSONModel): service_id=self.service_id, ) + def _update_areas(self): + self._update(areas_2={ + 'ids': self.area_ids, + 'simple_polygons': self.simple_polygons.as_coordinate_pairs_lat_long + }) + def _update(self, **kwargs): broadcast_message_api_client.update_broadcast_message( broadcast_message_id=self.id, From 7dbe3afa19e29ff793213ced0d1360d0e677f70c Mon Sep 17 00:00:00 2001 From: Ben Thorner Date: Thu, 26 Aug 2021 11:55:00 +0100 Subject: [PATCH 5/7] Include area names in data we send to API These will be used as a fallback for display in gov.uk/alerts. It also helps to have them in the DB to aid in quickly identifying where an alert was sent, which is hard from the IDs. We will look at backfilling names for past alerts in future work. --- app/models/broadcast_message.py | 1 + tests/app/main/views/test_broadcast.py | 56 ++++++++++++++------------ 2 files changed, 31 insertions(+), 26 deletions(-) diff --git a/app/models/broadcast_message.py b/app/models/broadcast_message.py index f2582b805..6fc3fd8fd 100644 --- a/app/models/broadcast_message.py +++ b/app/models/broadcast_message.py @@ -243,6 +243,7 @@ class BroadcastMessage(JSONModel): def _update_areas(self): self._update(areas_2={ 'ids': self.area_ids, + 'names': [area.name for area in self.areas], 'simple_polygons': self.simple_polygons.as_coordinate_pairs_lat_long }) diff --git a/tests/app/main/views/test_broadcast.py b/tests/app/main/views/test_broadcast.py index 32f7c7315..9d51c6725 100644 --- a/tests/app/main/views/test_broadcast.py +++ b/tests/app/main/views/test_broadcast.py @@ -1397,31 +1397,32 @@ def test_add_broadcast_area( data={ 'areas_2': { 'ids': ['ctry19-E92000001', 'ctry19-S92000003', 'ctry19-W92000004'], + 'names': ['England', 'Scotland', 'Wales'], 'simple_polygons': coordinates } }, ) -@pytest.mark.parametrize('post_data, expected_selected', ( - ({ - 'select_all': 'y', - 'areas': [ - 'wd20-S13002845', - ] - }, [ - 'lad20-S12000033', - # wd20-S13002845 is ignored because the user chose ‘Select all…’ - ]), - ({ - 'areas': [ - 'wd20-S13002845', - 'wd20-S13002836', - ] - }, [ - 'wd20-S13002845', - 'wd20-S13002836', - ]), +@pytest.mark.parametrize('post_data, expected_data', ( + ( + { + 'select_all': 'y', 'areas': ['wd20-S13002845'] + }, + { + # wd20-S13002845 is ignored because the user chose ‘Select all…’ + 'ids': ['lad20-S12000033'], 'names': ['Aberdeen City'] + } + ), + ( + { + 'areas': ['wd20-S13002845', 'wd20-S13002836'] + }, + { + 'ids': ['wd20-S13002845', 'wd20-S13002836'], + 'names': ['Bridge of Don', 'Airyhall/Broomhill/Garthdee'], + } + ), )) def test_add_broadcast_sub_area_district_view( client_request, @@ -1430,7 +1431,7 @@ def test_add_broadcast_sub_area_district_view( mock_update_broadcast_message, fake_uuid, post_data, - expected_selected, + expected_data, mocker, active_user_create_broadcasts_permission, ): @@ -1449,17 +1450,18 @@ def test_add_broadcast_sub_area_district_view( area_slug='lad20-S12000033', _data=post_data, ) + + # These two areas are on the broadcast already + expected_data['ids'] = ['ctry19-E92000001', 'ctry19-S92000003'] + expected_data['ids'] + expected_data['names'] = ['England', 'Scotland'] + expected_data['names'] + mock_update_broadcast_message.assert_called_once_with( service_id=SERVICE_ONE_ID, broadcast_message_id=fake_uuid, data={ 'areas_2': { 'simple_polygons': coordinates, - 'ids': [ - # These two areas are on the broadcast already - 'ctry19-E92000001', - 'ctry19-S92000003', - ] + expected_selected + **expected_data, } }, ) @@ -1501,7 +1503,8 @@ def test_add_broadcast_sub_area_county_view( 'ctry19-S92000003', ] + [ 'ctyua19-E10000016' - ] + ], + 'names': ['England', 'Scotland', 'Kent'] } }, ) @@ -1541,6 +1544,7 @@ def test_remove_broadcast_area_page( data={ 'areas_2': { 'simple_polygons': coordinates, + 'names': ['Scotland'], 'ids': ['ctry19-S92000003'] }, }, From 39a1212508c27a5c21f8b027fef3fb409a28657f Mon Sep 17 00:00:00 2001 From: Ben Thorner Date: Thu, 26 Aug 2021 12:47:36 +0100 Subject: [PATCH 6/7] Switch existing command to standard approach This is the suggested approach in the documentation [1] and using it makes it clearer what's going on and to add other commands with arguments, which we'll do in the next commit. [1]: https://flask.palletsprojects.com/en/2.0.x/cli/#custom-commands --- app/commands.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/app/commands.py b/app/commands.py index 026be777a..63a3e218b 100644 --- a/app/commands.py +++ b/app/commands.py @@ -1,6 +1,10 @@ +import click from flask import current_app +from flask.cli import with_appcontext +@click.command('list-routes') +@with_appcontext def list_routes(): """List URLs of all application routes.""" for rule in sorted(current_app.url_map.iter_rules(), key=lambda r: r.rule): @@ -8,4 +12,4 @@ def list_routes(): def setup_commands(application): - application.cli.command('list-routes')(list_routes) + application.cli.add_command(list_routes) From 9667433b7e2e23540eaeb25edf1a558955e97d7e Mon Sep 17 00:00:00 2001 From: Ben Thorner Date: Thu, 26 Aug 2021 12:50:47 +0100 Subject: [PATCH 7/7] Add temporary command to migrate data for "areas" This will be run with a CSV of all broadcast messages. Since very few users are creating or updating broadcasts, it's highly unlikely we'll encounter a race condition during the update. --- app/commands.py | 17 +++++++++++++++++ app/models/broadcast_message.py | 14 +++++++++++--- 2 files changed, 28 insertions(+), 3 deletions(-) diff --git a/app/commands.py b/app/commands.py index 63a3e218b..67a971f6c 100644 --- a/app/commands.py +++ b/app/commands.py @@ -11,5 +11,22 @@ def list_routes(): print("{:10} {}".format(", ".join(rule.methods - set(['OPTIONS', 'HEAD'])), rule.rule)) # noqa +@click.command() +@click.argument('csv_path') +@with_appcontext +def tmp_backfill_areas(csv_path, dry_run=True): + import csv + + from app.models.broadcast_message import BroadcastMessage + + for id, service_id in csv.reader(open(csv_path)): + message = BroadcastMessage.from_id(id, service_id=service_id) + print(f'Updating {message.id}') # noqa + + if not dry_run: + message._update_areas(force_override=True) + + def setup_commands(application): application.cli.add_command(list_routes) + application.cli.add_command(tmp_backfill_areas) diff --git a/app/models/broadcast_message.py b/app/models/broadcast_message.py index 6fc3fd8fd..c794b7461 100644 --- a/app/models/broadcast_message.py +++ b/app/models/broadcast_message.py @@ -240,12 +240,20 @@ class BroadcastMessage(JSONModel): service_id=self.service_id, ) - def _update_areas(self): - self._update(areas_2={ + def _update_areas(self, force_override=False): + areas_2 = { 'ids': self.area_ids, 'names': [area.name for area in self.areas], 'simple_polygons': self.simple_polygons.as_coordinate_pairs_lat_long - }) + } + + data = {'areas_2': areas_2} + + # TEMPORARY: while we migrate to a new format for "areas" + if force_override: + data['force_override'] = True + + self._update(**data) def _update(self, **kwargs): broadcast_message_api_client.update_broadcast_message(