-
Notifications
You must be signed in to change notification settings - Fork 19
Unitary Councils
There are going to be more of these, so I thought I'd keep track of what I did for the Surrey ones.
Go to https://github.com/mysociety/uk_local_authority_names_and_codes/ and suggest some new reg codes. They should be three capital letters, and be unique. example issue
There is a command for this: every_election/apps/organisations/management/commands/create_unitary_authority.py
python manage.py create_unitary_authority \
--official-name 'West Surrey Council' \
--common-name 'West Surrey' \
--identifier WSY \
--constituent-councils E07000209 E07000212 E07000213 E07000214 E07000216 E07000217 \
--slug west-surrey \
--start-date 2026-07-01For the next command you will need to prepare a csv, and upload to s3.
Example here: s3://ee.boundary-reviews.production/west-surrey/The Surrey (Structural Changes) Order 2026/divisions.csv
To make this I copied the schedule from the legislation, and then csvified it in my editor.
You also need the id (as in db pk).
You can get this with:
uv run \
manage.py \
shell \
-c 'from organisations.models.organisations import Organisation; print(Organisation.objects.get(official_identifier="WSY").pk)'Then you can run something like::
uv run python manage.py create_divisions_for_new_org \
-s 'west-surrey/The Surrey (Structural Changes) Order 2026/divisions.csv' \
--organisation 532 \
--division-type UTW \
--short-title "The Surrey (Structural Changes) Order 2026"At this point you should see the organisation and divisionset with divisions on the frontend.
The final step is to build some pmtiles for it:
python manage.py update_pmtiles --divset-ids 842Set end dates on any organisations that are being replaced by the new Unitary Council.
The code here is probably superseded by the code in the two commands used above.
It seems sensible to have a record of how these were made. I did this with a jupyter notebook, so I've copied the relevant bits here. The formatting is horrible, so might be easier to click edit and copy the source.
Somerset
Includes making divisions from the old county divisions, but also a couple of the new divisions had to be made from district wards.
somerset_county = Organisation.objects.get(official_identifier="SOM")
somerset_county_divset = OrganisationDivisionSet.objects.get(id=368)
somerset_county_divset.end_date = datetime.datetime.strptime(
"2022-05-04", "%Y-%m-%d"
).date()
somerset_county_divset.save()
if somerset_county.divisionset.latest().end_date is None:
somerset_county.divisionset.latest().delete()
new_divset = OrganisationDivisionSet.objects.create(
**{
"start_date": "2022-05-05",
"organisation_id": somerset_county.id,
"legislation_url": "TBC",
"short_title": "The Somerset (Structural Changes) Order 2022",
}
)
new_divset.save()
somerset_divs = [
div
for div in somerset_county_divset.divisions.all()
if div.slug != "glastonbury-and-street"
]
for div in somerset_divs:
div.pk = None
div.official_identifier = ":".join(
[
new_divset.organisation.official_identifier,
div.slug,
]
)
div.seats_total = 2
div.divisionset = new_divset
div.save()
geographies = [
(div.slug, div.geography)
for div in somerset_county_divset.divisions.all()
if div.slug != "glastonbury-and-street"
]
for slug, geog in geographies:
div = OrganisationDivision.objects.get(slug=slug, divisionset=new_divset)
geog.pk = None
geog.division_id = div.id
geog.save()
# attach it to the target division
div.geography = geog
div.save()
new_divisions = {
"glastonbury": {
"division": {
"slug": "glastonbury",
"official_identifier": "SOM:glastonbury",
"division_type": "CED",
"division_subtype": "County council electoral division",
"name": "Glastonbury",
"division_election_sub_type": "",
"divisionset_id": new_divset.id,
"seats_total": 2,
},
"geographies": [
d.geography.geography
for d in OrganisationDivision.objects.filter(
official_identifier__in=[
"gss:E05006775",
"gss:E05006777",
"gss:E05006778",
"gss:E05006776",
]
)
],
},
"street": {
"division": {
"slug": "street",
"official_identifier": "SOM:street",
"division_type": "CED",
"division_subtype": "County council electoral division",
"name": "Street",
"division_election_sub_type": "",
"divisionset_id": new_divset.id,
"seats_total": 2,
},
"geographies": [
d.geography.geography
for d in OrganisationDivision.objects.filter(
official_identifier__in=[
"gss:E05006786",
"gss:E05006787",
"gss:E05006788",
]
)
],
},
}
for value in new_divisions.values():
new_div = OrganisationDivision.objects.create(**value["division"])
new_div.save()
geog = DivisionGeography.objects.create(
division=new_div,
geography=union_list(value["geographies"]),
source="unkown",
)
geog.save()
div.geography = geog
new_div.save()
Cumberland
Includes making divisions from the divisions.
cumbria_county = Organisation.objects.get(official_identifier="CMA")
cumbria_county_divset = OrganisationDivisionSet.objects.get(id=123)
cumberland = Organisation.objects.create(
**{
"official_identifier": "CBD",
"organisation_type": "local-authority",
"organisation_subtype": "UA",
"official_name": "Cumberland Council",
"common_name": "Cumberland",
"slug": "cumberland",
"territory_code": "ENG",
"election_name": "Cumberland local election",
"start_date": "2022-05-05",
"legislation_url": "https://www.legislation.gov.uk/uksi/2022/331/made",
}
)
cumberland.save()
cumberland_divset = OrganisationDivisionSet.objects.create(
**{
"start_date": "2022-05-05",
"organisation_id": cumberland.id,
"legislation_url": "https://www.legislation.gov.uk/uksi/2022/331/made",
"short_title": "The Cumbria (Structural Changes) Order 2022",
}
)
cumberland_divset.save()
cumberland_div_names = [
"Aspatria",
"Belah",
"Belle Vue",
"Botcherby",
"Bothel and Wharrels",
"Brampton",
"Bransty",
"Castle",
"Cleator Moor East and Frizington",
"Cleator Moor West",
"Cockermouth North",
"Cockermouth South",
"Corby and Hayton",
"Currock",
"Dalston and Burgh",
"Dearham and Broughton",
"Denton Holme",
"Egremont",
"Egremont North and St Bees",
"Gosforth",
"Harraby North",
"Harraby South",
"Harrington",
"Hillcrest and Hensingham",
"Houghton and Irthington",
"Howgate",
"Kells and Sandwith",
"Keswick",
"Longtown",
"Maryport North",
"Maryport South",
"Millom",
"Millom Without",
"Mirehouse",
"Morton",
"Moss Bay and Moorclose",
"Seaton",
"Solway Coast",
"St Michael's",
"St John's and Great Clifton",
"Stanwix Urban",
"Thursby",
"Upperby",
"Wetheral",
"Wigton",
"Yewdale",
]
cumberland_divs = [
div
for div in cumbria_county_divset.divisions.all()
if div.name in cumberland_div_names
]
print(
[
n
for n in cumberland_div_names
if n not in [d.name for d in cumberland_divs]
]
)
for div in cumberland_divs:
old_div_id = div.pk
div.pk = None
div.official_identifier = ":".join(
[
cumberland_divset.organisation.official_identifier,
div.slug,
]
)
div.seats_total = 1
div.divisionset = cumberland_divset
div.division_type = "UTW"
div.division_subtype = ""
div.save()
geog = DivisionGeography.objects.get(division_id=old_div_id)
geog.pk = None
geog.division_id = div.pk
geog.save()
div.geography = geog
div.save()
cumberland_geographies = [
d.geography.geography for d in cumberland_divset.divisions.all()
]
cumberland_geography = OrganisationGeography.objects.create(
**{
"organisation_id": cumberland.id,
"start_date": cumberland.start_date,
"legislation_url": cumberland.legislation_url,
"geography": union_list(cumberland_geographies),
}
)
cumberland_geography.save()
cumberland.geography = cumberland_geography
cumberland.save()
#%%