Refactor BroadcastAreas to reuse common methods

This commit makes an abstract base class for broadcast areas, so that
methods and properties which are common between `BroadcastArea`s (those
which come from our library) and `CustomBroadcastArea`s (those supplied
via the API) can be shared.
This commit is contained in:
Chris Hill-Scott
2021-03-19 16:21:43 +00:00
parent ddfc60cde6
commit fc75d60f65

View File

@@ -1,4 +1,5 @@
import math
from abc import ABC, abstractmethod
from notifications_utils.formatters import formatted_list
from notifications_utils.polygons import Polygons
@@ -34,7 +35,52 @@ class GetItemByIdMixin:
raise KeyError(id)
class BroadcastArea(SortableMixin):
class BaseBroadcastArea(ABC):
@property
@abstractmethod
def simple_polygons(self):
pass
@property
@abstractmethod
def polygons(self):
pass
@property
@abstractmethod
def count_of_phones(self):
pass
@cached_property
def simple_polygons_with_bleed(self):
return self.simple_polygons.bleed_by(self.estimated_bleed_in_degrees)
@cached_property
def phone_density(self):
if not self.polygons.estimated_area:
return 0
return self.count_of_phones / self.polygons.estimated_area
@property
def estimated_bleed_in_m(self):
'''
Estimates the amount of bleed based on the population of an
area. Higher density areas tend to have short range masts, so
the bleed is low (down to 500m). Lower density areas have longer
range masts, so the typical bleed will be high (up to 5,000m).
'''
if self.phone_density < 1:
return Polygons.approx_bleed_in_degrees * Polygons.approx_metres_to_degree
estimated_bleed = 5_900 - (math.log(self.phone_density, 10) * 1_250)
return max(500, min(estimated_bleed, 5000))
@property
def estimated_bleed_in_degrees(self):
return self.estimated_bleed_in_m / Polygons.approx_metres_to_degree
class BroadcastArea(BaseBroadcastArea, SortableMixin):
def __init__(self, row):
self.id, self.name, self._count_of_phones, self.library_id = row
@@ -51,10 +97,6 @@ class BroadcastArea(SortableMixin):
BroadcastAreasRepository().get_simple_polygons_for_area(self.id)
)
@cached_property
def simple_polygons_with_bleed(self):
return self.simple_polygons.bleed_by(self.estimated_bleed_in_degrees)
@cached_property
def sub_areas(self):
return [
@@ -74,27 +116,6 @@ class BroadcastArea(SortableMixin):
# https://www.pivotaltracker.com/story/show/174837293
return self._count_of_phones or 0
@cached_property
def phone_density(self):
return self.count_of_phones / self.polygons.estimated_area
@property
def estimated_bleed_in_m(self):
'''
Estimates the amount of bleed based on the population of an
area. Higher density areas tend to have short range masts, so
the bleed is low (down to 500m). Lower density areas have longer
range masts, so the typical bleed will be high (up to 5,000m).
'''
if self.phone_density < 1:
return Polygons.approx_bleed_in_degrees * Polygons.approx_metres_to_degree
estimated_bleed = 5_900 - (math.log(self.phone_density, 10) * 1_250)
return max(500, min(estimated_bleed, 5000))
@property
def estimated_bleed_in_degrees(self):
return self.estimated_bleed_in_m / Polygons.approx_metres_to_degree
@cached_property
def parents(self):
return list(filter(None, self._parents_iterator))
@@ -116,7 +137,7 @@ class BroadcastArea(SortableMixin):
id = parent_broadcast_area.id
class CustomBroadcastArea:
class CustomBroadcastArea(BaseBroadcastArea):
# We dont yet have a way to estimate the number of phones in a
# user-defined polygon
@@ -136,13 +157,6 @@ class CustomBroadcastArea:
simple_polygons = polygons
@cached_property
def simple_polygons_with_bleed(self):
# We dont yet have a way of working out the population density
# of a custom area, so for now we have to use an average number
# to estimate the amount of bleed
return self.simple_polygons.bleed_by(Polygons.approx_bleed_in_degrees)
class CustomBroadcastAreas(SerialisedModelCollection):
model = CustomBroadcastArea