diff --git a/hub/management/commands/import_new_constituencies.py b/hub/management/commands/import_new_constituencies.py index 295ba1594..a9e6ec1c0 100644 --- a/hub/management/commands/import_new_constituencies.py +++ b/hub/management/commands/import_new_constituencies.py @@ -4,9 +4,11 @@ from django.core.management.base import BaseCommand from django.db.utils import IntegrityError +from mysoc_dataset import get_dataset_df from tqdm import tqdm -from hub.models import Area, AreaType +from hub.models import Area, AreaOverlap, AreaType +from utils.constituency_mapping import get_overlap_df class Command(BaseCommand): @@ -57,3 +59,30 @@ def handle(self, quiet: bool = False, *args, **options): con["properties"]["type"] = "WMC23" a.geometry = json.dumps(con) a.save() + + constituency_lookup = ( + get_dataset_df( + repo_name="2025-constituencies", + package_name="parliament_con_2025", + version_name="latest", + file_name="parl_constituencies_2025.csv", + ) + .set_index("short_code")["gss_code"] + .to_dict() + ) + + df = get_overlap_df("PARL10", "PARL25") + for area in Area.objects.filter(area_type__code="WMC"): + overlap_constituencies = df.query("PARL10 == @area.gss") + for _, row in overlap_constituencies.iterrows(): + new_area = Area.objects.get( + area_type__code="WMC23", gss=constituency_lookup[row["PARL25"]] + ) + AreaOverlap.objects.get_or_create( + area_old=area, + area_new=new_area, + defaults={ + "population_overlap": int(row["percentage_overlap_pop"] * 100), + "area_overlap": int(row["percentage_overlap_area"] * 100), + }, + ) diff --git a/hub/migrations/0058_areaoverlap_area_overlaps.py b/hub/migrations/0058_areaoverlap_area_overlaps.py new file mode 100644 index 000000000..10bef0175 --- /dev/null +++ b/hub/migrations/0058_areaoverlap_area_overlaps.py @@ -0,0 +1,51 @@ +# Generated by Django 4.2.5 on 2023-11-14 11:15 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ("hub", "0057_populate_dataset_areas_available"), + ] + + operations = [ + migrations.CreateModel( + name="AreaOverlap", + fields=[ + ( + "id", + models.BigAutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("population_overlap", models.SmallIntegerField(default=0)), + ("area_overlap", models.SmallIntegerField(default=0)), + ( + "area_new", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="new_overlaps", + to="hub.area", + ), + ), + ( + "area_old", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="old_overlaps", + to="hub.area", + ), + ), + ], + ), + migrations.AddField( + model_name="area", + name="overlaps", + field=models.ManyToManyField(through="hub.AreaOverlap", to="hub.area"), + ), + ] diff --git a/hub/models.py b/hub/models.py index f46d813cc..bdfb4e685 100644 --- a/hub/models.py +++ b/hub/models.py @@ -502,6 +502,7 @@ class Area(models.Model): name = models.CharField(max_length=200) area_type = models.ForeignKey(AreaType, on_delete=models.CASCADE) geometry = models.TextField(blank=True, null=True) + overlaps = models.ManyToManyField("self", through="AreaOverlap") def __str__(self): return self.name @@ -546,6 +547,17 @@ class Meta: unique_together = ["gss", "area_type"] +class AreaOverlap(models.Model): + area_old = models.ForeignKey( + Area, on_delete=models.CASCADE, related_name="old_overlaps" + ) + area_new = models.ForeignKey( + Area, on_delete=models.CASCADE, related_name="new_overlaps" + ) + population_overlap = models.SmallIntegerField(default=0) + area_overlap = models.SmallIntegerField(default=0) + + class AreaData(CommonData): area = models.ForeignKey(Area, on_delete=models.CASCADE) diff --git a/hub/templates/hub/area.html b/hub/templates/hub/area.html index 5242c0146..6da0203c6 100644 --- a/hub/templates/hub/area.html +++ b/hub/templates/hub/area.html @@ -41,24 +41,43 @@
At the next election, this constituency will be divided into {{ overlap_constituencies|length|apnumber }} new constituenc{% if overlap_constituencies|length_is:1 %}y{% else %}ies{% endif %}:
+ {% else %} +At the next election, this constituency will be replaced with:
+ {% endif %} +Will cover approximately {{ overlap_constituency.pop_overlap }}% of this constituency’s population, and {{ overlap_constituency.area_overlap }}% of this constituency’s area.
+At the next election, people from this constituency will be divided into {{ overlap_constituencies|length|apnumber }} constituenc{% if overlap_constituencies|length_is:1 %}y{% else %}ies{% endif %}:
-{{ overlap_constituency.area }} | -Covers approximately {{ overlap_constituency.pop_overlap }}% of this constituency’s population, and {{ overlap_constituency.area_overlap }}% of this constituency’s area. | -
---|
This constituency will only exist at the next election, and replaces the previous {{ overlap_constituencies|length|apnumber }} constituenc{% if overlap_constituencies|length_is:1 %}y{% else %}ies{% endif %}:
+ {% else %} +This constituency will only exist at the next election, and replaces:
+ {% endif %} +Covered approximately {{ overlap_constituency.pop_overlap }}% of this constituency’s population, and {{ overlap_constituency.area_overlap }}% of this constituency’s area.
+