diff --git a/documentation/docs/developer/boundaries.md b/documentation/docs/developer/boundaries.md new file mode 100644 index 0000000..478264f --- /dev/null +++ b/documentation/docs/developer/boundaries.md @@ -0,0 +1,42 @@ +--- +title: Ordnance Survey and Boundaries +author: Dr Simon Chapman +--- + +## Boundaries + +Healthcare often reports outcomes according to health geographies - for example at the level of Integrated Care Boards (ICBs) in England, but many like to have their data in administrative contexts also. Deprivation index, for example, which has a large impact on health, is not measured across health geographies, but across administrative ones. + +### Lower Layer Super Output Areas (LSOA) + +The index of multiple deprivation [for which RCPCH has another repository](https://github.com/rcpch/rcpch-census-platform) relies on census data to summarize different societal and population features to rank geographical units in order of most deprived to least deprived - the higher the score, the better off the region is. IMD uses Lower Layer Super Output Areas (LSOA) whose boundaries were last defined in 2011. These boundaries don't change much, but were updated last in 2021. They are not meant to change to allow longitudinal data to be gathered and be meaningful. LSOAs are chosen to be the way they are as they have population numbers as their common feature (1500 individuals or 650 households) - geographically distributed LSOAs cover a larger area, but in cities they are small as population numbers per unit area are larger. + +The last index of multiple of deprivation data for England was published in 2019 and used the 2011 LSOAs, since those were the latest boundaries. Since then, there has been the census of 2021, with the LSOA boundaries updated the same year. The decision on which boundaries will be included in the next iteration of IMD will be decided bythe Office for National Statistics (ONS) and the Ministry of Housing, Communities & Local Government (MHCLG) (now the Department for Levelling Up, Housing and Communities). + +### Local Authorities + +These are administrative geography boundaries. LSOAs conveniently fit neatly inside LAs but the LA boundaries change more frequently. + +**Local Authority Districts (LADs)** are a unit of local administration and statistical geography, and their structure varies across the country. Depending on the region, LADs can take different forms: + +#### Two-Tier Systems + +In some areas, LADs correspond to district councils, which operate alongside a county council that provides upper-tier services (e.g., Hertfordshire has multiple district councils, such as Watford Borough Council, each of which is an LAD). + +#### Unitary Authorities + +In other areas, LADs correspond to unitary authorities, which combine the responsibilities of both district and county councils into a single authority. + +#### Metropolitan Boroughs + +In metropolitan areas, LADs correspond to metropolitan boroughs (e.g., Manchester Metropolitan Borough), which provide local services. These boroughs are part of a wider metropolitan county (e.g., Greater Manchester) that handles strategic functions like transport and policing. + +##### London + +London is unique, with 32 London Boroughs serving as LADs, plus the City of London, which is its own LAD. The Greater London Authority (GLA) oversees strategic citywide functions but is not an LAD + +LAD boundaries change quite frequently. Each LAD has its own unique identifier and the boundaries and membership of LADs, which LSOAs they comprise, are updated by the ONS and published in the form of [look up tables](https://geoportal.statistics.gov.uk/documents/ons::local-authority-districts-counties-and-unitary-authorities-april-2023-map-in-the-uk/about?path=) + +## Implications for RCPCH NHS Organisations + +Although local authorities are not health geographies, local government has an interest in the health of the local population, and a responsibility for all functions of government that influence health (from housing, to green space, to transport etc), so RCPCH stores the relationship between the organisations that care for children and their local authority district. diff --git a/documentation/docs/developer/shapes.md b/documentation/docs/developer/shapes.md index 9e67aa7..fa8a410 100644 --- a/documentation/docs/developer/shapes.md +++ b/documentation/docs/developer/shapes.md @@ -30,4 +30,118 @@ RCPCH in the main uses the most generalised views - this is because more detail ### Boundary to IMD -2011 LSOAs mapped to 2019 IMD data is a service fortunately already provided by [Consumer Data Research Centre](https://data.cdrc.ac.uk/dataset/index-multiple-deprivation-imd) \ No newline at end of file +2011 LSOAs mapped to 2019 IMD data is a service fortunately already provided by [Consumer Data Research Centre](https://data.cdrc.ac.uk/dataset/index-multiple-deprivation-imd) + +## Importing a .shp file + +1. Download the file from whichever source (using the LocalAuthorityDistrict model as an example) +2. On the command line: + `python manage.py ogrinspect rcpch_nhs_organisations/hospitals/shape_files/Local_Authority_Districts_May_2024_Boundaries_UK_BUC/LAD_MAY_2024_UK_BUC.shp LocalAuthorityDistrict --srid 27700 --mapping --multi` + This will generate the code for the model (don't forget to import in `__init__.py` and add to admin): + + ```python + class LocalAuthorityDistrict(models.Model): + lad24cd = models.CharField(max_length=9) + lad24nm = models.CharField(max_length=36) + lad24nmw = models.CharField(max_length=24) + bng_e = models.BigIntegerField() + bng_n = models.BigIntegerField() + long = models.FloatField() + lat = models.FloatField() + globalid = models.CharField(max_length=38) + geom = models.MultiPolygonField(srid=27700) + + + # Auto-generated `LayerMapping` dictionary for LocalAuthorityDistrict model + localauthoritydistrict_mapping = { + 'lad24cd': 'LAD24CD', + 'lad24nm': 'LAD24NM', + 'lad24nmw': 'LAD24NMW', + 'bng_e': 'BNG_E', + 'bng_n': 'BNG_N', + 'long': 'LONG', + 'lat': 'LAT', + 'globalid': 'GlobalID', + 'geom': 'MULTIPOLYGON', + } + ``` + +3. Create a migration for the new model (check previous examples, as RCPCH tend to add this as an abstract model, so this example diverges from actual practice for simplicity) + `python manage.py makemigrations` + +4. Create an empty migration + `python manage.py makemigrations hospitals --name seed_local_authority_districts_boundaries --empty` + +5. Create a custom function in the empty migration to import the .shp file geometry data using the layer map created earlier. + + ```python + from django.db import migrations + from django.apps import apps as django_apps + + import os + from django.contrib.gis.utils import LayerMapping + + """ + Local Authority Districts May 2024 Boundaries UK BUC + https://geoportal.statistics.gov.uk/search?q=BDY_LAD%202024&sort=Title%7Ctitle%7Casc + """ + + # Auto-generated `LayerMapping` dictionary for LocalAuthorityDistrict model + localauthoritydistrict_mapping = { + "lad24cd": "LAD24CD", + "lad24nm": "LAD24NM", + "lad24nmw": "LAD24NMW", + "bng_e": "BNG_E", + "bng_n": "BNG_N", + "long": "LONG", + "lat": "LAT", + "globalid": "GlobalID", + "geom": "MULTIPOLYGON", + } + + + # Boundary files + + app_config = django_apps.get_app_config("hospitals") + app_path = app_config.path + + Local_Authority_Districts_May_2024_Boundaries_UK_BUC = os.path.join( + app_path, + "shape_files", + "Local_Authority_Districts_May_2024_Boundaries_UK_BUC", + "LAD_MAY_2024_UK_BUC.shp", + ) + + + def load(apps, schema_editor, verbose=True): + LocalAuthorityDistrict = apps.get_model("hospitals", "LocalAuthorityDistrict") + lm = LayerMapping( + LocalAuthorityDistrict, + Local_Authority_Districts_May_2024_Boundaries_UK_BUC, + localauthoritydistrict_mapping, + transform=False, + encoding="utf-8", + ) + lm.save(strict=True, verbose=verbose) + + + class Migration(migrations.Migration): + dependencies = [ + ("hospitals", "0012_localauthoritydistrict"), + ] + + operations = [migrations.RunPython(load)] + ``` + +6. Migrate the changes + `python manage.py migrate` +7. If successful you should see: + + ```console + .... + Saved: LocalAuthorityDistrict object (360) + Saved: LocalAuthorityDistrict object (361) + OK + ``` + +8. This populates the model with the geometry files for mapping all the local authority boundaries, the names and codes. The next step is to hook this up to the other models. \ No newline at end of file diff --git a/documentation/mkdocs.yml b/documentation/mkdocs.yml index 1c9cd84..54e065c 100644 --- a/documentation/mkdocs.yml +++ b/documentation/mkdocs.yml @@ -139,6 +139,8 @@ nav: - 'developer/models.md' - 'developer/seeding.md' - 'developer/database_maintenance.md' + - 'developer/shapes.md' + - 'developer/boundaries.md' - Contact: - 'contact/contact-rcpch.md' - Legal: diff --git a/envs/.example.env b/envs/example.env similarity index 100% rename from envs/.example.env rename to envs/example.env diff --git a/rcpch_nhs_organisations/hospitals/constants/organisation_ods_code_to_lsoa_lad_code_mapping.py b/rcpch_nhs_organisations/hospitals/constants/organisation_ods_code_to_lsoa_lad_code_mapping.py new file mode 100644 index 0000000..d480b18 --- /dev/null +++ b/rcpch_nhs_organisations/hospitals/constants/organisation_ods_code_to_lsoa_lad_code_mapping.py @@ -0,0 +1,352 @@ +""" +This file contains the mapping of ODS code to LSOA and LAD codes for the hospitals in the UK. +It was generated by running the postcode against the postcodes.io API and extracting the LSOA and LAD codes. +""" + +ORGANISATION_TO_LSOA_LAD_MAPPING = [ + {"lsoa_code": "E01017995", "lad_code": "E07000008", "ods_code": "RGT01"}, + {"lsoa_code": "E01010645", "lad_code": "E08000032", "ods_code": "RCF22"}, + {"lsoa_code": "E01006570", "lad_code": "E08000012", "ods_code": "RBS25"}, + {"lsoa_code": "E01032253", "lad_code": "E07000236", "ods_code": "RWP01"}, + {"lsoa_code": "E01027358", "lad_code": "E06000057", "ods_code": "RTFDJ"}, + {"lsoa_code": "E01017745", "lad_code": "E06000060", "ods_code": "RXQ51"}, + {"lsoa_code": "E01007251", "lad_code": "E08000015", "ods_code": "RBL14"}, + {"lsoa_code": "E01030707", "lad_code": "E07000213", "ods_code": "RTK02"}, + {"lsoa_code": "E01000294", "lad_code": "E09000003", "ods_code": "RAL26"}, + {"lsoa_code": "E01007446", "lad_code": "E08000016", "ods_code": "RFFAA"}, + {"lsoa_code": "E01033356", "lad_code": "E06000023", "ods_code": "RVN38"}, + {"lsoa_code": "E01021297", "lad_code": "E07000066", "ods_code": "RAJ12"}, + {"lsoa_code": "E01032840", "lad_code": "E07000084", "ods_code": "RN506"}, + {"lsoa_code": "E01028042", "lad_code": "E07000171", "ods_code": "RP5BA"}, + {"lsoa_code": "E01017049", "lad_code": "E06000044", "ods_code": "R1CE4"}, + {"lsoa_code": "E01017473", "lad_code": "E06000055", "ods_code": "RC979"}, + {"lsoa_code": "E01033617", "lad_code": "E08000025", "ods_code": "RQ301"}, + {"lsoa_code": "E01020872", "lad_code": "E06000047", "ods_code": "RXPBA"}, + {"lsoa_code": "E01012722", "lad_code": "E06000009", "ods_code": "RXL01"}, + {"lsoa_code": "E01010289", "lad_code": "E08000030", "ods_code": "RYK14"}, + {"lsoa_code": "E01027415", "lad_code": "E06000057", "ods_code": "RTFDX"}, + {"lsoa_code": "E01010810", "lad_code": "E08000032", "ods_code": "RAE01"}, + {"lsoa_code": "E01004795", "lad_code": "E08000001", "ods_code": "RMC79"}, + {"lsoa_code": "E01014542", "lad_code": "E06000023", "ods_code": "RA723"}, + {"lsoa_code": "W01000512", "lad_code": "W06000008", "ods_code": "7A2AJ"}, + {"lsoa_code": "E01021542", "lad_code": "E07000070", "ods_code": "R1LCT"}, + {"lsoa_code": "E01017645", "lad_code": "E06000060", "ods_code": "RXQ61"}, + {"lsoa_code": "E01024240", "lad_code": "E07000108", "ods_code": "RVV02"}, + {"lsoa_code": "E01024897", "lad_code": "E07000117", "ods_code": "RXR10"}, + {"lsoa_code": "E01017251", "lad_code": "E06000045", "ods_code": "RHM08"}, + {"lsoa_code": "E01029435", "lad_code": "E07000193", "ods_code": "RTG02"}, + {"lsoa_code": "E01010972", "lad_code": "E08000033", "ods_code": "RWY02"}, + {"lsoa_code": "E01032659", "lad_code": "E06000058", "ods_code": "C1G7Z"}, + {"lsoa_code": "E01000600", "lad_code": "E09000005", "ods_code": "R1K02"}, + {"lsoa_code": "E01017515", "lad_code": "E06000055", "ods_code": "RYVD9"}, + {"lsoa_code": "E01001468", "lad_code": "E09000010", "ods_code": "RALC7"}, + {"lsoa_code": "E01002912", "lad_code": "E09000020", "ods_code": "RQM01"}, + {"lsoa_code": "E01022116", "lad_code": "E07000078", "ods_code": "RTE01"}, + {"lsoa_code": "W01001586", "lad_code": "W06000021", "ods_code": "7A6BJ"}, + {"lsoa_code": "E01019585", "lad_code": "E07000034", "ods_code": "RFSDA"}, + {"lsoa_code": "E01020598", "lad_code": "E06000047", "ods_code": "RXP48"}, + {"lsoa_code": "E01002912", "lad_code": "E09000020", "ods_code": "K8C1E"}, + {"lsoa_code": "E01002912", "lad_code": "E09000020", "ods_code": "RYW91"}, + {"lsoa_code": "E01030092", "lad_code": "E07000245", "ods_code": "RGR69"}, + {"lsoa_code": "E01021466", "lad_code": "E07000068", "ods_code": "RATE2"}, + {"lsoa_code": "E01031482", "lad_code": "E07000225", "ods_code": "RYR41"}, + {"lsoa_code": "E01021827", "lad_code": "E07000073", "ods_code": "R1LVD"}, + {"lsoa_code": "W01000092", "lad_code": "W06000002", "ods_code": "7A1LW"}, + {"lsoa_code": "W01000133", "lad_code": "W06000003", "ods_code": "7A1NU"}, + {"lsoa_code": "E01015072", "lad_code": "E06000026", "ods_code": "RK98A"}, + {"lsoa_code": "E01017995", "lad_code": "E07000008", "ods_code": "RYVF7"}, + {"lsoa_code": "E01031323", "lad_code": "E07000222", "ods_code": "RJC07"}, + {"lsoa_code": "E01003636", "lad_code": "E09000025", "ods_code": "RP416"}, + {"lsoa_code": "E01004471", "lad_code": "E09000031", "ods_code": "RP427"}, + {"lsoa_code": "E01002412", "lad_code": "E09000017", "ods_code": "RV3J5"}, + {"lsoa_code": "W01000885", "lad_code": "W06000012", "ods_code": "7A3LW"}, + {"lsoa_code": "E01016845", "lad_code": "E06000042", "ods_code": "RV3J1"}, + {"lsoa_code": "E01018855", "lad_code": "E06000052", "ods_code": "REFCH"}, + {"lsoa_code": "W01000493", "lad_code": "W06000023", "ods_code": "7A7BM"}, + {"lsoa_code": "E01013967", "lad_code": "E06000018", "ods_code": "RX1LK"}, + {"lsoa_code": "E01024937", "lad_code": "E07000118", "ods_code": "RXN01"}, + {"lsoa_code": "E01025763", "lad_code": "E07000130", "ods_code": "RT5CF"}, + {"lsoa_code": "E01009345", "lad_code": "E08000025", "ods_code": "RXK02"}, + {"lsoa_code": "E01021682", "lad_code": "E07000071", "ods_code": "RDEE4"}, + {"lsoa_code": "E01013475", "lad_code": "E06000015", "ods_code": "RTG11"}, + {"lsoa_code": "E01002042", "lad_code": "E09000014", "ods_code": "RKEAA"}, + {"lsoa_code": "E01018147", "lad_code": "E07000011", "ods_code": "RYV42"}, + {"lsoa_code": "E01026815", "lad_code": "E07000148", "ods_code": "RY327"}, + {"lsoa_code": "E01018480", "lad_code": "E06000049", "ods_code": "RBTCP"}, + {"lsoa_code": "E01010363", "lad_code": "E08000030", "ods_code": "RBKCP"}, + {"lsoa_code": "E01013542", "lad_code": "E06000015", "ods_code": "RTG70"}, + {"lsoa_code": "E01019155", "lad_code": "E06000064", "ods_code": "RNNHT"}, + {"lsoa_code": "E01007340", "lad_code": "E08000016", "ods_code": "RXGDG"}, + {"lsoa_code": "E01007251", "lad_code": "E08000015", "ods_code": "RBL69"}, + {"lsoa_code": "E01031229", "lad_code": "E07000221", "ods_code": "RJC39"}, + {"lsoa_code": "E01020948", "lad_code": "E07000061", "ods_code": "E1H0U"}, + {"lsoa_code": "E01021318", "lad_code": "E07000066", "ods_code": "RATE3"}, + {"lsoa_code": "E01015995", "lad_code": "E06000034", "ods_code": "RATE1"}, + {"lsoa_code": "E01017100", "lad_code": "E06000044", "ods_code": "R1C22"}, + {"lsoa_code": "E01012632", "lad_code": "E06000008", "ods_code": "RXR2M"}, + {"lsoa_code": "E01017251", "lad_code": "E06000045", "ods_code": "R1C55"}, + {"lsoa_code": "E01022977", "lad_code": "E07000091", "ods_code": "R1CA6"}, + {"lsoa_code": "E01020986", "lad_code": "E07000062", "ods_code": "RXC01"}, + {"lsoa_code": "E01018374", "lad_code": "E06000050", "ods_code": "RJR05"}, + {"lsoa_code": "E01029725", "lad_code": "E07000197", "ods_code": "RJE09"}, + {"lsoa_code": "E01032359", "lad_code": "E07000238", "ods_code": "R1APF"}, + {"lsoa_code": "E01000990", "lad_code": "E09000008", "ods_code": "RJ6CC"}, + {"lsoa_code": "E01001178", "lad_code": "E09000008", "ods_code": "RJ611"}, + {"lsoa_code": "E01019205", "lad_code": "E06000063", "ods_code": "RNN62"}, + {"lsoa_code": "E01015092", "lad_code": "E06000026", "ods_code": "RK950"}, + {"lsoa_code": "E01011026", "lad_code": "E08000034", "ods_code": "RXF10"}, + {"lsoa_code": "E01013185", "lad_code": "E06000012", "ods_code": "RJL30"}, + {"lsoa_code": "E01032761", "lad_code": "E06000041", "ods_code": "RHW0C"}, + {"lsoa_code": "E01007567", "lad_code": "E08000017", "ods_code": "RP5DR"}, + {"lsoa_code": "E01032643", "lad_code": "E06000059", "ods_code": "RBD01"}, + {"lsoa_code": "E01014830", "lad_code": "E06000024", "ods_code": "RVN4T"}, + {"lsoa_code": "E01001337", "lad_code": "E09000009", "ods_code": "R1K04"}, + {"lsoa_code": "E01020948", "lad_code": "E07000061", "ods_code": "RXC02"}, + {"lsoa_code": "E01014665", "lad_code": "E06000023", "ods_code": "RVJT9"}, + {"lsoa_code": "E01030572", "lad_code": "E07000211", "ods_code": "RTP04"}, + {"lsoa_code": "E01005599", "lad_code": "E08000006", "ods_code": "RM332"}, + {"lsoa_code": "W01000703", "lad_code": "W06000010", "ods_code": "7A2P7"}, + {"lsoa_code": "E01030419", "lad_code": "E07000208", "ods_code": "RVR50"}, + {"lsoa_code": "W01000312", "lad_code": "W06000005", "ods_code": "7A1MF"}, + {"lsoa_code": "E01008321", "lad_code": "E08000021", "ods_code": "RTD01"}, + {"lsoa_code": "E01027612", "lad_code": "E06000065", "ods_code": "RTR45"}, + {"lsoa_code": "E01030768", "lad_code": "E07000214", "ods_code": "RDU01"}, + {"lsoa_code": "E01019155", "lad_code": "E06000064", "ods_code": "RTXBU"}, + {"lsoa_code": "E01008848", "lad_code": "E08000024", "ods_code": "R0B0G"}, + {"lsoa_code": "E01031049", "lad_code": "E07000219", "ods_code": "RLT09"}, + {"lsoa_code": "W01000640", "lad_code": "W06000010", "ods_code": "7A2AA"}, + {"lsoa_code": "E01022312", "lad_code": "E07000081", "ods_code": "RTE03"}, + {"lsoa_code": "E01030953", "lad_code": "E07000217", "ods_code": "RTK09"}, + {"lsoa_code": "E01009432", "lad_code": "E08000025", "ods_code": "RRK98"}, + {"lsoa_code": "E01026352", "lad_code": "E07000141", "ods_code": "RWDLP"}, + {"lsoa_code": "E01000915", "lad_code": "E09000007", "ods_code": "RP401"}, + {"lsoa_code": "E01003110", "lad_code": "E09000022", "ods_code": "RJ1E7"}, + {"lsoa_code": "E01003817", "lad_code": "E09000027", "ods_code": "RY905"}, + {"lsoa_code": "E01001876", "lad_code": "E09000013", "ods_code": "RYJ03"}, + {"lsoa_code": "E01027649", "lad_code": "E06000065", "ods_code": "RCD01"}, + {"lsoa_code": "E01009341", "lad_code": "E08000025", "ods_code": "RRK97"}, + {"lsoa_code": "E01002664", "lad_code": "E09000018", "ods_code": "RY918"}, + {"lsoa_code": "E01016524", "lad_code": "E06000040", "ods_code": "RDU52"}, + {"lsoa_code": "E01014011", "lad_code": "E06000019", "ods_code": "RLQ01"}, + {"lsoa_code": "E01023479", "lad_code": "E07000242", "ods_code": "RWH23"}, + {"lsoa_code": "E01027494", "lad_code": "E06000057", "ods_code": "RTFDR"}, + {"lsoa_code": "E01002412", "lad_code": "E09000017", "ods_code": "RAS01"}, + {"lsoa_code": "E01018151", "lad_code": "E07000011", "ods_code": "RGN90"}, + {"lsoa_code": "E01001729", "lad_code": "E09000012", "ods_code": "RQXM1"}, + {"lsoa_code": "E01028427", "lad_code": "E07000177", "ods_code": "RTH05"}, + {"lsoa_code": "E01031145", "lad_code": "E07000220", "ods_code": "RKB03"}, + {"lsoa_code": "E01012852", "lad_code": "E06000010", "ods_code": "RWA01"}, + {"lsoa_code": "E01019638", "lad_code": "E07000036", "ods_code": "RTG07"}, + {"lsoa_code": "E01029965", "lad_code": "E07000202", "ods_code": "RDE03"}, + {"lsoa_code": "E01005621", "lad_code": "E08000006", "ods_code": "RM335"}, + {"lsoa_code": "E01026617", "lad_code": "E07000145", "ods_code": "RGP75"}, + {"lsoa_code": "E01028534", "lad_code": "E07000178", "ods_code": "RTH08"}, + {"lsoa_code": "E01024046", "lad_code": "E07000106", "ods_code": "RVVKC"}, + {"lsoa_code": "E01027117", "lad_code": "E06000061", "ods_code": "RNQ51"}, + {"lsoa_code": "E01032476", "lad_code": "E07000239", "ods_code": "RWP31"}, + {"lsoa_code": "E01003076", "lad_code": "E09000022", "ods_code": "RJZ01"}, + {"lsoa_code": "E01027985", "lad_code": "E07000170", "ods_code": "RK5BC"}, + {"lsoa_code": "E01002954", "lad_code": "E09000021", "ods_code": "RAX01"}, + {"lsoa_code": "E01014927", "lad_code": "E06000025", "ods_code": "RVNE6"}, + {"lsoa_code": "E01027985", "lad_code": "E07000170", "ods_code": "RK5CP"}, + {"lsoa_code": "E01033008", "lad_code": "E08000035", "ods_code": "RR801"}, + {"lsoa_code": "E01032875", "lad_code": "E06000016", "ods_code": "RWEAA"}, + {"lsoa_code": "E01018480", "lad_code": "E06000049", "ods_code": "RBT20"}, + {"lsoa_code": "E01031274", "lad_code": "E07000222", "ods_code": "RJC79"}, + {"lsoa_code": "E01026125", "lad_code": "E07000138", "ods_code": "RWDDA"}, + {"lsoa_code": "E01023800", "lad_code": "E07000243", "ods_code": "RWH01"}, + {"lsoa_code": "E01015713", "lad_code": "E06000032", "ods_code": "RGT2X"}, + {"lsoa_code": "E01015713", "lad_code": "E06000032", "ods_code": "RC971"}, + {"lsoa_code": "E01023002", "lad_code": "E07000091", "ods_code": "RW1Y0"}, + {"lsoa_code": "E01018616", "lad_code": "E06000049", "ods_code": "RJN71"}, + {"lsoa_code": "E01027786", "lad_code": "E06000065", "ods_code": "RCBL8"}, + {"lsoa_code": "E01033662", "lad_code": "E08000003", "ods_code": "COX3P"}, + {"lsoa_code": "E01010363", "lad_code": "E08000030", "ods_code": "RBK02"}, + {"lsoa_code": "E01016045", "lad_code": "E06000035", "ods_code": "RPA02"}, + {"lsoa_code": "E01010063", "lad_code": "E08000028", "ods_code": "I3W1A"}, + {"lsoa_code": "E01016845", "lad_code": "E06000042", "ods_code": "RD816"}, + {"lsoa_code": "E01016845", "lad_code": "E06000042", "ods_code": "RD825"}, + {"lsoa_code": "E01015530", "lad_code": "E06000030", "ods_code": "RN351"}, + {"lsoa_code": "W01000816", "lad_code": "W06000011", "ods_code": "7A3C7"}, + {"lsoa_code": "E01000866", "lad_code": "E09000007", "ods_code": "RV3F3"}, + {"lsoa_code": "E01015176", "lad_code": "E06000026", "ods_code": "RK901"}, + {"lsoa_code": "E01029302", "lad_code": "E06000066", "ods_code": "RH5A8"}, + {"lsoa_code": "W01000885", "lad_code": "W06000012", "ods_code": "7A3CJ"}, + {"lsoa_code": "W01001557", "lad_code": "W06000021", "ods_code": "7A623"}, + {"lsoa_code": "E01028333", "lad_code": "E07000175", "ods_code": "RK5HP"}, + {"lsoa_code": "E01010476", "lad_code": "E08000031", "ods_code": "RL403"}, + {"lsoa_code": "E01003603", "lad_code": "E09000025", "ods_code": "R1HNH"}, + {"lsoa_code": "E01019599", "lad_code": "E07000035", "ods_code": "RY8NA"}, + {"lsoa_code": "E01010394", "lad_code": "E08000030", "ods_code": "RBK58"}, + {"lsoa_code": "W01001770", "lad_code": "W06000015", "ods_code": "7A4H1"}, + {"lsoa_code": "E01026883", "lad_code": "E07000149", "ods_code": "RM102"}, + {"lsoa_code": "E01027224", "lad_code": "E06000062", "ods_code": "RNS01"}, + {"lsoa_code": "E01020134", "lad_code": "E07000043", "ods_code": "RH880"}, + {"lsoa_code": "E01005164", "lad_code": "E08000003", "ods_code": "R0A66"}, + {"lsoa_code": "E01032574", "lad_code": "E09000010", "ods_code": "RAPNM"}, + {"lsoa_code": "E01008556", "lad_code": "E08000022", "ods_code": "RTFFS"}, + {"lsoa_code": "E01027394", "lad_code": "E06000057", "ods_code": "RTF86"}, + {"lsoa_code": "E01000568", "lad_code": "E09000005", "ods_code": "R1K01"}, + {"lsoa_code": "E01026863", "lad_code": "E07000148", "ods_code": "RY312"}, + {"lsoa_code": "E01033410", "lad_code": "E06000018", "ods_code": "RX1RA"}, + {"lsoa_code": "E01031278", "lad_code": "E07000222", "ods_code": "RJC1V"}, + {"lsoa_code": "E01025488", "lad_code": "E07000127", "ods_code": "RVY02"}, + {"lsoa_code": "E01032515", "lad_code": "E06000023", "ods_code": "RVNE9"}, + {"lsoa_code": "E01002770", "lad_code": "E09000019", "ods_code": "RKE04"}, + {"lsoa_code": "E01014950", "lad_code": "E06000025", "ods_code": "RVJT4"}, + {"lsoa_code": "E01009574", "lad_code": "E08000026", "ods_code": "RYGHP"}, + {"lsoa_code": "E01005682", "lad_code": "E08000006", "ods_code": "RM330"}, + {"lsoa_code": "E01015691", "lad_code": "E06000031", "ods_code": "RGN80"}, + {"lsoa_code": "E01026021", "lad_code": "E07000136", "ods_code": "RWDLA"}, + {"lsoa_code": "E01011884", "lad_code": "E08000036", "ods_code": "RXF05"}, + {"lsoa_code": "E01015468", "lad_code": "E06000058", "ods_code": "R0D01"}, + {"lsoa_code": "W01001299", "lad_code": "W06000024", "ods_code": "7A5B3"}, + {"lsoa_code": "W01000652", "lad_code": "W06000010", "ods_code": "7A2AL"}, + {"lsoa_code": "E01021843", "lad_code": "E07000073", "ods_code": "RQWG0"}, + {"lsoa_code": "E01017187", "lad_code": "E06000045", "ods_code": "RHM12"}, + {"lsoa_code": "W01001038", "lad_code": "W06000013", "ods_code": "7A3B7"}, + {"lsoa_code": "E01000751", "lad_code": "E09000006", "ods_code": "RJZ30"}, + {"lsoa_code": "E01033410", "lad_code": "E06000018", "ods_code": "RX1CP"}, + {"lsoa_code": "E01033410", "lad_code": "E06000018", "ods_code": "RX1PD"}, + {"lsoa_code": "E01017051", "lad_code": "E06000044", "ods_code": "RHU03"}, + {"lsoa_code": "E01001876", "lad_code": "E09000013", "ods_code": "RYJ04"}, + {"lsoa_code": "E01008219", "lad_code": "E08000037", "ods_code": "RR7EN"}, + {"lsoa_code": "E01001701", "lad_code": "E09000011", "ods_code": "RJ231"}, + {"lsoa_code": "E01023946", "lad_code": "E07000241", "ods_code": "RWH20"}, + {"lsoa_code": "E01024694", "lad_code": "E07000114", "ods_code": "RVV09"}, + {"lsoa_code": "E01004128", "lad_code": "E09000029", "ods_code": "RVR07"}, + {"lsoa_code": "E01000428", "lad_code": "E09000004", "ods_code": "RN7QM"}, + {"lsoa_code": "E01002248", "lad_code": "E09000016", "ods_code": "RF4QH"}, + {"lsoa_code": "E01031731", "lad_code": "E07000228", "ods_code": "RWF11"}, + {"lsoa_code": "E01019462", "lad_code": "E07000032", "ods_code": "RTG14"}, + {"lsoa_code": "E01031119", "lad_code": "E07000219", "ods_code": "RJC49"}, + {"lsoa_code": "E01028910", "lad_code": "E06000051", "ods_code": "RL131"}, + {"lsoa_code": "E01014070", "lad_code": "E06000019", "ods_code": "RLQ22"}, + {"lsoa_code": "E01007678", "lad_code": "E08000018", "ods_code": "RFRPA"}, + {"lsoa_code": "E01006370", "lad_code": "E08000010", "ods_code": "RRF02"}, + {"lsoa_code": "E01016862", "lad_code": "E06000043", "ods_code": "G0W1C"}, + {"lsoa_code": "E01016414", "lad_code": "E06000038", "ods_code": "RHW01"}, + {"lsoa_code": "E01012632", "lad_code": "E06000008", "ods_code": "RXR20"}, + {"lsoa_code": "E01004880", "lad_code": "E08000001", "ods_code": "RMC01"}, + {"lsoa_code": "E01015319", "lad_code": "E06000058", "ods_code": "R0D02"}, + {"lsoa_code": "E01000914", "lad_code": "E09000007", "ods_code": "8HV48"}, + {"lsoa_code": "E01018855", "lad_code": "E06000052", "ods_code": "REF12"}, + {"lsoa_code": "E01013542", "lad_code": "E06000015", "ods_code": "RTGFG"}, + {"lsoa_code": "E01020013", "lad_code": "E07000041", "ods_code": "RK963"}, + {"lsoa_code": "E01020013", "lad_code": "E07000041", "ods_code": "RH801"}, + {"lsoa_code": "E01000898", "lad_code": "E09000007", "ods_code": "RAL01"}, + {"lsoa_code": "W01001686", "lad_code": "W06000022", "ods_code": "7A6AR"}, + {"lsoa_code": "E01023263", "lad_code": "E07000094", "ods_code": "RN541"}, + {"lsoa_code": "E01033071", "lad_code": "E07000121", "ods_code": "RTX02"}, + {"lsoa_code": "E01005062", "lad_code": "E08000003", "ods_code": "R0A03"}, + {"lsoa_code": "E01005354", "lad_code": "E08000004", "ods_code": "RM317"}, + {"lsoa_code": "E01025300", "lad_code": "E07000123", "ods_code": "RXN02"}, + {"lsoa_code": "E01028942", "lad_code": "E06000051", "ods_code": "RXWAS"}, + {"lsoa_code": "E01032750", "lad_code": "E06000045", "ods_code": "RHM02"}, + {"lsoa_code": "E01014294", "lad_code": "E06000021", "ods_code": "RJE01"}, + {"lsoa_code": "E01030470", "lad_code": "E07000209", "ods_code": "RA201"}, + {"lsoa_code": "E01014428", "lad_code": "E06000022", "ods_code": "RD130"}, + {"lsoa_code": "E01024515", "lad_code": "E07000112", "ods_code": "RVV03"}, + {"lsoa_code": "E01010322", "lad_code": "E08000030", "ods_code": "RBK10"}, + {"lsoa_code": "E01009884", "lad_code": "E08000027", "ods_code": "RNA01"}, + {"lsoa_code": "E01007868", "lad_code": "E08000019", "ods_code": "RCU02"}, + {"lsoa_code": "E01032907", "lad_code": "E08000006", "ods_code": "RM366"}, + {"lsoa_code": "E01005719", "lad_code": "E08000006", "ods_code": "RM301"}, + {"lsoa_code": "E01032000", "lad_code": "E06000054", "ods_code": "RNZ02"}, + {"lsoa_code": "E01029524", "lad_code": "E07000194", "ods_code": "RTG54"}, + {"lsoa_code": "E01010104", "lad_code": "E08000028", "ods_code": "RXK01"}, + {"lsoa_code": "E01027873", "lad_code": "E06000065", "ods_code": "RCBCA"}, + {"lsoa_code": "E01013333", "lad_code": "E06000013", "ods_code": "RJL32"}, + {"lsoa_code": "E01016895", "lad_code": "E06000043", "ods_code": "RDRMK"}, + {"lsoa_code": "E01007866", "lad_code": "E08000019", "ods_code": "RCUEF"}, + {"lsoa_code": "E01002022", "lad_code": "E09000014", "ods_code": "RKE54"}, + {"lsoa_code": "W01000861", "lad_code": "W06000011", "ods_code": "7A3C4"}, + {"lsoa_code": "E01026092", "lad_code": "E07000137", "ods_code": "RWDLB"}, + {"lsoa_code": "E01010226", "lad_code": "E08000029", "ods_code": "RRK99"}, + {"lsoa_code": "E01017251", "lad_code": "E06000045", "ods_code": "RHM01"}, + {"lsoa_code": "E01014607", "lad_code": "E06000023", "ods_code": "RA773"}, + {"lsoa_code": "E01015858", "lad_code": "E06000033", "ods_code": "RAJ01"}, + {"lsoa_code": "E01014994", "lad_code": "E06000025", "ods_code": "RVJ72"}, + {"lsoa_code": "E01008592", "lad_code": "E08000023", "ods_code": "R0B0Q"}, + {"lsoa_code": "E01023551", "lad_code": "E07000098", "ods_code": "RWG07"}, + {"lsoa_code": "E01019194", "lad_code": "E06000063", "ods_code": "RNN82"}, + {"lsoa_code": "E01030738", "lad_code": "E07000213", "ods_code": "RTK23"}, + {"lsoa_code": "E01013960", "lad_code": "E06000018", "ods_code": "RX160"}, + {"lsoa_code": "E01005801", "lad_code": "E08000007", "ods_code": "RWJ09"}, + {"lsoa_code": "E01004617", "lad_code": "E09000032", "ods_code": "RJ701"}, + {"lsoa_code": "E01006845", "lad_code": "E08000013", "ods_code": "RBN02"}, + {"lsoa_code": "E01029966", "lad_code": "E07000202", "ods_code": "RDE90"}, + {"lsoa_code": "E01004128", "lad_code": "E09000029", "ods_code": "RVR05"}, + {"lsoa_code": "E01033035", "lad_code": "E08000035", "ods_code": "RR813"}, + {"lsoa_code": "E01010249", "lad_code": "E08000030", "ods_code": "RBK70"}, + {"lsoa_code": "E01010733", "lad_code": "E08000032", "ods_code": "RAE05"}, + {"lsoa_code": "E01017334", "lad_code": "E06000046", "ods_code": "R1F01"}, + {"lsoa_code": "E01027100", "lad_code": "E06000061", "ods_code": "RP1A1"}, + {"lsoa_code": "E01033594", "lad_code": "E09000033", "ods_code": "RYJ01"}, + {"lsoa_code": "E01014542", "lad_code": "E06000023", "ods_code": "RA707"}, + {"lsoa_code": "E01017690", "lad_code": "E06000060", "ods_code": "RXQ02"}, + {"lsoa_code": "E01030677", "lad_code": "E07000212", "ods_code": "RTK01"}, + {"lsoa_code": "E01013866", "lad_code": "E06000018", "ods_code": "RX128"}, + {"lsoa_code": "E01031488", "lad_code": "E07000225", "ods_code": "RYR16"}, + {"lsoa_code": "E01005766", "lad_code": "E08000007", "ods_code": "RWJ04"}, + {"lsoa_code": "E01003013", "lad_code": "E09000022", "ods_code": "RJ122"}, + {"lsoa_code": "W01001686", "lad_code": "W06000022", "ods_code": "7A6AT"}, + {"lsoa_code": "E01008704", "lad_code": "E08000024", "ods_code": "R0B01"}, + {"lsoa_code": "E01032714", "lad_code": "E06000030", "ods_code": "RN341"}, + {"lsoa_code": "E01005944", "lad_code": "E08000008", "ods_code": "RMP01"}, + {"lsoa_code": "E01017207", "lad_code": "E06000045", "ods_code": "R1CD1"}, + {"lsoa_code": "W01001508", "lad_code": "W06000020", "ods_code": "7A6G9"}, + {"lsoa_code": "E01015547", "lad_code": "E06000030", "ods_code": "RN325"}, + {"lsoa_code": "E01012018", "lad_code": "E06000002", "ods_code": "RTRAT"}, + {"lsoa_code": "E01033088", "lad_code": "E07000110", "ods_code": "RWF03"}, + {"lsoa_code": "W01000967", "lad_code": "W06000012", "ods_code": "7A3LE"}, + {"lsoa_code": "E01031144", "lad_code": "E07000220", "ods_code": "RJC66"}, + {"lsoa_code": "E01014102", "lad_code": "E06000020", "ods_code": "RXWAT"}, + {"lsoa_code": "E01026714", "lad_code": "E07000146", "ods_code": "RCX70"}, + {"lsoa_code": "W01001242", "lad_code": "W06000016", "ods_code": "7A5B1"}, + {"lsoa_code": "E01004322", "lad_code": "E09000030", "ods_code": "R1H12"}, + {"lsoa_code": "E01008395", "lad_code": "E08000021", "ods_code": "RTD02"}, + {"lsoa_code": "E01024825", "lad_code": "E07000116", "ods_code": "RWFTW"}, + {"lsoa_code": "E01002770", "lad_code": "E09000019", "ods_code": "RKEQ4"}, + {"lsoa_code": "E01015251", "lad_code": "E06000027", "ods_code": "RA901"}, + {"lsoa_code": "E01006123", "lad_code": "E08000009", "ods_code": "RM321"}, + {"lsoa_code": "E01006186", "lad_code": "E08000009", "ods_code": "Q3K9W"}, + {"lsoa_code": "E01000854", "lad_code": "E09000007", "ods_code": "RRV03"}, + {"lsoa_code": "E01009580", "lad_code": "E08000026", "ods_code": "RKB01"}, + {"lsoa_code": "E01003291", "lad_code": "E09000023", "ods_code": "RJ224"}, + {"lsoa_code": "W01001104", "lad_code": "W06000014", "ods_code": "7A4C1"}, + {"lsoa_code": "E01011952", "lad_code": "E06000001", "ods_code": "RVWAA"}, + {"lsoa_code": "E01020702", "lad_code": "E06000047", "ods_code": "RXPCP"}, + {"lsoa_code": "E01032544", "lad_code": "E06000004", "ods_code": "RVWAE"}, + {"lsoa_code": "E01016514", "lad_code": "E06000039", "ods_code": "RWX85"}, + {"lsoa_code": "E01007214", "lad_code": "E08000015", "ods_code": "RBL02"}, + {"lsoa_code": "E01005708", "lad_code": "E08000006", "ods_code": "RM331"}, + {"lsoa_code": "E01027549", "lad_code": "E06000057", "ods_code": "RTFED"}, + {"lsoa_code": "E01012457", "lad_code": "E06000007", "ods_code": "RWWWH"}, + {"lsoa_code": "E01031323", "lad_code": "E07000222", "ods_code": "RJC02"}, + {"lsoa_code": "E01023902", "lad_code": "E07000103", "ods_code": "RWG02"}, + {"lsoa_code": "E01019285", "lad_code": "E06000063", "ods_code": "RNNBX"}, + {"lsoa_code": "E01017207", "lad_code": "E06000045", "ods_code": "R1C03"}, + {"lsoa_code": "E01002681", "lad_code": "E09000018", "ods_code": "RQM91"}, + {"lsoa_code": "E01014803", "lad_code": "E06000024", "ods_code": "RVJJ8"}, + {"lsoa_code": "E01030142", "lad_code": "E07000245", "ods_code": "RGR50"}, + {"lsoa_code": "E01016519", "lad_code": "E06000039", "ods_code": "RDU50"}, + {"lsoa_code": "E01004363", "lad_code": "E09000031", "ods_code": "R1HKH"}, + {"lsoa_code": "E01006499", "lad_code": "E08000011", "ods_code": "RBN01"}, + {"lsoa_code": "E01024004", "lad_code": "E07000105", "ods_code": "RVV01"}, + {"lsoa_code": "E01005083", "lad_code": "E08000003", "ods_code": "RM311"}, + {"lsoa_code": "W01000572", "lad_code": "W06000009", "ods_code": "7A2BL"}, + {"lsoa_code": "E01032309", "lad_code": "E07000237", "ods_code": "RWP50"}, + {"lsoa_code": "E01019121", "lad_code": "E06000063", "ods_code": "RNN42"}, + {"lsoa_code": "E01031789", "lad_code": "E07000229", "ods_code": "RYR18"}, + {"lsoa_code": "E01011734", "lad_code": "E08000035", "ods_code": "RY661"}, + {"lsoa_code": "W01000350", "lad_code": "W06000006", "ods_code": "7A1A4"}, + {"lsoa_code": "E01017839", "lad_code": "E06000060", "ods_code": "RXQ50"}, + {"lsoa_code": "E01005070", "lad_code": "E08000003", "ods_code": "R0A07"}, + {"lsoa_code": "E01005070", "lad_code": "E08000003", "ods_code": "RM325"}, + {"lsoa_code": "E01029231", "lad_code": "E06000066", "ods_code": "RA430"}, + {"lsoa_code": "E01013343", "lad_code": "E06000014", "ods_code": "RCB55"}, + {"lsoa_code": "W01000191", "lad_code": "W06000004", "ods_code": "7A1A1"}, + {"lsoa_code": "W01000102", "lad_code": "W06000002", "ods_code": "7A1AU"}, + {"lsoa_code": "W01001434", "lad_code": "W06000018", "ods_code": "7A6AV"}, +] diff --git a/rcpch_nhs_organisations/hospitals/constants/rcpch_organisations.py b/rcpch_nhs_organisations/hospitals/constants/rcpch_organisations.py index cb0cdf5..bb17d04 100644 --- a/rcpch_nhs_organisations/hospitals/constants/rcpch_organisations.py +++ b/rcpch_nhs_organisations/hospitals/constants/rcpch_organisations.py @@ -517,7 +517,7 @@ "Address3": "", "City": "BRADFORD", "County": "WEST YORKSHIRE", - "Postcode": "BD9 6RH", + "Postcode": "BD9 6RJ", "Latitude": "53.80683265969457", "Longitude": "-1.7966404739876927", "ParentODSCode": "RAE", @@ -1117,7 +1117,7 @@ "Address3": "", "City": "STANLEY", "County": "COUNTY DURHAM", - "Postcode": "DH9 7TG", + "Postcode": "DL3 6HX", # "DH9 7TG" deprecated "Latitude": "54.857744", "Longitude": "-1.736403", "ParentODSCode": "RXP", @@ -6942,7 +6942,7 @@ "Address3": "", "City": "SWINDON", "County": "WILTSHIRE", - "Postcode": "SN1 1ED", + "Postcode": "SN1 2DL", # "SN1 1ED" does not exist - not also clear if this is a GP surgery or whether paediatric services are provided here "Latitude": "51.563553146767845", "Longitude": "-1.7808641421440663", "ParentODSCode": "RN3", @@ -8518,7 +8518,7 @@ "Address3": "", "City": "ST HELIER", "County": "JERSEY", - "Postcode": "JE1 3Q", + "Postcode": "JE1 3QS", "Latitude": "49.18841258908002", "Longitude": "-2.1122213730166157", "ParentODSCode": "RGT1W", # This is a special case as it is both an organisation and a trust so the parent trust ODS code is the same as the organisation code and exists in both models. diff --git a/rcpch_nhs_organisations/hospitals/general_functions/postcode_api.py b/rcpch_nhs_organisations/hospitals/general_functions/postcode_api.py index e08c395..f0ea93d 100644 --- a/rcpch_nhs_organisations/hospitals/general_functions/postcode_api.py +++ b/rcpch_nhs_organisations/hospitals/general_functions/postcode_api.py @@ -1,7 +1,15 @@ +""" +Functions to interact with the RCPCH instance of the Postcodes API +""" + +# Python imports import requests from requests.exceptions import HTTPError import os +# Django imports +from django.apps import apps + def fetch_by_postcode(postcode: str): """ @@ -33,3 +41,56 @@ def fetch_by_postcode(postcode: str): return None return response.json()["result"] + + +def generate_lsoa_lad_for_all_organisations(organisation_list=None): + """ + Generates a dictionary of LSOA and LAD codes for all organisations + + Current errors in the stored postcodes include: + BD9 6RH - Bradford Royal Infirmary should be BD9 6RJ + DL3 6HX - Child Development Centre in County Durham, Darlington is correct but not in the Postcode API + DA2 8DA - This is the correct postcode for Darent Valley Hospital but does not exist in the Postcode API + JE1 3Q should be JE1 3QS - it is in Jersey, Channel Islands, so should be excluded + SN1 1ED - probably a GP practice, but might provide paediatric services in Swindon: change to SN1 2DL + M27 6BP - Swinton Gateway provides podiatry and audiology services in Salford, but is not in the Postcode API. Not clear if it provides paediatric services. Update to M27 6BP does exist but is not in the Postcode API. + """ + Organisation = apps.get_model("hospitals", "Organisation") + LocalAuthorityDistrict = apps.get_model("hospitals", "LocalAuthorityDistrict") + all_codes = [] + organisation_list = organisation_list or Organisation.objects.all() + for organisation in organisation_list: + if ( + organisation.postcode and organisation.ods_code != "RGT1W" + ): # exclude Jersey General Hospital + new_code = { + "lsoa_code": None, + "lad_code": None, + "ods_code": organisation.ods_code, + } + postcode_data = fetch_by_postcode(organisation.postcode) + if postcode_data: + new_code["lsoa_code"] = postcode_data["codes"]["lsoa"] + new_code["lad_code"] = postcode_data["codes"]["admin_district"] + new_code["ods_code"] = organisation.ods_code + all_codes.append(new_code) + if LocalAuthorityDistrict.objects.filter( + lad24cd=new_code["lad_code"] + ).exists(): + organisation.local_authority_district = ( + LocalAuthorityDistrict.objects.get(lad24cd=new_code["lad_code"]) + ) + organisation.save() + + print( + f"{organisation.name} updated with LSOA {new_code['lsoa_code']} and LAD {new_code['lad_code']} codes" + ) + else: + print(f"{organisation.name} not updated") + else: + print(f"{organisation.name} has no postcode") + + # save the codes to a file + with open("lsoa_lad_codes.txt", "w") as f: + f.write(str(all_codes)) + print("Codes saved to file") diff --git a/rcpch_nhs_organisations/hospitals/management/commands/seed.py b/rcpch_nhs_organisations/hospitals/management/commands/seed.py index 803fb22..c812f28 100644 --- a/rcpch_nhs_organisations/hospitals/management/commands/seed.py +++ b/rcpch_nhs_organisations/hospitals/management/commands/seed.py @@ -5,6 +5,7 @@ # RCPCH from .seed_functions import ( + seed_local_authorities_and_lsoas, seed_organisations, seed_trusts, seed_paediatric_diabetes_networks, @@ -66,6 +67,10 @@ def handle(self, *args, **options): ) update_pdu_networks() rcpch_ascii_art() + elif options["level"] == "local_authorities": + self.stdout.write(B + "Adding local authorities and LSOAs..." + W) + seed_local_authorities_and_lsoas() + rcpch_ascii_art() elif options["level"] == "all": self.stdout.write( B + "Adding all organisations and levels of abstraction..." + W @@ -75,6 +80,8 @@ def handle(self, *args, **options): seed_organisations() create_jersey_general_hospital() seed_pdus() + seed_paediatric_diabetes_networks() + seed_local_authorities_and_lsoas() rcpch_ascii_art() else: diff --git a/rcpch_nhs_organisations/hospitals/management/commands/seed_functions/__init__.py b/rcpch_nhs_organisations/hospitals/management/commands/seed_functions/__init__.py index b06e246..a90fe81 100644 --- a/rcpch_nhs_organisations/hospitals/management/commands/seed_functions/__init__.py +++ b/rcpch_nhs_organisations/hospitals/management/commands/seed_functions/__init__.py @@ -1,5 +1,6 @@ from .abstraction_levels import * from .jersey import * +from .local_authorities import * from .organisations import * from .paediatric_diabetes_networks import * from .pdus import * diff --git a/rcpch_nhs_organisations/hospitals/management/commands/seed_functions/local_authorities.py b/rcpch_nhs_organisations/hospitals/management/commands/seed_functions/local_authorities.py new file mode 100644 index 0000000..a2d2e26 --- /dev/null +++ b/rcpch_nhs_organisations/hospitals/management/commands/seed_functions/local_authorities.py @@ -0,0 +1,63 @@ +# Python imports +import logging + +# Django imports +from django.apps import apps + +# RCPCH imports +from rcpch_nhs_organisations.hospitals.constants.organisation_ods_code_to_lsoa_lad_code_mapping import ( + ORGANISATION_TO_LSOA_LAD_MAPPING, +) + +# logger setup +logger = logging.getLogger("hospitals") + + +# seeds the local authorities table with data from the local authorities csv file +def seed_local_authorities_and_lsoas(): + """ + Seed the Organisation table with the local authorities and LSOA data + """ + Organisation = apps.get_model("hospitals", "Organisation") + LocalAuthorityDistrict = apps.get_model("hospitals", "LocalAuthorityDistrict") + LowerLayerSuperOutputArea = apps.get_model("hospitals", "LowerLayerSuperOutputArea") + + for organisation_mapping in ORGANISATION_TO_LSOA_LAD_MAPPING: + if Organisation.objects.filter( + ods_code=organisation_mapping["ods_code"] + ).exists(): + organisation = Organisation.objects.get( + ods_code=organisation_mapping["ods_code"] + ) + if LowerLayerSuperOutputArea.objects.filter( + lsoa11cd=organisation_mapping["lsoa_code"] + ).exists(): + lsoa = LowerLayerSuperOutputArea.objects.get( + lsoa11cd=organisation_mapping["lsoa_code"] + ) + organisation.lower_layer_super_output_area = lsoa + logger.info(f"Updated {organisation.name} with LSOA {lsoa}") + else: + logger.warning( + f"LSOA with code {organisation_mapping['lsoa_code']} not found for {organisation.name}" + ) + if LocalAuthorityDistrict.objects.filter( + lad24cd=organisation_mapping["lad_code"] + ).exists(): + lad = LocalAuthorityDistrict.objects.get( + lad24cd=organisation_mapping["lad_code"] + ) + organisation.local_authority_district = lad + logger.info(f"Updated {organisation.name} with Local Authority {lad}") + else: + logger.warning( + f"Local authority with code {organisation_mapping['lad_code']} not found for {organisation.name}" + ) + organisation.save() + else: + print( + f"Organisation with ODS code {organisation_mapping['ods_code']} not found" + ) + logger.info( + f"{Organisation.objects.filter(local_authority_district__isnull=False).count()} Local authorities and {Organisation.objects.filter(lower_layer_super_output_area__isnull=False).count()} LSOAs seeded of total {Organisation.objects.count()} organisations." + ) diff --git a/rcpch_nhs_organisations/hospitals/management/commands/seed_functions/paediatric_diabetes_networks.py b/rcpch_nhs_organisations/hospitals/management/commands/seed_functions/paediatric_diabetes_networks.py index 9f2b383..923530f 100644 --- a/rcpch_nhs_organisations/hospitals/management/commands/seed_functions/paediatric_diabetes_networks.py +++ b/rcpch_nhs_organisations/hospitals/management/commands/seed_functions/paediatric_diabetes_networks.py @@ -26,11 +26,14 @@ def seed_paediatric_diabetes_networks(): "PaediatricDiabetesNetworks already seeded. Updating existing records." ) for network in PAEDIATRIC_DIABETES_NETWORKS: - PaediatricDiabetesNetwork.objects.update_or_create( + created, _ = PaediatricDiabetesNetwork.objects.update_or_create( pn_code=network["id"], defaults={"name": network["name"]}, ) - logger.info(f"Seeded PaediatricDiabetesNetwork: {network['name']}") + if created: + logger.info(f"Seeded PaediatricDiabetesNetwork: {network['name']}") + else: + logger.info(f"Updated PaediatricDiabetesNetwork: {network['name']}") def update_pdu_networks(): diff --git a/rcpch_nhs_organisations/hospitals/migrations/0012_localauthoritydistrict.py b/rcpch_nhs_organisations/hospitals/migrations/0012_localauthoritydistrict.py new file mode 100644 index 0000000..746f755 --- /dev/null +++ b/rcpch_nhs_organisations/hospitals/migrations/0012_localauthoritydistrict.py @@ -0,0 +1,53 @@ +# Generated by Django 4.2.11 on 2024-12-08 13:13 + +import django.contrib.gis.db.models.fields +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("hospitals", "0011_alter_trust_ods_code"), + ] + + operations = [ + migrations.CreateModel( + name="LocalAuthorityDistrict", + fields=[ + ( + "id", + models.BigAutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("created_at", models.DateTimeField(auto_now_add=True)), + ("updated_at", models.DateTimeField(auto_now=True)), + ("lad24cd", models.CharField(max_length=9)), + ("lad24nm", models.CharField(max_length=36)), + ("lad24nmw", models.CharField(blank=True, max_length=24, null=True)), + ("bng_e", models.BigIntegerField()), + ("bng_n", models.BigIntegerField()), + ("long", models.FloatField()), + ("lat", models.FloatField()), + ("globalid", models.CharField(max_length=38)), + ( + "geom", + django.contrib.gis.db.models.fields.MultiPolygonField(srid=27700), + ), + ("publication_date", models.DateField(blank=True, null=True)), + ], + options={ + "verbose_name": "Local Authority District", + "verbose_name_plural": "Local Authority Districts", + "ordering": ("lad24nm",), + "indexes": [ + models.Index( + fields=["lad24cd"], name="hospitals_l_lad24cd_eb5d42_idx" + ) + ], + }, + ), + ] diff --git a/rcpch_nhs_organisations/hospitals/migrations/0013_seed_local_authority_districts_boundaries.py b/rcpch_nhs_organisations/hospitals/migrations/0013_seed_local_authority_districts_boundaries.py new file mode 100644 index 0000000..17945fc --- /dev/null +++ b/rcpch_nhs_organisations/hospitals/migrations/0013_seed_local_authority_districts_boundaries.py @@ -0,0 +1,56 @@ +from django.db import migrations +from django.apps import apps as django_apps + +import os +from django.contrib.gis.utils import LayerMapping + +""" +Local Authority Districts May 2024 Boundaries UK BUC +https://geoportal.statistics.gov.uk/search?q=BDY_LAD%202024&sort=Title%7Ctitle%7Casc +""" + +# Auto-generated `LayerMapping` dictionary for LocalAuthorityDistrict model +localauthoritydistrict_mapping = { + "lad24cd": "LAD24CD", + "lad24nm": "LAD24NM", + "lad24nmw": "LAD24NMW", + "bng_e": "BNG_E", + "bng_n": "BNG_N", + "long": "LONG", + "lat": "LAT", + "globalid": "GlobalID", + "geom": "MULTIPOLYGON", +} + + +# Boundary files + +app_config = django_apps.get_app_config("hospitals") +app_path = app_config.path + +Local_Authority_Districts_May_2024_Boundaries_UK_BUC = os.path.join( + app_path, + "shape_files", + "Local_Authority_Districts_May_2024_Boundaries_UK_BUC", + "LAD_MAY_2024_UK_BUC.shp", +) + + +def load(apps, schema_editor, verbose=True): + LocalAuthorityDistrict = apps.get_model("hospitals", "LocalAuthorityDistrict") + lm = LayerMapping( + LocalAuthorityDistrict, + Local_Authority_Districts_May_2024_Boundaries_UK_BUC, + localauthoritydistrict_mapping, + transform=False, + encoding="utf-8", + ) + lm.save(strict=True, verbose=verbose) + + +class Migration(migrations.Migration): + dependencies = [ + ("hospitals", "0012_localauthoritydistrict"), + ] + + operations = [migrations.RunPython(load)] diff --git a/rcpch_nhs_organisations/hospitals/migrations/0014_organisation_local_authority_district.py b/rcpch_nhs_organisations/hospitals/migrations/0014_organisation_local_authority_district.py new file mode 100644 index 0000000..a93b572 --- /dev/null +++ b/rcpch_nhs_organisations/hospitals/migrations/0014_organisation_local_authority_district.py @@ -0,0 +1,26 @@ +# Generated by Django 4.2.11 on 2024-12-08 13:36 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ("hospitals", "0013_seed_local_authority_districts_boundaries"), + ] + + operations = [ + migrations.AddField( + model_name="organisation", + name="local_authority_district", + field=models.ForeignKey( + blank=True, + default=None, + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name="local_authority_district_organisations", + to="hospitals.localauthoritydistrict", + ), + ), + ] diff --git a/rcpch_nhs_organisations/hospitals/migrations/0015_lowerlayersuperoutputarea.py b/rcpch_nhs_organisations/hospitals/migrations/0015_lowerlayersuperoutputarea.py new file mode 100644 index 0000000..a8d79dd --- /dev/null +++ b/rcpch_nhs_organisations/hospitals/migrations/0015_lowerlayersuperoutputarea.py @@ -0,0 +1,53 @@ +# Generated by Django 4.2.11 on 2024-12-08 14:57 + +import django.contrib.gis.db.models.fields +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("hospitals", "0014_organisation_local_authority_district"), + ] + + operations = [ + migrations.CreateModel( + name="LowerLayerSuperOutputArea", + fields=[ + ( + "id", + models.BigAutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("created_at", models.DateTimeField(auto_now_add=True)), + ("updated_at", models.DateTimeField(auto_now=True)), + ("lsoa11cd", models.CharField(max_length=9)), + ("lsoa11nm", models.CharField(max_length=50)), + ("lsoa11nmw", models.CharField(blank=True, max_length=50, null=True)), + ("bng_e", models.BigIntegerField()), + ("bng_n", models.BigIntegerField()), + ("long", models.FloatField()), + ("lat", models.FloatField()), + ("globalid", models.CharField(max_length=38)), + ( + "geom", + django.contrib.gis.db.models.fields.MultiPolygonField(srid=27700), + ), + ("publication_date", models.DateField(blank=True, null=True)), + ], + options={ + "verbose_name": "Local Authority District", + "verbose_name_plural": "Local Authority Districts", + "ordering": ("lsoa11nm",), + "indexes": [ + models.Index( + fields=["lsoa11cd"], name="hospitals_l_lsoa11c_c42f95_idx" + ) + ], + }, + ), + ] diff --git a/rcpch_nhs_organisations/hospitals/migrations/0016_seed_lsoa_boundaries.py b/rcpch_nhs_organisations/hospitals/migrations/0016_seed_lsoa_boundaries.py new file mode 100644 index 0000000..dd4b441 --- /dev/null +++ b/rcpch_nhs_organisations/hospitals/migrations/0016_seed_lsoa_boundaries.py @@ -0,0 +1,56 @@ +from django.db import migrations +from django.apps import apps as django_apps + +import os +from django.contrib.gis.utils import LayerMapping + +""" +Local Authority Districts May 2024 Boundaries UK BUC +https://geoportal.statistics.gov.uk/search?q=BDY_LAD%202024&sort=Title%7Ctitle%7Casc +""" + +# Auto-generated `LayerMapping` dictionary for LowerLayerSuperOutputArea model +lowerlayersuperoutputarea_mapping = { + "lsoa11cd": "LSOA11CD", + "lsoa11nm": "LSOA11NM", + "lsoa11nmw": "LSOA11NMW", + "bng_e": "BNG_E", + "bng_n": "BNG_N", + "long": "LONG", + "lat": "LAT", + "globalid": "GlobalID", + "geom": "MULTIPOLYGON", +} + + +# Boundary files + +app_config = django_apps.get_app_config("hospitals") +app_path = app_config.path + +LSOA_2011_Boundaries_Super_Generalised_Clipped_BSC_EW_V4 = os.path.join( + app_path, + "shape_files", + "LSOA_2011_Boundaries_Super_Generalised_Clipped_BSC_EW_V4", + "LSOA_2011_EW_BSC_V4.shp", +) + + +def load(apps, schema_editor, verbose=True): + LowerLayerSuperOutputArea = apps.get_model("hospitals", "LowerLayerSuperOutputArea") + lm = LayerMapping( + LowerLayerSuperOutputArea, + LSOA_2011_Boundaries_Super_Generalised_Clipped_BSC_EW_V4, + lowerlayersuperoutputarea_mapping, + transform=False, + encoding="utf-8", + ) + lm.save(strict=True, verbose=verbose) + + +class Migration(migrations.Migration): + dependencies = [ + ("hospitals", "0015_lowerlayersuperoutputarea"), + ] + + operations = [migrations.RunPython(load)] diff --git a/rcpch_nhs_organisations/hospitals/migrations/0017_organisation_lower_layer_super_output_area.py b/rcpch_nhs_organisations/hospitals/migrations/0017_organisation_lower_layer_super_output_area.py new file mode 100644 index 0000000..498533f --- /dev/null +++ b/rcpch_nhs_organisations/hospitals/migrations/0017_organisation_lower_layer_super_output_area.py @@ -0,0 +1,26 @@ +# Generated by Django 4.2.11 on 2024-12-08 15:04 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ("hospitals", "0016_seed_lsoa_boundaries"), + ] + + operations = [ + migrations.AddField( + model_name="organisation", + name="lower_layer_super_output_area", + field=models.ForeignKey( + blank=True, + default=None, + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name="lower_layer_super_output_area_organisations", + to="hospitals.lowerlayersuperoutputarea", + ), + ), + ] diff --git a/rcpch_nhs_organisations/hospitals/models/__init__.py b/rcpch_nhs_organisations/hospitals/models/__init__.py index 3bdd854..c507d0f 100644 --- a/rcpch_nhs_organisations/hospitals/models/__init__.py +++ b/rcpch_nhs_organisations/hospitals/models/__init__.py @@ -2,6 +2,11 @@ from .general_practice import GeneralPractice from .integrated_care_board import IntegratedCareBoard, IntegratedCareBoardBoundaries from .local_health_board import LocalHealthBoard, LocalHealthBoardBoundaries +from .local_authority import LocalAuthorityDistrict, LocalAuthorityDistrictBoundaries +from .lower_layer_super_output_area import ( + LowerLayerSuperOutputArea, + LowerLayerSuperOutputArea2011Boundaries, +) from .london_borough import LondonBorough, LondonBoroughBoundaries from .nhs_england_region import NHSEnglandRegion, NHSEnglandRegionBoundaries from .open_uk_network import OPENUKNetwork diff --git a/rcpch_nhs_organisations/hospitals/models/local_authority.py b/rcpch_nhs_organisations/hospitals/models/local_authority.py new file mode 100644 index 0000000..2a35d04 --- /dev/null +++ b/rcpch_nhs_organisations/hospitals/models/local_authority.py @@ -0,0 +1,49 @@ +""" +# Auto-generated `LayerMapping` dictionary for LocalAuthorityDistrict model +localauthoritydistrict_mapping = { + 'lad24cd': 'LAD24CD', + 'lad24nm': 'LAD24NM', + 'lad24nmw': 'LAD24NMW', + 'bng_e': 'BNG_E', + 'bng_n': 'BNG_N', + 'long': 'LONG', + 'lat': 'LAT', + 'globalid': 'GlobalID', + 'geom': 'MULTIPOLYGON', +} +""" + +from django.contrib.gis.db import models +from .time_and_user_abstract_base_classes import TimeStampAbstractBaseClass + + +class LocalAuthorityDistrictBoundaries(TimeStampAbstractBaseClass): + lad24cd = models.CharField(max_length=9) + lad24nm = models.CharField(max_length=36) + lad24nmw = models.CharField(max_length=24, null=True, blank=True) + bng_e = models.BigIntegerField() + bng_n = models.BigIntegerField() + long = models.FloatField() + lat = models.FloatField() + globalid = models.CharField(max_length=38) + geom = models.MultiPolygonField(srid=27700) + + class Meta: + abstract = True + + +class LocalAuthorityDistrict(LocalAuthorityDistrictBoundaries): + + publication_date = models.DateField(blank=True, null=True) + + class Meta: + indexes = [models.Index(fields=["lad24cd"])] + verbose_name = "Local Authority District" + verbose_name_plural = "Local Authority Districts" + ordering = ("lad24nm",) + + def __str__(self) -> str: + return self.lad24nm + + def get_publication_date(self) -> str: + return self.publication_date diff --git a/rcpch_nhs_organisations/hospitals/models/lower_layer_super_output_area.py b/rcpch_nhs_organisations/hospitals/models/lower_layer_super_output_area.py new file mode 100644 index 0000000..c9ea94a --- /dev/null +++ b/rcpch_nhs_organisations/hospitals/models/lower_layer_super_output_area.py @@ -0,0 +1,50 @@ +# Auto-generated `LayerMapping` dictionary for LowerLayerSuperOutputArea model +# lowerlayersuperoutputarea_mapping = { +# 'lsoa11cd': 'LSOA11CD', +# 'lsoa11nm': 'LSOA11NM', +# 'lsoa11nmw': 'LSOA11NMW', +# 'bng_e': 'BNG_E', +# 'bng_n': 'BNG_N', +# 'long': 'LONG', +# 'lat': 'LAT', +# 'globalid': 'GlobalID', +# 'geom': 'MULTIPOLYGON', +# } + +from django.contrib.gis.db import models +from .time_and_user_abstract_base_classes import TimeStampAbstractBaseClass + + +class LowerLayerSuperOutputArea2011Boundaries(TimeStampAbstractBaseClass): + lsoa11cd = models.CharField(max_length=9) + lsoa11nm = models.CharField(max_length=50) + lsoa11nmw = models.CharField(max_length=50, null=True, blank=True) + bng_e = models.BigIntegerField() + bng_n = models.BigIntegerField() + long = models.FloatField() + lat = models.FloatField() + globalid = models.CharField(max_length=38) + geom = models.MultiPolygonField(srid=27700) + + class Meta: + abstract = True + + +class LowerLayerSuperOutputArea(LowerLayerSuperOutputArea2011Boundaries): + + publication_date = models.DateField(blank=True, null=True) + + class Meta: + indexes = [models.Index(fields=["lsoa11cd"])] + verbose_name = "Local Authority District" + verbose_name_plural = "Local Authority Districts" + ordering = ("lsoa11nm",) + + def __str__(self) -> str: + return self.lsoa11nm + + def get_publication_date(self) -> str: + return self.publication_date + + +# When we come to map the 2021, we will need to create new fields for the lsoa21cd, lsoa21nm, lsoa21nmw and map those to the new fields in the 2021 shapefile. The LSOA model will need a conditional to return the correct name based on the publication date. diff --git a/rcpch_nhs_organisations/hospitals/models/organisation.py b/rcpch_nhs_organisations/hospitals/models/organisation.py index b5c67d3..2d8b5e8 100644 --- a/rcpch_nhs_organisations/hospitals/models/organisation.py +++ b/rcpch_nhs_organisations/hospitals/models/organisation.py @@ -109,6 +109,24 @@ class Organisation(TimeStampAbstractBaseClass): related_name="london_borough_organisations", ) + local_authority_district = models.ForeignKey( + to="LocalAuthorityDistrict", + on_delete=models.CASCADE, + null=True, + blank=True, + default=None, + related_name="local_authority_district_organisations", + ) + + lower_layer_super_output_area = models.ForeignKey( + to="LowerLayerSuperOutputArea", + on_delete=models.CASCADE, + null=True, + blank=True, + default=None, + related_name="lower_layer_super_output_area_organisations", + ) + country = models.ForeignKey( to=Country, on_delete=models.PROTECT, diff --git a/rcpch_nhs_organisations/hospitals/serializers/__init__.py b/rcpch_nhs_organisations/hospitals/serializers/__init__.py index 391cce4..3bb669f 100644 --- a/rcpch_nhs_organisations/hospitals/serializers/__init__.py +++ b/rcpch_nhs_organisations/hospitals/serializers/__init__.py @@ -3,6 +3,9 @@ IntegratedCareBoardSerializer, IntegratedCareBoardLimitedSerializer, ) +from .local_authority_district import ( + LocalAuthorityDistrictSerializer, +) from .local_health_board import ( LocalHealthBoardSerializer, LocalHealthBoardLimitedSerializer, @@ -12,6 +15,7 @@ NHSEnglandRegionSerializer, NHSEnglandRegionLimitedSerializer, ) +from .lower_layer_super_output_area import LowerLayerSuperOutputAreaSerializer from .openuk_network import OPENUKNetworkSerializer from .organisation import ( OrganisationSerializer, @@ -23,6 +27,7 @@ NHSEnglandRegionWithNestedOrganisationsSerializer, PaediatricDiabetesUnitWithNestedOrganisationSerializer, PaediatricDiabetesUnitWithNestedOrganisationAndParentSerializer, + OrganisationWithLSOAAndLADSerializer, ) from .paediatric_diabetes_unit import PaediatricDiabetesUnitSerializer from .trust import TrustSerializer, PaediatricDiabetesUnitWithNestedParentSerializer diff --git a/rcpch_nhs_organisations/hospitals/serializers/local_authority_district.py b/rcpch_nhs_organisations/hospitals/serializers/local_authority_district.py new file mode 100644 index 0000000..056f31c --- /dev/null +++ b/rcpch_nhs_organisations/hospitals/serializers/local_authority_district.py @@ -0,0 +1,42 @@ +from django.apps import apps +from rest_framework import serializers + +from drf_spectacular.utils import extend_schema_serializer, OpenApiExample + +from ..models import LocalAuthorityDistrict + + +@extend_schema_serializer( + examples=[ + OpenApiExample( + "/local_authority_district/2024/1/extended", + value={ + "lad24cd": "", + "lad24nm": "", + "lad24nmw": "", + "bng_e": "", + "bng_n": "", + "long": "", + "lat": "", + "globalid": "", + "geom": "", + }, + response_only=True, + ) + ] +) +class LocalAuthorityDistrictSerializer(serializers.ModelSerializer): + class Meta: + model = LocalAuthorityDistrict + # depth = 1 + fields = [ + "lad24cd", + "lad24nm", + "lad24nmw", + "bng_e", + "bng_n", + "long", + "lat", + "globalid", + "geom", + ] diff --git a/rcpch_nhs_organisations/hospitals/serializers/lower_layer_super_output_area.py b/rcpch_nhs_organisations/hospitals/serializers/lower_layer_super_output_area.py new file mode 100644 index 0000000..7332129 --- /dev/null +++ b/rcpch_nhs_organisations/hospitals/serializers/lower_layer_super_output_area.py @@ -0,0 +1,42 @@ +from django.apps import apps +from rest_framework import serializers + +from drf_spectacular.utils import extend_schema_serializer, OpenApiExample + +from ..models import LowerLayerSuperOutputArea + + +@extend_schema_serializer( + examples=[ + OpenApiExample( + "/lower_layer_super_output_area/2011/1/extended", + value={ + "lsoa11cd": "", + "lsoa11nm": "", + "lsoa11nmw": "", + "bng_e": "", + "bng_n": "", + "long": "", + "lat": "", + "globalid": "", + "geom": "", + }, + response_only=True, + ) + ] +) +class LowerLayerSuperOutputAreaSerializer(serializers.ModelSerializer): + class Meta: + model = LowerLayerSuperOutputArea + # depth = 1 + fields = [ + "lsoa11cd", + "lsoa11nm", + "lsoa11nmw", + "bng_e", + "bng_n", + "long", + "lat", + "globalid", + "geom", + ] diff --git a/rcpch_nhs_organisations/hospitals/serializers/organisation.py b/rcpch_nhs_organisations/hospitals/serializers/organisation.py index ab57061..6c801d1 100644 --- a/rcpch_nhs_organisations/hospitals/serializers/organisation.py +++ b/rcpch_nhs_organisations/hospitals/serializers/organisation.py @@ -24,11 +24,13 @@ IntegratedCareBoardSerializer, IntegratedCareBoardLimitedSerializer, ) +from .local_authority_district import LocalAuthorityDistrictSerializer from .local_health_board import ( LocalHealthBoardSerializer, LocalHealthBoardLimitedSerializer, ) from .london_borough import LondonBoroughSerializer, LondonBoroughLimitedSerializer +from .lower_layer_super_output_area import LowerLayerSuperOutputAreaSerializer from .nhs_england_region import ( NHSEnglandRegionSerializer, NHSEnglandRegionLimitedSerializer, @@ -4559,6 +4561,8 @@ class OrganisationSerializer(serializers.ModelSerializer): ) london_borough = LondonBoroughLimitedSerializer() country = CountryLimitedSerializer() + lower_layer_super_output_area = LowerLayerSuperOutputAreaSerializer() + local_authority_district = LocalAuthorityDistrictSerializer() class Meta: model = Organisation @@ -4578,6 +4582,8 @@ class Meta: "geocode_coordinates", "active", "published_at", + "local_authority_district", + "lower_layer_super_output_area", "paediatric_diabetes_unit", "trust", "local_health_board", @@ -4607,7 +4613,10 @@ class OrganisationNoParentsSerializer(serializers.ModelSerializer): # returns only ods_code and name class Meta: model = Organisation - fields = ["ods_code", "name"] + fields = [ + "ods_code", + "name", + ] class OrganisationTrustLHBParentSerializer(serializers.ModelSerializer): @@ -4735,6 +4744,36 @@ class Meta: ] +class OrganisationWithLSOAAndLADSerializer(serializers.ModelSerializer): + # serializes an organisation with its LSOA and LAD + + lower_layer_super_output_area = serializers.SerializerMethodField() + local_authority_district = serializers.SerializerMethodField() + + def get_lower_layer_super_output_area(self, obj): + if obj.lower_layer_super_output_area is not None: + return LowerLayerSuperOutputAreaSerializer( + obj.lower_layer_super_output_area + ).data + else: + return None + + def get_local_authority_district(self, obj): + if obj.local_authority_district is not None: + return LocalAuthorityDistrictSerializer(obj.local_authority_district).data + else: + return None + + class Meta: + model = Organisation + fields = [ + "ods_code", + "name", + "lower_layer_super_output_area", + "local_authority_district", + ] + + @extend_schema_serializer( examples=[ OpenApiExample( @@ -4747,7 +4786,7 @@ class Meta: class PaediatricDiabetesUnitWithNestedOrganisationSerializer( serializers.ModelSerializer ): - organisations = OrganisationNoParentsSerializer( + organisations = OrganisationWithLSOAAndLADSerializer( many=True, read_only=True, source="paediatric_diabetes_unit_organisations" ) diff --git a/rcpch_nhs_organisations/hospitals/shape_files/LSOA_2011_Boundaries_Super_Generalised_Clipped_BSC_EW_V4/LSOA_2011_EW_BSC_V4.cpg b/rcpch_nhs_organisations/hospitals/shape_files/LSOA_2011_Boundaries_Super_Generalised_Clipped_BSC_EW_V4/LSOA_2011_EW_BSC_V4.cpg new file mode 100644 index 0000000..3ad133c --- /dev/null +++ b/rcpch_nhs_organisations/hospitals/shape_files/LSOA_2011_Boundaries_Super_Generalised_Clipped_BSC_EW_V4/LSOA_2011_EW_BSC_V4.cpg @@ -0,0 +1 @@ +UTF-8 \ No newline at end of file diff --git a/rcpch_nhs_organisations/hospitals/shape_files/LSOA_2011_Boundaries_Super_Generalised_Clipped_BSC_EW_V4/LSOA_2011_EW_BSC_V4.dbf b/rcpch_nhs_organisations/hospitals/shape_files/LSOA_2011_Boundaries_Super_Generalised_Clipped_BSC_EW_V4/LSOA_2011_EW_BSC_V4.dbf new file mode 100644 index 0000000..5cd01ed Binary files /dev/null and b/rcpch_nhs_organisations/hospitals/shape_files/LSOA_2011_Boundaries_Super_Generalised_Clipped_BSC_EW_V4/LSOA_2011_EW_BSC_V4.dbf differ diff --git a/rcpch_nhs_organisations/hospitals/shape_files/LSOA_2011_Boundaries_Super_Generalised_Clipped_BSC_EW_V4/LSOA_2011_EW_BSC_V4.prj b/rcpch_nhs_organisations/hospitals/shape_files/LSOA_2011_Boundaries_Super_Generalised_Clipped_BSC_EW_V4/LSOA_2011_EW_BSC_V4.prj new file mode 100644 index 0000000..fec0ee2 --- /dev/null +++ b/rcpch_nhs_organisations/hospitals/shape_files/LSOA_2011_Boundaries_Super_Generalised_Clipped_BSC_EW_V4/LSOA_2011_EW_BSC_V4.prj @@ -0,0 +1 @@ +PROJCS["British_National_Grid",GEOGCS["GCS_OSGB_1936",DATUM["D_OSGB_1936",SPHEROID["Airy_1830",6377563.396,299.3249646]],PRIMEM["Greenwich",0.0],UNIT["Degree",0.0174532925199433]],PROJECTION["Transverse_Mercator"],PARAMETER["False_Easting",400000.0],PARAMETER["False_Northing",-100000.0],PARAMETER["Central_Meridian",-2.0],PARAMETER["Scale_Factor",0.9996012717],PARAMETER["Latitude_Of_Origin",49.0],UNIT["Meter",1.0]] \ No newline at end of file diff --git a/rcpch_nhs_organisations/hospitals/shape_files/LSOA_2011_Boundaries_Super_Generalised_Clipped_BSC_EW_V4/LSOA_2011_EW_BSC_V4.shp b/rcpch_nhs_organisations/hospitals/shape_files/LSOA_2011_Boundaries_Super_Generalised_Clipped_BSC_EW_V4/LSOA_2011_EW_BSC_V4.shp new file mode 100644 index 0000000..d9974f0 --- /dev/null +++ b/rcpch_nhs_organisations/hospitals/shape_files/LSOA_2011_Boundaries_Super_Generalised_Clipped_BSC_EW_V4/LSOA_2011_EW_BSC_V4.shp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:03a446365cd37ec73568556076b7e76c45e1afc3217229f4c0bd4b5d6f0bfad7 +size 8844904 diff --git a/rcpch_nhs_organisations/hospitals/shape_files/LSOA_2011_Boundaries_Super_Generalised_Clipped_BSC_EW_V4/LSOA_2011_EW_BSC_V4.shp.xml b/rcpch_nhs_organisations/hospitals/shape_files/LSOA_2011_Boundaries_Super_Generalised_Clipped_BSC_EW_V4/LSOA_2011_EW_BSC_V4.shp.xml new file mode 100644 index 0000000..7eaa7a5 --- /dev/null +++ b/rcpch_nhs_organisations/hospitals/shape_files/LSOA_2011_Boundaries_Super_Generalised_Clipped_BSC_EW_V4/LSOA_2011_EW_BSC_V4.shp.xml @@ -0,0 +1,2 @@ + +20240314145535001.0TRUE diff --git a/rcpch_nhs_organisations/hospitals/shape_files/LSOA_2011_Boundaries_Super_Generalised_Clipped_BSC_EW_V4/LSOA_2011_EW_BSC_V4.shx b/rcpch_nhs_organisations/hospitals/shape_files/LSOA_2011_Boundaries_Super_Generalised_Clipped_BSC_EW_V4/LSOA_2011_EW_BSC_V4.shx new file mode 100644 index 0000000..b4107ec Binary files /dev/null and b/rcpch_nhs_organisations/hospitals/shape_files/LSOA_2011_Boundaries_Super_Generalised_Clipped_BSC_EW_V4/LSOA_2011_EW_BSC_V4.shx differ diff --git a/rcpch_nhs_organisations/hospitals/shape_files/Local_Authority_Districts_May_2024_Boundaries_UK_BUC/LAD_MAY_2024_UK_BUC.cpg b/rcpch_nhs_organisations/hospitals/shape_files/Local_Authority_Districts_May_2024_Boundaries_UK_BUC/LAD_MAY_2024_UK_BUC.cpg new file mode 100644 index 0000000..3ad133c --- /dev/null +++ b/rcpch_nhs_organisations/hospitals/shape_files/Local_Authority_Districts_May_2024_Boundaries_UK_BUC/LAD_MAY_2024_UK_BUC.cpg @@ -0,0 +1 @@ +UTF-8 \ No newline at end of file diff --git a/rcpch_nhs_organisations/hospitals/shape_files/Local_Authority_Districts_May_2024_Boundaries_UK_BUC/LAD_MAY_2024_UK_BUC.dbf b/rcpch_nhs_organisations/hospitals/shape_files/Local_Authority_Districts_May_2024_Boundaries_UK_BUC/LAD_MAY_2024_UK_BUC.dbf new file mode 100644 index 0000000..55c73bb Binary files /dev/null and b/rcpch_nhs_organisations/hospitals/shape_files/Local_Authority_Districts_May_2024_Boundaries_UK_BUC/LAD_MAY_2024_UK_BUC.dbf differ diff --git a/rcpch_nhs_organisations/hospitals/shape_files/Local_Authority_Districts_May_2024_Boundaries_UK_BUC/LAD_MAY_2024_UK_BUC.prj b/rcpch_nhs_organisations/hospitals/shape_files/Local_Authority_Districts_May_2024_Boundaries_UK_BUC/LAD_MAY_2024_UK_BUC.prj new file mode 100644 index 0000000..fec0ee2 --- /dev/null +++ b/rcpch_nhs_organisations/hospitals/shape_files/Local_Authority_Districts_May_2024_Boundaries_UK_BUC/LAD_MAY_2024_UK_BUC.prj @@ -0,0 +1 @@ +PROJCS["British_National_Grid",GEOGCS["GCS_OSGB_1936",DATUM["D_OSGB_1936",SPHEROID["Airy_1830",6377563.396,299.3249646]],PRIMEM["Greenwich",0.0],UNIT["Degree",0.0174532925199433]],PROJECTION["Transverse_Mercator"],PARAMETER["False_Easting",400000.0],PARAMETER["False_Northing",-100000.0],PARAMETER["Central_Meridian",-2.0],PARAMETER["Scale_Factor",0.9996012717],PARAMETER["Latitude_Of_Origin",49.0],UNIT["Meter",1.0]] \ No newline at end of file diff --git a/rcpch_nhs_organisations/hospitals/shape_files/Local_Authority_Districts_May_2024_Boundaries_UK_BUC/LAD_MAY_2024_UK_BUC.shp b/rcpch_nhs_organisations/hospitals/shape_files/Local_Authority_Districts_May_2024_Boundaries_UK_BUC/LAD_MAY_2024_UK_BUC.shp new file mode 100644 index 0000000..668a916 --- /dev/null +++ b/rcpch_nhs_organisations/hospitals/shape_files/Local_Authority_Districts_May_2024_Boundaries_UK_BUC/LAD_MAY_2024_UK_BUC.shp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ef9d75e34bd75985e6d6296f91814bfbc8f76f891083516074f4ec70c4391787 +size 488256 diff --git a/rcpch_nhs_organisations/hospitals/shape_files/Local_Authority_Districts_May_2024_Boundaries_UK_BUC/LAD_MAY_2024_UK_BUC.shp.xml b/rcpch_nhs_organisations/hospitals/shape_files/Local_Authority_Districts_May_2024_Boundaries_UK_BUC/LAD_MAY_2024_UK_BUC.shp.xml new file mode 100644 index 0000000..40cf62d --- /dev/null +++ b/rcpch_nhs_organisations/hospitals/shape_files/Local_Authority_Districts_May_2024_Boundaries_UK_BUC/LAD_MAY_2024_UK_BUC.shp.xml @@ -0,0 +1,2 @@ + +20240702155756001.0TRUE diff --git a/rcpch_nhs_organisations/hospitals/shape_files/Local_Authority_Districts_May_2024_Boundaries_UK_BUC/LAD_MAY_2024_UK_BUC.shx b/rcpch_nhs_organisations/hospitals/shape_files/Local_Authority_Districts_May_2024_Boundaries_UK_BUC/LAD_MAY_2024_UK_BUC.shx new file mode 100644 index 0000000..05ddc4a Binary files /dev/null and b/rcpch_nhs_organisations/hospitals/shape_files/Local_Authority_Districts_May_2024_Boundaries_UK_BUC/LAD_MAY_2024_UK_BUC.shx differ diff --git a/rcpch_nhs_organisations/hospitals/views/organisation.py b/rcpch_nhs_organisations/hospitals/views/organisation.py index 5b35537..7c6d095 100644 --- a/rcpch_nhs_organisations/hospitals/views/organisation.py +++ b/rcpch_nhs_organisations/hospitals/views/organisation.py @@ -291,5 +291,10 @@ class OrganisationsAssociatedWithPaediatricDiabetesUnitsList(generics.ListAPIVie queryset = Organisation.objects.filter(paediatric_diabetes_unit__isnull=False) serializer_class = OrganisationSerializer filter_backends = [DjangoFilterBackend, filters.OrderingFilter] - filterset_fields = ["ods_code", "name"] + filterset_fields = [ + "ods_code", + "name", + "local_authority_district", + "lower_layer_super_output_area", + ] ordering_fields = ["name", "ods_code"] diff --git a/rcpch_nhs_organisations/hospitals/views/paediatric_diabetes_unit.py b/rcpch_nhs_organisations/hospitals/views/paediatric_diabetes_unit.py index 0968f87..83e9628 100644 --- a/rcpch_nhs_organisations/hospitals/views/paediatric_diabetes_unit.py +++ b/rcpch_nhs_organisations/hospitals/views/paediatric_diabetes_unit.py @@ -21,6 +21,7 @@ PaediatricDiabetesUnitWithNestedOrganisationSerializer, PaediatricDiabetesUnitWithNestedOrganisationAndParentSerializer, PaediatricDiabetesUnitWithNestedParentSerializer, + OrganisationWithLSOAAndLADSerializer, )