mirror of
https://github.com/GSA/notifications-admin.git
synced 2026-09-10 18:22:37 -04:00
Give estimates of the number of phones in a broadcast area
We need to give people a better feel for the consequences of broadcasting an alert. We’ve seen in research that some users will assume it is subscription based, or opt-in, rather than going to every phone in the area. I reckon that the most effective way to communicate this is to put some numbers next to the areas, to give people an idea of how many people will get alerted. We can estimate how many phones are in an area by: - taking the population of all electoral wards in that area - multiplying it by the percentage of people who own an internet connected phone[1] The Office for National Statistics publish both these datasets. The number of people who own an intenet connected phone varies a lot by age. Since the population data for each ward is broken down by age we can factor this in. Simplified, the calculation looks like this: - take the _Abbey_ ward of _Barking and Dagenham_ - in this ward there are 26 people aged 80 - 40% of people over 65 have an internet-connected phone - therefore 10 of these 80-year-olds would be likely to receive a broadcast - (repeat for all other ages) These numbers won’t be exact, but should be enough to give people a feel for the severity of what they’re about to do. We can see if they acheive this aim in user research. 1. This is a proxy for the number of people who are likely to have a 4G capable phone, because only 4G capable phones will be receiving broadcasts to begin with
This commit is contained in:
+4
-1
@@ -27,7 +27,10 @@
|
||||
!tests/non_spreadsheet_files/actually_a_png.xlsx
|
||||
!tests/spreadsheet_files/excel 2007.xlsx
|
||||
!app/broadcast_areas/source_files/Lower_Tier_Local_Authority_to_Upper_Tier_Local_Authority__December_2019__Lookup_in_England_and_Wales.csv
|
||||
|
||||
!app/broadcast_areas/source_files/MYE1-2019.csv
|
||||
!app/broadcast_areas/source_files/Mid-2019_Persons_England_Wales.csv
|
||||
!app/broadcast_areas/source_files/Mid-2019_Persons_Scotland.csv
|
||||
!app/broadcast_areas/source_files/Ward-2014_Northern_Ireland.csv
|
||||
|
||||
# Byte-compiled / optimized / DLL files
|
||||
__pycache__/
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
from notifications_utils.serialised_model import SerialisedModelCollection
|
||||
from werkzeug.utils import cached_property
|
||||
|
||||
from .constants import CITY_OF_LONDON
|
||||
from .polygons import Polygons
|
||||
from .repo import BroadcastAreasRepository
|
||||
|
||||
@@ -27,7 +28,7 @@ class GetItemByIdMixin:
|
||||
class BroadcastArea(SortableMixin):
|
||||
|
||||
def __init__(self, row):
|
||||
self.id, self.name = row
|
||||
self.id, self.name, self._count_of_phones = row
|
||||
|
||||
def __eq__(self, other):
|
||||
return self.id == other.id
|
||||
@@ -51,6 +52,16 @@ class BroadcastArea(SortableMixin):
|
||||
for row in BroadcastAreasRepository().get_all_areas_for_group(self.id)
|
||||
]
|
||||
|
||||
@property
|
||||
def count_of_phones(self):
|
||||
if self.id.endswith(CITY_OF_LONDON.WARDS):
|
||||
return CITY_OF_LONDON.DAYTIME_POPULATION * (
|
||||
self.polygons.estimated_area / CITY_OF_LONDON.AREA_SQUARE_MILES
|
||||
)
|
||||
if self.sub_areas:
|
||||
return sum(area.count_of_phones or 0 for area in self.sub_areas)
|
||||
return self._count_of_phones
|
||||
|
||||
|
||||
class BroadcastAreaLibrary(SerialisedModelCollection, SortableMixin, GetItemByIdMixin):
|
||||
|
||||
|
||||
Binary file not shown.
@@ -0,0 +1,40 @@
|
||||
import math
|
||||
|
||||
SMARTPHONE_OWNERSHIP_BY_AGE_RANGE = {
|
||||
# If no children have a phone when they’re born but 100% of
|
||||
# children have a phone by age 16 then 50% is a rough
|
||||
# approximation of how many children have phones
|
||||
(0, 15): 0.50,
|
||||
# https://www.finder.com/uk/mobile-internet-statistics
|
||||
(16, 24): 1.00,
|
||||
(25, 34): 0.97,
|
||||
(35, 44): 0.91,
|
||||
(45, 54): 0.88,
|
||||
(55, 64): 0.73,
|
||||
(65, math.inf): 0.40,
|
||||
}
|
||||
|
||||
MEDIAN_AGE_UK = 40
|
||||
|
||||
for min, max in SMARTPHONE_OWNERSHIP_BY_AGE_RANGE.keys():
|
||||
if min <= MEDIAN_AGE_UK <= max:
|
||||
MEDIAN_AGE_RANGE_UK = (min, max)
|
||||
|
||||
|
||||
class CITY_OF_LONDON:
|
||||
WARDS = (
|
||||
'E05009289', 'E05009290', 'E05009291', 'E05009292', 'E05009293',
|
||||
'E05009294', 'E05009295', 'E05009296', 'E05009297', 'E05009298',
|
||||
'E05009299', 'E05009300', 'E05009301', 'E05009302', 'E05009303',
|
||||
'E05009304', 'E05009305', 'E05009306', 'E05009307', 'E05009308',
|
||||
'E05009309', 'E05009310', 'E05009311', 'E05009312',
|
||||
)
|
||||
# https://data.london.gov.uk/blog/daytime-population-of-london-2014/
|
||||
DAYTIME_POPULATION = 553_000
|
||||
# Approx area of the polygons we’re storing, not the actual area
|
||||
AREA_SQUARE_MILES = 1.78
|
||||
|
||||
|
||||
class BRYHER:
|
||||
WD20_CODE = 'E05011090'
|
||||
POPULATION = 84
|
||||
@@ -7,6 +7,13 @@ from pathlib import Path
|
||||
import geojson
|
||||
from notifications_utils.formatters import formatted_list
|
||||
|
||||
from constants import (
|
||||
BRYHER,
|
||||
CITY_OF_LONDON,
|
||||
MEDIAN_AGE_RANGE_UK,
|
||||
MEDIAN_AGE_UK,
|
||||
SMARTPHONE_OWNERSHIP_BY_AGE_RANGE,
|
||||
)
|
||||
from polygons import Polygons
|
||||
from repo import BroadcastAreasRepository
|
||||
|
||||
@@ -54,6 +61,46 @@ def polygons_and_simplified_polygons(feature):
|
||||
)
|
||||
|
||||
|
||||
def estimate_number_of_smartphones_in_area(country_or_ward_code):
|
||||
|
||||
if country_or_ward_code in CITY_OF_LONDON.WARDS:
|
||||
# We don’t have population figures for wards of the City of
|
||||
# London. We’ll leave it empty here and estimate on the fly
|
||||
# later based on physical area.
|
||||
print(f' Population: N/A') # noqa: T001
|
||||
return None
|
||||
|
||||
# For some reason Bryher is the only ward missing population data, so we
|
||||
# need to hard code it. For simplicity, let’s assume all 84 people who
|
||||
# live on Bryher are 40 years old
|
||||
if country_or_ward_code == BRYHER.WD20_CODE:
|
||||
return BRYHER.POPULATION * SMARTPHONE_OWNERSHIP_BY_AGE_RANGE[MEDIAN_AGE_RANGE_UK]
|
||||
|
||||
if country_or_ward_code not in area_to_population_mapping:
|
||||
raise ValueError(f'No population data for {country_or_ward_code}')
|
||||
|
||||
population = area_to_population_mapping[country_or_ward_code]
|
||||
population_by_range = {}
|
||||
|
||||
for range, ownership in SMARTPHONE_OWNERSHIP_BY_AGE_RANGE.items():
|
||||
min, max = range
|
||||
population_by_range[range] = sum(
|
||||
people
|
||||
for age, people in population
|
||||
if min <= age <= max
|
||||
) * ownership
|
||||
|
||||
total_population = sum(dict(population).values())
|
||||
total_phones = sum(population_by_range.values())
|
||||
|
||||
print( # noqa: T001
|
||||
f' Population:{total_population: 11,.0f}'
|
||||
f' Phones:{total_phones: 11,.0f}'
|
||||
)
|
||||
|
||||
return total_phones
|
||||
|
||||
|
||||
ctry19_filepath = source_files_path / "Countries.geojson"
|
||||
|
||||
# https://geoportal.statistics.gov.uk/datasets/wards-may-2020-boundaries-uk-bgc
|
||||
@@ -66,12 +113,21 @@ lad20_filepath = source_files_path / "Local Authorities May 2020.geojson"
|
||||
# https://geoportal.statistics.gov.uk/datasets/counties-and-unitary-authorities-december-2019-boundaries-uk-bgc
|
||||
ctyua19_filepath = source_files_path / "Counties_and_Unitary_Authorities__December_2019__Boundaries_UK_BGC.geojson"
|
||||
|
||||
|
||||
# http://geoportal.statistics.gov.uk/datasets/ward-to-westminster-parliamentary-constituency-to-local-authority-district-december-2019-lookup-in-the-united-kingdom/data
|
||||
wd_lad_map_filepath = source_files_path / "Electoral Wards and Local Authorities 2020.geojson"
|
||||
|
||||
# https://geoportal.statistics.gov.uk/datasets/lower-tier-local-authority-to-upper-tier-local-authority-december-2019-lookup-in-england-and-wales?where=LTLA19CD%20%3D%20%27E06000045%27
|
||||
ltla_utla_map_filepath = source_files_path / "Lower_Tier_Local_Authority_to_Upper_Tier_Local_Authority__December_2019__Lookup_in_England_and_Wales.csv" # noqa: E501
|
||||
|
||||
# https://www.ons.gov.uk/peoplepopulationandcommunity/populationandmigration/populationestimates/datasets/wardlevelmidyearpopulationestimatesexperimental
|
||||
population_filepath_england_wales = source_files_path / "Mid-2019_Persons_England_Wales.csv"
|
||||
# https://www.nrscotland.gov.uk/statistics-and-data/statistics/statistics-by-theme/population/population-estimates/2011-based-special-area-population-estimates/electoral-ward-population-estimates
|
||||
population_filepath_scotland = source_files_path / "Mid-2019_Persons_Scotland.csv"
|
||||
population_filepath_northern_ireland = source_files_path / "Ward-2014_Northern_Ireland.csv"
|
||||
population_filepath_uk = source_files_path / "MYE1-2019.csv"
|
||||
|
||||
|
||||
ward_code_to_la_mapping = {
|
||||
f["properties"]["WD19CD"]: f["properties"]["LAD19NM"]
|
||||
for f in geojson.loads(wd_lad_map_filepath.read_text())["features"]
|
||||
@@ -88,6 +144,24 @@ la_code_to_cty_id_mapping = {
|
||||
row['LTLA19CD']: row['UTLA19CD'] for row in ltla_utla_mapping_csv if row['LTLA19CD'] != row['UTLA19CD']
|
||||
}
|
||||
|
||||
area_to_population_mapping = {}
|
||||
|
||||
for population_filepath in (
|
||||
population_filepath_uk,
|
||||
population_filepath_england_wales,
|
||||
population_filepath_northern_ireland,
|
||||
population_filepath_scotland,
|
||||
):
|
||||
area_to_population_csv = csv.DictReader(population_filepath.open())
|
||||
for row in area_to_population_csv:
|
||||
area_to_population_mapping[row['ward']] = [
|
||||
(
|
||||
int(k) if k.isnumeric() else MEDIAN_AGE_UK,
|
||||
int(float(v.replace(',', '') or '0'))
|
||||
)
|
||||
for k, v in row.items() if k != 'ward'
|
||||
]
|
||||
|
||||
|
||||
def add_countries():
|
||||
dataset_id = 'ctry19'
|
||||
@@ -101,7 +175,7 @@ def add_countries():
|
||||
|
||||
areas_to_add = []
|
||||
for feature in dataset_geojson["features"]:
|
||||
f_id = 'ctry19-' + feature["properties"]['ctry19cd']
|
||||
f_id = feature["properties"]['ctry19cd']
|
||||
f_name = feature["properties"]['ctry19nm']
|
||||
|
||||
print() # noqa: T001
|
||||
@@ -111,9 +185,10 @@ def add_countries():
|
||||
polygons_and_simplified_polygons(feature["geometry"])
|
||||
)
|
||||
areas_to_add.append([
|
||||
f_id, f_name,
|
||||
f'ctry19-{f_id}', f_name,
|
||||
dataset_id, None,
|
||||
feature, simple_feature,
|
||||
estimate_number_of_smartphones_in_area(f_id),
|
||||
])
|
||||
|
||||
repo.insert_broadcast_areas(areas_to_add, keep_old_polygons)
|
||||
@@ -155,7 +230,8 @@ def _add_electoral_wards(dataset_id):
|
||||
areas_to_add.append([
|
||||
ward_id, ward_name,
|
||||
dataset_id, la_id,
|
||||
feature, simple_feature
|
||||
feature, simple_feature,
|
||||
estimate_number_of_smartphones_in_area(ward_code),
|
||||
])
|
||||
|
||||
except KeyError:
|
||||
@@ -187,7 +263,8 @@ def _add_local_authorities(dataset_id):
|
||||
dataset_id,
|
||||
'ctyua19-' + ctyua_id if ctyua_id else None,
|
||||
feature,
|
||||
simple_feature
|
||||
simple_feature,
|
||||
None,
|
||||
])
|
||||
repo.insert_broadcast_areas(areas_to_add, keep_old_polygons)
|
||||
|
||||
@@ -212,7 +289,8 @@ def _add_counties_and_unitary_authorities(dataset_id):
|
||||
areas_to_add.append([
|
||||
group_id, group_name,
|
||||
dataset_id, None,
|
||||
feature, simple_feature
|
||||
feature, simple_feature,
|
||||
None,
|
||||
])
|
||||
|
||||
repo.insert_broadcast_areas(areas_to_add, keep_old_polygons)
|
||||
@@ -237,6 +315,7 @@ most_detailed_polygons = formatted_list(
|
||||
before_each='',
|
||||
after_each='',
|
||||
)
|
||||
|
||||
print( # noqa: T001
|
||||
'\n'
|
||||
'DONE\n'
|
||||
|
||||
+13
-11
@@ -37,6 +37,7 @@ class BroadcastAreasRepository(object):
|
||||
name TEXT NOT NULL,
|
||||
broadcast_area_library_id TEXT NOT NULL,
|
||||
broadcast_area_library_group_id TEXT,
|
||||
count_of_phones INTEGER,
|
||||
|
||||
FOREIGN KEY (broadcast_area_library_id)
|
||||
REFERENCES broadcast_area_libraries(id),
|
||||
@@ -84,9 +85,10 @@ class BroadcastAreasRepository(object):
|
||||
areas_q = """
|
||||
INSERT INTO broadcast_areas (
|
||||
id, name,
|
||||
broadcast_area_library_id, broadcast_area_library_group_id
|
||||
broadcast_area_library_id, broadcast_area_library_group_id,
|
||||
count_of_phones
|
||||
)
|
||||
VALUES (?, ?, ?, ?)
|
||||
VALUES (?, ?, ?, ?, ?)
|
||||
"""
|
||||
|
||||
features_q = """
|
||||
@@ -98,9 +100,9 @@ class BroadcastAreasRepository(object):
|
||||
"""
|
||||
|
||||
with self.conn() as conn:
|
||||
for id, name, area_id, group, polygons, simple_polygons in areas:
|
||||
for id, name, area_id, group, polygons, simple_polygons, count_of_phones in areas:
|
||||
conn.execute(areas_q, (
|
||||
id, name, area_id, group,
|
||||
id, name, area_id, group, count_of_phones
|
||||
))
|
||||
if not keep_old_features:
|
||||
conn.execute(features_q, (
|
||||
@@ -152,7 +154,7 @@ class BroadcastAreasRepository(object):
|
||||
|
||||
def get_areas(self, area_ids):
|
||||
q = """
|
||||
SELECT id, name
|
||||
SELECT id, name, count_of_phones
|
||||
FROM broadcast_areas
|
||||
WHERE id IN ({})
|
||||
""".format(("?," * len(area_ids))[:-1])
|
||||
@@ -160,7 +162,7 @@ class BroadcastAreasRepository(object):
|
||||
results = self.query(q, *area_ids)
|
||||
|
||||
areas = [
|
||||
(row[0], row[1])
|
||||
(row[0], row[1], row[2])
|
||||
for row in results
|
||||
]
|
||||
|
||||
@@ -179,7 +181,7 @@ class BroadcastAreasRepository(object):
|
||||
if is_multi_tier_library:
|
||||
# only interested in areas with children - eg local authorities, counties, unitary authorities. not wards.
|
||||
q = """
|
||||
SELECT id, name
|
||||
SELECT id, name, count_of_phones
|
||||
FROM broadcast_areas
|
||||
JOIN (
|
||||
SELECT DISTINCT broadcast_area_library_group_id
|
||||
@@ -191,7 +193,7 @@ class BroadcastAreasRepository(object):
|
||||
else:
|
||||
# Countries don't have any children, so the above query wouldn't return anything.
|
||||
q = """
|
||||
SELECT id, name
|
||||
SELECT id, name, count_of_phones
|
||||
FROM broadcast_areas
|
||||
WHERE broadcast_area_library_id = ?
|
||||
"""
|
||||
@@ -199,13 +201,13 @@ class BroadcastAreasRepository(object):
|
||||
results = self.query(q, library_id)
|
||||
|
||||
return [
|
||||
(row[0], row[1])
|
||||
(row[0], row[1], row[2])
|
||||
for row in results
|
||||
]
|
||||
|
||||
def get_all_areas_for_group(self, group_id):
|
||||
q = """
|
||||
SELECT id, name
|
||||
SELECT id, name, count_of_phones
|
||||
FROM broadcast_areas
|
||||
WHERE broadcast_area_library_group_id = ?
|
||||
"""
|
||||
@@ -213,7 +215,7 @@ class BroadcastAreasRepository(object):
|
||||
results = self.query(q, group_id)
|
||||
|
||||
areas = [
|
||||
(row[0], row[1])
|
||||
(row[0], row[1], row[2])
|
||||
for row in results
|
||||
]
|
||||
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
ward,0,5,10,15,20,25,30,35,40,45,50,55,60,65,70,75,80,85,90
|
||||
E92000001,3299637,3538206,3354246,3090232,3487863,3801409,3807954,3733642,3414297,3715812,3907461,3670651,3111835,2796740,2779326,1940686,1439913,879778,517273
|
||||
W92000004,165542,183784,180819,172381,202402,208260,192954,184920,169556,199072,220480,217480,192853,182142,179286,126785,89769,53878,30516
|
||||
S92000003,271715,299316,294674,281958,347456,382255,369463,352522,319432,361458,401090,393123,344693,299444,278856,195951,142807,83429,43658
|
||||
N92000002,120369,128546,124127,112397,115359,122325,126761,124583,116254,125780,131984,124654,105804,89873,81399,61874,42839,25005,13734
|
||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,463 @@
|
||||
ward,all
|
||||
N08000101,3391.998826
|
||||
N08000102,4294.999284
|
||||
N08000103,3565.998752
|
||||
N08000104,3887.999475
|
||||
N08000105,3488.999756
|
||||
N08000106,3115.998786
|
||||
N08000107,2876.999072
|
||||
N08000108,3351.999363
|
||||
N08000109,3286.998855
|
||||
N08000110,3443.998656
|
||||
N08000111,2805.998884
|
||||
N08000112,3169
|
||||
N08000113,3358.999002
|
||||
N08000114,2960.999712
|
||||
N08000115,3090.999925
|
||||
N08000116,3874.999358
|
||||
N08000117,3338.999944
|
||||
N08000118,3054.99948
|
||||
N08000119,3044.998945
|
||||
N08000120,3274.999331
|
||||
N08000121,3330.999504
|
||||
N08000122,3069.99875
|
||||
N08000123,2937.998937
|
||||
N08000124,3383.998989
|
||||
N08000125,4635.998379
|
||||
N08000126,3576.999396
|
||||
N08000127,3638.999936
|
||||
N08000128,3177.998633
|
||||
N08000129,3113.9992
|
||||
N08000130,3643.999253
|
||||
N08000131,3270.999094
|
||||
N08000132,3117.999451
|
||||
N08000133,3349.999122
|
||||
N08000134,4791.999722
|
||||
N08000135,3478.999645
|
||||
N08000136,3818.998512
|
||||
N08000137,3073.999884
|
||||
N08000138,3552.999528
|
||||
N08000139,3241.9992
|
||||
N08000140,2759.998644
|
||||
N08000201,4774.999131
|
||||
N08000202,4576.998644
|
||||
N08000203,4183.999775
|
||||
N08000204,4184.999336
|
||||
N08000205,4630.998288
|
||||
N08000206,5430.999078
|
||||
N08000207,5070.9993
|
||||
N08000208,4415
|
||||
N08000209,6225.999885
|
||||
N08000210,4892.998676
|
||||
N08000211,5202.999096
|
||||
N08000212,5411.998432
|
||||
N08000213,4917.999003
|
||||
N08000214,4711.998572
|
||||
N08000215,4463.998704
|
||||
N08000216,4529
|
||||
N08000217,4750.998236
|
||||
N08000218,4648.99897
|
||||
N08000219,4476.99954
|
||||
N08000220,4923.998771
|
||||
N08000221,4629.998736
|
||||
N08000222,4427.9985
|
||||
N08000223,4148.999787
|
||||
N08000224,4808.99916
|
||||
N08000225,4971.998687
|
||||
N08000226,5115.998258
|
||||
N08000227,4411.998384
|
||||
N08000228,4692.998232
|
||||
N08000229,4841.999306
|
||||
N08000230,4998.99849
|
||||
N08000231,4715.999262
|
||||
N08000232,5307.99829
|
||||
N08000233,4097.99952
|
||||
N08000234,4745.999701
|
||||
N08000235,4689.999882
|
||||
N08000236,4875.99924
|
||||
N08000237,5811.999512
|
||||
N08000238,5049.999318
|
||||
N08000239,5131.999465
|
||||
N08000240,4793.998832
|
||||
N08000241,4941.999656
|
||||
N08000301,4733.999152
|
||||
N08000302,5494.997984
|
||||
N08000303,5858.998712
|
||||
N08000304,5397.997752
|
||||
N08000305,5049.998784
|
||||
N08000306,4983.998058
|
||||
N08000307,5801.999904
|
||||
N08000308,6423.997481
|
||||
N08000309,5124.997752
|
||||
N08000310,4762.99812
|
||||
N08000311,4912.997848
|
||||
N08000312,7117.997544
|
||||
N08000313,5812.997196
|
||||
N08000314,4285.999747
|
||||
N08000315,11293.99857
|
||||
N08000316,5481.999298
|
||||
N08000317,5324.997942
|
||||
N08000318,5781.99952
|
||||
N08000319,5659.999026
|
||||
N08000320,5756.998764
|
||||
N08000321,4563.9984
|
||||
N08000322,6367.999628
|
||||
N08000323,5390.999038
|
||||
N08000324,5076.99967
|
||||
N08000325,4727.99817
|
||||
N08000326,4494.9996
|
||||
N08000327,4658.999499
|
||||
N08000328,4621.998706
|
||||
N08000329,4808.99806
|
||||
N08000330,5045.998848
|
||||
N08000331,4634.99982
|
||||
N08000332,5170.998196
|
||||
N08000333,5030.9987
|
||||
N08000334,5028.998559
|
||||
N08000335,7661.998736
|
||||
N08000336,5610.999032
|
||||
N08000337,4702.998804
|
||||
N08000338,4384.998296
|
||||
N08000339,4869
|
||||
N08000340,4869.999925
|
||||
N08000341,4768.998512
|
||||
N08000342,6502.997228
|
||||
N08000343,6001.99968
|
||||
N08000344,4731.998472
|
||||
N08000345,5460.998568
|
||||
N08000346,4451.998182
|
||||
N08000347,5084.998232
|
||||
N08000348,5931.999941
|
||||
N08000349,5080.99904
|
||||
N08000350,5107.998384
|
||||
N08000351,4745.99793
|
||||
N08000352,6595.999728
|
||||
N08000353,5062.997562
|
||||
N08000354,4992.998868
|
||||
N08000355,4882.999128
|
||||
N08000356,4813.999904
|
||||
N08000357,5719.998193
|
||||
N08000358,10508.9961
|
||||
N08000359,6082.999776
|
||||
N08000360,4863.9984
|
||||
N08000401,3539.999718
|
||||
N08000402,3084.99962
|
||||
N08000403,4247.998599
|
||||
N08000404,3254.998692
|
||||
N08000405,3129.999327
|
||||
N08000406,3068.998698
|
||||
N08000407,3411.999518
|
||||
N08000408,3091.999222
|
||||
N08000409,3596.998851
|
||||
N08000410,3684.999264
|
||||
N08000411,3726.99932
|
||||
N08000412,2958.99934
|
||||
N08000413,3409.999194
|
||||
N08000414,3415.999797
|
||||
N08000415,3628.999767
|
||||
N08000416,3318.99975
|
||||
N08000417,3575.999664
|
||||
N08000418,3355.99929
|
||||
N08000419,3350.99896
|
||||
N08000420,3497.999788
|
||||
N08000421,4326.998739
|
||||
N08000422,3339.999765
|
||||
N08000423,3764.999628
|
||||
N08000424,3546.99954
|
||||
N08000425,3648.999743
|
||||
N08000426,3652.999812
|
||||
N08000427,2942.999829
|
||||
N08000428,3166.999825
|
||||
N08000429,3108.99904
|
||||
N08000430,3602.999159
|
||||
N08000431,2927.999692
|
||||
N08000432,3235.999861
|
||||
N08000433,3918.9995
|
||||
N08000434,3889.998912
|
||||
N08000435,2822.9993
|
||||
N08000436,3194.999184
|
||||
N08000437,3553.9998
|
||||
N08000438,3381.999482
|
||||
N08000439,4266.99834
|
||||
N08000440,3907.998614
|
||||
N08000501,3469.999268
|
||||
N08000502,3698.998833
|
||||
N08000503,4115.998656
|
||||
N08000504,3441.999202
|
||||
N08000505,3098.999104
|
||||
N08000506,3364.999201
|
||||
N08000507,4354.998434
|
||||
N08000508,3439.999169
|
||||
N08000509,3561.999246
|
||||
N08000510,3505.999134
|
||||
N08000511,3512.998944
|
||||
N08000512,3229.999212
|
||||
N08000513,4236.99936
|
||||
N08000514,4101.998502
|
||||
N08000515,3478.99888
|
||||
N08000516,3840.999893
|
||||
N08000517,3901.99972
|
||||
N08000518,3985.999866
|
||||
N08000519,4216.998588
|
||||
N08000520,3188.998935
|
||||
N08000521,4118.998884
|
||||
N08000522,3226.9995
|
||||
N08000523,3314.999688
|
||||
N08000524,4171.999976
|
||||
N08000525,3390.998908
|
||||
N08000526,2984.999524
|
||||
N08000527,3622.999296
|
||||
N08000528,3326.999431
|
||||
N08000529,4420.998754
|
||||
N08000530,3495.99977
|
||||
N08000531,3552.999966
|
||||
N08000532,4351.999267
|
||||
N08000533,3431.999507
|
||||
N08000534,3701.999488
|
||||
N08000535,4019.999123
|
||||
N08000536,3526.999836
|
||||
N08000537,3237.999804
|
||||
N08000538,3538.99968
|
||||
N08000539,3027.998912
|
||||
N08000540,3885.999978
|
||||
N08000601,2954.99925
|
||||
N08000602,2538.999136
|
||||
N08000603,2940.999994
|
||||
N08000604,2838.99947
|
||||
N08000605,3173.999348
|
||||
N08000606,2286.999894
|
||||
N08000607,2895.999131
|
||||
N08000608,3189.999132
|
||||
N08000609,3407.99886
|
||||
N08000610,2412.99949
|
||||
N08000611,2717.99915
|
||||
N08000612,2663.99924
|
||||
N08000613,2352.999358
|
||||
N08000614,2810.999583
|
||||
N08000615,2985.999856
|
||||
N08000616,2898.999057
|
||||
N08000617,2800.99974
|
||||
N08000618,2865.999317
|
||||
N08000619,2764.999614
|
||||
N08000620,2527.999088
|
||||
N08000621,2613.999423
|
||||
N08000622,3196
|
||||
N08000623,3205.999788
|
||||
N08000624,3268
|
||||
N08000625,3030.999842
|
||||
N08000626,3207.999813
|
||||
N08000627,3062.998898
|
||||
N08000628,2521.99949
|
||||
N08000629,3150.998949
|
||||
N08000630,2509.999128
|
||||
N08000631,2700
|
||||
N08000632,2653.99932
|
||||
N08000633,3168.999593
|
||||
N08000634,2161.999345
|
||||
N08000635,2377.999827
|
||||
N08000636,2858.99951
|
||||
N08000637,2720.998871
|
||||
N08000638,2499.999488
|
||||
N08000639,2321.999794
|
||||
N08000640,2630.999856
|
||||
N08000701,3700.9989
|
||||
N08000702,2784.999364
|
||||
N08000703,3168.999708
|
||||
N08000704,2926.999588
|
||||
N08000705,4199
|
||||
N08000706,3318.9991
|
||||
N08000707,3086.999265
|
||||
N08000708,3164.999145
|
||||
N08000709,3565.998964
|
||||
N08000710,3430.999104
|
||||
N08000711,3397.998745
|
||||
N08000712,3896.999064
|
||||
N08000713,3436.999644
|
||||
N08000714,2967.999366
|
||||
N08000715,3030.998997
|
||||
N08000716,3268.999162
|
||||
N08000717,3193.999369
|
||||
N08000718,3557.999712
|
||||
N08000719,3017.999604
|
||||
N08000720,3070.999029
|
||||
N08000721,3672.998514
|
||||
N08000722,3512.99949
|
||||
N08000723,3217.999752
|
||||
N08000724,3489.998728
|
||||
N08000725,3856.998548
|
||||
N08000726,3116.999968
|
||||
N08000727,3284.99942
|
||||
N08000728,3405.999036
|
||||
N08000729,3128.99886
|
||||
N08000730,3565.999374
|
||||
N08000731,3236.998779
|
||||
N08000732,2922.999615
|
||||
N08000733,3648.998712
|
||||
N08000734,2802.999667
|
||||
N08000735,2900.99921
|
||||
N08000736,3969.999648
|
||||
N08000737,2799.99977
|
||||
N08000738,2923.99936
|
||||
N08000739,3347.999564
|
||||
N08000740,4461.99866
|
||||
N08000801,2544.99904
|
||||
N08000802,3501.9999
|
||||
N08000803,3449.999322
|
||||
N08000804,3267.999108
|
||||
N08000805,3520.999616
|
||||
N08000806,3174.998814
|
||||
N08000807,3082.999347
|
||||
N08000808,3230.999616
|
||||
N08000809,3525.998616
|
||||
N08000810,2861.999664
|
||||
N08000811,3361.998695
|
||||
N08000812,3145.99896
|
||||
N08000813,3558.999537
|
||||
N08000814,3226.9987
|
||||
N08000815,3950.999358
|
||||
N08000816,2826.9997
|
||||
N08000817,3298.998528
|
||||
N08000818,3861.998459
|
||||
N08000819,3167.999586
|
||||
N08000820,3353.999814
|
||||
N08000821,3388.999448
|
||||
N08000822,3007.999944
|
||||
N08000823,3315.999312
|
||||
N08000824,3455.999319
|
||||
N08000825,3046.998991
|
||||
N08000826,3166.999407
|
||||
N08000827,3304.99944
|
||||
N08000828,4160.999676
|
||||
N08000829,4060.9998
|
||||
N08000830,3013.999056
|
||||
N08000831,3195.9992
|
||||
N08000832,2956.99992
|
||||
N08000833,2875.99984
|
||||
N08000834,3113.99976
|
||||
N08000835,2958.999804
|
||||
N08000836,4186.999258
|
||||
N08000837,3634.998482
|
||||
N08000838,3759.998919
|
||||
N08000839,3523.99878
|
||||
N08000840,3608.99892
|
||||
N08000901,3552.999962
|
||||
N08000902,3281.999749
|
||||
N08000903,3310.9993
|
||||
N08000904,3299.999724
|
||||
N08000905,3745.99914
|
||||
N08000906,4256.99932
|
||||
N08000907,3542.99907
|
||||
N08000908,3513.999762
|
||||
N08000909,3175.999245
|
||||
N08000910,3659.998749
|
||||
N08000911,3151.999896
|
||||
N08000912,3451.99992
|
||||
N08000913,4051.999014
|
||||
N08000914,3051.999153
|
||||
N08000915,3422.998789
|
||||
N08000916,3459.99912
|
||||
N08000917,3471.999228
|
||||
N08000918,3571.999794
|
||||
N08000919,3006.999552
|
||||
N08000920,2988.99916
|
||||
N08000921,3671.998876
|
||||
N08000922,3111.99971
|
||||
N08000923,4082.998698
|
||||
N08000924,3457.999667
|
||||
N08000925,2915.998944
|
||||
N08000926,2917.999328
|
||||
N08000927,3555.99909
|
||||
N08000928,3512.999952
|
||||
N08000929,3337.99884
|
||||
N08000930,3880.998628
|
||||
N08000931,3127.999148
|
||||
N08000932,3649.999788
|
||||
N08000933,3104.99904
|
||||
N08000934,3328.99905
|
||||
N08000935,3475.999378
|
||||
N08000936,3756.999087
|
||||
N08000937,3445.998893
|
||||
N08000938,3564.999126
|
||||
N08000939,3510
|
||||
N08000940,3403.999998
|
||||
N08001001,4005.999583
|
||||
N08001002,4014.999534
|
||||
N08001003,5044.998816
|
||||
N08001004,3946.999979
|
||||
N08001005,4247.999842
|
||||
N08001006,4074.998787
|
||||
N08001007,4589.99904
|
||||
N08001008,4052.999979
|
||||
N08001009,3985.99893
|
||||
N08001010,3788.999064
|
||||
N08001011,4186.998973
|
||||
N08001012,3968.999685
|
||||
N08001013,4345.998712
|
||||
N08001014,3483.999324
|
||||
N08001015,4330.999512
|
||||
N08001016,3958.998912
|
||||
N08001017,4362.99922
|
||||
N08001018,3785.998656
|
||||
N08001019,4738.999455
|
||||
N08001020,3920.999328
|
||||
N08001021,4219.999056
|
||||
N08001022,3892.99932
|
||||
N08001023,3834.999828
|
||||
N08001024,4747.999395
|
||||
N08001025,3778.998504
|
||||
N08001026,3782.998813
|
||||
N08001027,4502.99859
|
||||
N08001028,4186.999351
|
||||
N08001029,4582.999876
|
||||
N08001030,4839.99925
|
||||
N08001031,3936.999402
|
||||
N08001032,4062.999104
|
||||
N08001033,3848.999625
|
||||
N08001034,3695.998914
|
||||
N08001035,4409.998796
|
||||
N08001036,3906.998784
|
||||
N08001037,4739.998351
|
||||
N08001038,3881.999548
|
||||
N08001039,3932.998916
|
||||
N08001040,4715.99939
|
||||
N08001041,3988.998948
|
||||
N08001101,3179.999872
|
||||
N08001102,4049.998976
|
||||
N08001103,4781.998482
|
||||
N08001104,3764.999076
|
||||
N08001105,4176.999618
|
||||
N08001106,4380.999194
|
||||
N08001107,3987.999912
|
||||
N08001108,3557.999216
|
||||
N08001109,3600
|
||||
N08001110,4071.999736
|
||||
N08001111,3979.998902
|
||||
N08001112,3693.999357
|
||||
N08001113,3740.99885
|
||||
N08001114,3561.999293
|
||||
N08001115,3507.998652
|
||||
N08001116,4031.999424
|
||||
N08001117,4430.999216
|
||||
N08001118,3608.99946
|
||||
N08001119,3948.999977
|
||||
N08001120,4025.999646
|
||||
N08001121,3393.99957
|
||||
N08001122,3592.999312
|
||||
N08001123,4437
|
||||
N08001124,3505.9994
|
||||
N08001125,4064.99951
|
||||
N08001126,4117.99815
|
||||
N08001127,3445.999488
|
||||
N08001128,4154.999662
|
||||
N08001129,3661.998806
|
||||
N08001130,4769.99916
|
||||
N08001131,4066.99872
|
||||
N08001132,3628.999641
|
||||
N08001133,3769.999104
|
||||
N08001134,3359.999486
|
||||
N08001135,3626.999072
|
||||
N08001136,3683.998758
|
||||
N08001137,3933.998592
|
||||
N08001138,3735.998532
|
||||
N08001139,3786.999454
|
||||
N08001140,4235.998456
|
||||
|
@@ -1,5 +1,6 @@
|
||||
import itertools
|
||||
from datetime import datetime, timedelta
|
||||
from math import floor, log10
|
||||
|
||||
from notifications_utils.template import BroadcastPreviewTemplate
|
||||
from orderedset import OrderedSet
|
||||
@@ -15,6 +16,13 @@ from app.notify_client.broadcast_message_api_client import (
|
||||
from app.notify_client.service_api_client import service_api_client
|
||||
|
||||
|
||||
def round_to_significant_figures(x, n):
|
||||
return int(round(
|
||||
x,
|
||||
-int(floor(log10(abs(x)))) + (n - 1)
|
||||
))
|
||||
|
||||
|
||||
class BroadcastMessage(JSONModel):
|
||||
|
||||
ALLOWED_PROPERTIES = {
|
||||
@@ -113,6 +121,22 @@ class BroadcastMessage(JSONModel):
|
||||
def cancelled_by(self):
|
||||
return User.from_id(self.cancelled_by_id)
|
||||
|
||||
@property
|
||||
def count_of_phones(self):
|
||||
return round_to_significant_figures(
|
||||
sum(area.count_of_phones or 0 for area in self.areas),
|
||||
2
|
||||
)
|
||||
|
||||
@property
|
||||
def count_of_phones_likely(self):
|
||||
area_estimate = self.simple_polygons.estimated_area
|
||||
bleed_area_estimate = self.simple_polygons.bleed.estimated_area - area_estimate
|
||||
return round_to_significant_figures(
|
||||
self.count_of_phones * bleed_area_estimate / area_estimate,
|
||||
1
|
||||
)
|
||||
|
||||
def get_areas(self, areas):
|
||||
return broadcast_area_libraries.get_areas(
|
||||
*areas
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
</span>
|
||||
Will get
|
||||
<span class="visually-hidden">the</span>
|
||||
alert
|
||||
alert ({{ broadcast_message.count_of_phones|format_thousands }} phones)
|
||||
</li>
|
||||
<li class="area-key area-key--likely">
|
||||
<span class="visually-hidden">
|
||||
@@ -18,6 +18,7 @@
|
||||
the
|
||||
</span>
|
||||
alert
|
||||
({{ broadcast_message.count_of_phones_likely|format_thousands }} phones)
|
||||
</li>
|
||||
</ul>
|
||||
{% endmacro %}
|
||||
|
||||
@@ -351,8 +351,8 @@ def test_preview_broadcast_areas_page(
|
||||
normalize_spaces(item.text)
|
||||
for item in page.select('ul li.area-key')
|
||||
] == [
|
||||
'An area of 176,714.9 square miles Will get the alert',
|
||||
'An extra area of 3,052.8 square miles is Likely to get the alert',
|
||||
'An area of 176,714.9 square miles Will get the alert (44,000,000 phones)',
|
||||
'An extra area of 3,052.8 square miles is Likely to get the alert (800,000 phones)',
|
||||
]
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user