diff --git a/hub/fixtures/areas.json b/hub/fixtures/areas.json index 261dd53d2..a71ccf1ee 100644 --- a/hub/fixtures/areas.json +++ b/hub/fixtures/areas.json @@ -5,7 +5,7 @@ "fields": { "name": "Constituency", "code": "WMC", - "area_type": "Constituency", + "area_type": "Westminster Constituency", "description": "Constituency" } }, @@ -15,7 +15,7 @@ "fields": { "name": "Constituency 2023", "code": "WMC23", - "area_type": "Constituency 2023", + "area_type": "Westminster Constituency", "description": "Constituency 2023" } }, diff --git a/hub/management/commands/import_areas.py b/hub/management/commands/import_areas.py index 982f55742..fe7949f24 100644 --- a/hub/management/commands/import_areas.py +++ b/hub/management/commands/import_areas.py @@ -11,6 +11,30 @@ class Command(BaseCommand): help = "Import basic area information from MaPit" + boundary_types = [ + { + "mapit_type": ["WMC"], + "name": "2010 Parliamentary Constituency", + "code": "WMC", + "area_type": "Westminster Constituency", + "description": "Westminster Parliamentary Constituency boundaries, as created in 2010", + }, + { + "mapit_type": ["LBO", "UTA", "COI", "LGD", "CTY", "MTD"], + "name": "Single Tier Councils", + "code": "STC", + "area_type": "Single Tier Council", + "description": "Single Tier Council", + }, + { + "mapit_type": ["DIS", "NMD"], + "name": "District Councils", + "code": "DIS", + "area_type": "District Council", + "description": "District Council", + }, + ] + def add_arguments(self, parser): parser.add_argument( "-q", "--quiet", action="store_true", help="Silence progress bars." @@ -18,39 +42,40 @@ def add_arguments(self, parser): def handle(self, quiet: bool = False, *args, **options): mapit_client = mapit.MapIt() - areas = mapit_client.areas_of_type(["WMC"]) - area_type, created = AreaType.objects.get_or_create( - name="2010 Parliamentary Constituency", - code="WMC", - area_type="Westminster Constituency", - description="Westminster Parliamentary Constituency boundaries, as created in 2010", - ) - - if not quiet: - print("Importing Areas") - for area in tqdm(areas, disable=quiet): - try: - geom = mapit_client.area_geometry(area["id"]) - geom = { - "type": "Feature", - "geometry": geom, - "properties": { - "PCON13CD": area["codes"]["gss"], - "name": area["name"], - "type": "WMC", - }, - } - geom = json.dumps(geom) - except mapit.NotFoundException: # pragma: no cover - print(f"could not find mapit area for {area['name']}") - geom = None - - a, created = Area.objects.get_or_create( - mapit_id=area["id"], - gss=area["codes"]["gss"], - name=area["name"], - area_type=area_type, + for b_type in self.boundary_types: + areas = mapit_client.areas_of_type(b_type["mapit_type"]) + area_type, created = AreaType.objects.get_or_create( + name=b_type["name"], + code=b_type["code"], + area_type=b_type["area_type"], + description=b_type["description"], ) - a.geometry = geom - a.save() + if not quiet: + print("Importing Areas") + for area in tqdm(areas, disable=quiet): + try: + geom = mapit_client.area_geometry(area["id"]) + geom = { + "type": "Feature", + "geometry": geom, + "properties": { + "PCON13CD": area["codes"]["gss"], + "name": area["name"], + "type": b_type["code"], + }, + } + geom = json.dumps(geom) + except mapit.NotFoundException: # pragma: no cover + print(f"could not find mapit area for {area['name']}") + geom = None + + a, created = Area.objects.get_or_create( + mapit_id=area["id"], + gss=area["codes"]["gss"], + name=area["name"], + area_type=area_type, + ) + + a.geometry = geom + a.save() diff --git a/hub/mixins.py b/hub/mixins.py index 39aa1264a..2757e0f22 100644 --- a/hub/mixins.py +++ b/hub/mixins.py @@ -159,6 +159,8 @@ def format_value(self, type, value): def data(self, as_dict=False, mp_name=False): headers = ["Constituency Name"] + if self.area_type().area_type != "Westminster Constituency": + headers = ["Council Name"] headers += map(lambda f: f["dataset"].label, self.filters()) headers += map( lambda f: f.get("header_label", f["label"]), self.columns(mp_name=mp_name) @@ -181,13 +183,17 @@ def data(self, as_dict=False, mp_name=False): shortcut if no filters/columns were requested: just return a single column of constituency names """ - if not cols: + if not cols or len(cols) == 0: areas = Area.objects area_type = self.area_type() if area_type is not None: areas = areas.filter(area_type=area_type) for area in areas.order_by("name"): data.append([area.name]) + if as_dict: + for area in Area.objects.filter(id__in=area_ids): + area_data[area.name]["area"] = area + return area_data return data """ diff --git a/hub/models.py b/hub/models.py index 2252795e3..8373e882d 100644 --- a/hub/models.py +++ b/hub/models.py @@ -412,9 +412,13 @@ def filter(self, query, **kwargs): class AreaType(models.Model): - VALID_AREA_TYPES = ["WMC", "WMC23"] + VALID_AREA_TYPES = ["WMC", "WMC23", "STC", "DIS"] - AREA_TYPES = [("westminster_constituency", "Westminster Constituency")] + AREA_TYPES = [ + ("westminster_constituency", "Westminster Constituency"), + ("single_tier_council", "Single Tier Council"), + ("district_council", "District Council"), + ] name = models.CharField(max_length=50, unique=True) code = models.CharField(max_length=10, unique=True) area_type = models.CharField(max_length=50, choices=AREA_TYPES) diff --git a/hub/static/js/explore.esm.js b/hub/static/js/explore.esm.js index bb55e3423..7b3e77ace 100644 --- a/hub/static/js/explore.esm.js +++ b/hub/static/js/explore.esm.js @@ -29,7 +29,14 @@ const app = createApp({ }, { slug: "WMC23", label: "Future constituencies" + }, { + slug: "STC", + label: "Single Tier councils" + }, { + slug: "DIS", + label: "District councils" }], + area_header_label: "constituencies", filters_applied: false, // were filters applied on the last Update? area_count: 0, // number of areas returned on last Update @@ -305,7 +312,7 @@ const app = createApp({ geomUrl() { let url = new URL(window.location.origin + '/exploregeometry.json') - if (["WMC", "WMC23"].includes(this.area_type)) { + if (["WMC", "WMC23", "DIS", "STC"].includes(this.area_type)) { url = new URL(window.location.origin + '/exploregeometry/' + this.area_type + '.json') } @@ -471,6 +478,11 @@ const app = createApp({ }); this.area_count = Object.keys(features).length + if (["DIS", "STC"].includes(this.area_type)) { + this.area_header_label = "councils" + } else { + this.area_header_label = "constituencies" + } window.geojson.eachLayer(function (layer) { if ( features[layer.feature.properties.PCON13CD] ) { @@ -518,6 +530,14 @@ const app = createApp({ this.loading = true this.filters_applied = (this.filters.length > 0) + if (this.sortBy == 'Constituency Name' || this.sortBy == 'Council Name') { + if (["DIS", "STC"].includes(this.area_type)) { + this.sortBy = "Council Name" + } else { + this.sortBy = "Constituency Name" + } + } + fetch(this.url('/explore.csv')) .then(response => response.blob()) .then(data => { diff --git a/hub/templates/hub/area.html b/hub/templates/hub/area.html index 294c8afab..c5a3e6832 100644 --- a/hub/templates/hub/area.html +++ b/hub/templates/hub/area.html @@ -120,9 +120,11 @@