From d871fe6360bd199447ff2bd0314a294bebcccbb2 Mon Sep 17 00:00:00 2001 From: eatyourpeas Date: Sat, 23 Nov 2024 11:18:20 +0000 Subject: [PATCH 01/21] A Jersey .shp files from `https://gadm.org/download_country.html` --- .../gadm41_JEY_shp/gadm41_JEY_0.cpg | 1 + .../gadm41_JEY_shp/gadm41_JEY_0.dbf | Bin 0 -> 119 bytes .../gadm41_JEY_shp/gadm41_JEY_0.prj | 1 + .../gadm41_JEY_shp/gadm41_JEY_0.shp | Bin 0 -> 42356 bytes .../gadm41_JEY_shp/gadm41_JEY_0.shx | Bin 0 -> 108 bytes .../gadm41_JEY_shp/gadm41_JEY_1.cpg | 1 + .../gadm41_JEY_shp/gadm41_JEY_1.dbf | Bin 0 -> 1790 bytes .../gadm41_JEY_shp/gadm41_JEY_1.prj | 1 + .../gadm41_JEY_shp/gadm41_JEY_1.shp | Bin 0 -> 63916 bytes .../gadm41_JEY_shp/gadm41_JEY_1.shx | Bin 0 -> 196 bytes 10 files changed, 4 insertions(+) create mode 100644 epilepsy12/shape_files/Countries_December_2022_UK_BUC/gadm41_JEY_shp/gadm41_JEY_0.cpg create mode 100644 epilepsy12/shape_files/Countries_December_2022_UK_BUC/gadm41_JEY_shp/gadm41_JEY_0.dbf create mode 100644 epilepsy12/shape_files/Countries_December_2022_UK_BUC/gadm41_JEY_shp/gadm41_JEY_0.prj create mode 100644 epilepsy12/shape_files/Countries_December_2022_UK_BUC/gadm41_JEY_shp/gadm41_JEY_0.shp create mode 100644 epilepsy12/shape_files/Countries_December_2022_UK_BUC/gadm41_JEY_shp/gadm41_JEY_0.shx create mode 100644 epilepsy12/shape_files/Countries_December_2022_UK_BUC/gadm41_JEY_shp/gadm41_JEY_1.cpg create mode 100644 epilepsy12/shape_files/Countries_December_2022_UK_BUC/gadm41_JEY_shp/gadm41_JEY_1.dbf create mode 100644 epilepsy12/shape_files/Countries_December_2022_UK_BUC/gadm41_JEY_shp/gadm41_JEY_1.prj create mode 100644 epilepsy12/shape_files/Countries_December_2022_UK_BUC/gadm41_JEY_shp/gadm41_JEY_1.shp create mode 100644 epilepsy12/shape_files/Countries_December_2022_UK_BUC/gadm41_JEY_shp/gadm41_JEY_1.shx diff --git a/epilepsy12/shape_files/Countries_December_2022_UK_BUC/gadm41_JEY_shp/gadm41_JEY_0.cpg b/epilepsy12/shape_files/Countries_December_2022_UK_BUC/gadm41_JEY_shp/gadm41_JEY_0.cpg new file mode 100644 index 000000000..3ad133c04 --- /dev/null +++ b/epilepsy12/shape_files/Countries_December_2022_UK_BUC/gadm41_JEY_shp/gadm41_JEY_0.cpg @@ -0,0 +1 @@ +UTF-8 \ No newline at end of file diff --git a/epilepsy12/shape_files/Countries_December_2022_UK_BUC/gadm41_JEY_shp/gadm41_JEY_0.dbf b/epilepsy12/shape_files/Countries_December_2022_UK_BUC/gadm41_JEY_shp/gadm41_JEY_0.dbf new file mode 100644 index 0000000000000000000000000000000000000000..67c62f33422021877d54c3288b6163f4330196aa GIT binary patch literal 119 zcmZRsVi#g$U|>jO5CxK$z}?d&-T)%(45GNulsNl``h^5Vf<&>(^D20`Mk+vnS87pl JY9)v*1pvM>3P1n= literal 0 HcmV?d00001 diff --git a/epilepsy12/shape_files/Countries_December_2022_UK_BUC/gadm41_JEY_shp/gadm41_JEY_0.prj b/epilepsy12/shape_files/Countries_December_2022_UK_BUC/gadm41_JEY_shp/gadm41_JEY_0.prj new file mode 100644 index 000000000..f45cbadf0 --- /dev/null +++ b/epilepsy12/shape_files/Countries_December_2022_UK_BUC/gadm41_JEY_shp/gadm41_JEY_0.prj @@ -0,0 +1 @@ +GEOGCS["GCS_WGS_1984",DATUM["D_WGS_1984",SPHEROID["WGS_1984",6378137.0,298.257223563]],PRIMEM["Greenwich",0.0],UNIT["Degree",0.0174532925199433]] \ No newline at end of file diff --git a/epilepsy12/shape_files/Countries_December_2022_UK_BUC/gadm41_JEY_shp/gadm41_JEY_0.shp b/epilepsy12/shape_files/Countries_December_2022_UK_BUC/gadm41_JEY_shp/gadm41_JEY_0.shp new file mode 100644 index 0000000000000000000000000000000000000000..329845e776e922f7f994dd1df4d4e8cde2b80540 GIT binary patch literal 42356 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 F{C~uMZgKzs literal 0 HcmV?d00001 diff --git a/epilepsy12/shape_files/Countries_December_2022_UK_BUC/gadm41_JEY_shp/gadm41_JEY_0.shx b/epilepsy12/shape_files/Countries_December_2022_UK_BUC/gadm41_JEY_shp/gadm41_JEY_0.shx new file mode 100644 index 0000000000000000000000000000000000000000..fdf50b32cf286c4746b861a990afec6aa9c71d23 GIT binary patch literal 108 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 BQ=9+* literal 0 HcmV?d00001 diff --git a/epilepsy12/shape_files/Countries_December_2022_UK_BUC/gadm41_JEY_shp/gadm41_JEY_1.shx b/epilepsy12/shape_files/Countries_December_2022_UK_BUC/gadm41_JEY_shp/gadm41_JEY_1.shx new file mode 100644 index 0000000000000000000000000000000000000000..7271e0476806a1a21eba22c9549a8fea4d954913 GIT binary patch literal 196 zcmZQzQ0HR64w7ClGcd3M$!>SHQp!VaLG0>%hQ}+Qh)Xwu6Bo2gqlcz`#&&38;<%0P2q(+W-In literal 0 HcmV?d00001 From 6dc8123a730c1f5a1608abf4a0aaa064a9a78181 Mon Sep 17 00:00:00 2001 From: eatyourpeas Date: Sat, 23 Nov 2024 14:49:15 +0000 Subject: [PATCH 02/21] too big a commit - sorry --- documentation/docs/development/shape-files.md | 186 ++++++++++++++++++ epilepsy12/admin.py | 33 +++- epilepsy12/constants/rcpch_organisations.py | 25 +++ epilepsy12/constants/trust.py | 10 + epilepsy12/migrations/0001_initial.py | 32 ++- epilepsy12/migrations/0043_jersey.py | 42 ++++ .../0044_jersey_shape_file_mapping.py | 55 ++++++ epilepsy12/models_folder/__init__.py | 9 +- epilepsy12/models_folder/entities/jersey.py | 21 ++ epilepsy12/models_folder/entities/trust.py | 2 +- .../gadm41_JEY_shp/gadm41_JEY_0.cpg | 0 .../gadm41_JEY_shp/gadm41_JEY_0.dbf | Bin .../gadm41_JEY_shp/gadm41_JEY_0.prj | 0 .../gadm41_JEY_shp/gadm41_JEY_0.shp | Bin .../gadm41_JEY_shp/gadm41_JEY_0.shx | Bin .../gadm41_JEY_shp/gadm41_JEY_1.cpg | 0 .../gadm41_JEY_shp/gadm41_JEY_1.dbf | Bin .../gadm41_JEY_shp/gadm41_JEY_1.prj | 0 .../gadm41_JEY_shp/gadm41_JEY_1.shp | Bin .../gadm41_JEY_shp/gadm41_JEY_1.shx | Bin 20 files changed, 397 insertions(+), 18 deletions(-) create mode 100644 documentation/docs/development/shape-files.md create mode 100644 epilepsy12/migrations/0043_jersey.py create mode 100644 epilepsy12/migrations/0044_jersey_shape_file_mapping.py create mode 100644 epilepsy12/models_folder/entities/jersey.py rename epilepsy12/shape_files/{Countries_December_2022_UK_BUC => }/gadm41_JEY_shp/gadm41_JEY_0.cpg (100%) rename epilepsy12/shape_files/{Countries_December_2022_UK_BUC => }/gadm41_JEY_shp/gadm41_JEY_0.dbf (100%) rename epilepsy12/shape_files/{Countries_December_2022_UK_BUC => }/gadm41_JEY_shp/gadm41_JEY_0.prj (100%) rename epilepsy12/shape_files/{Countries_December_2022_UK_BUC => }/gadm41_JEY_shp/gadm41_JEY_0.shp (100%) rename epilepsy12/shape_files/{Countries_December_2022_UK_BUC => }/gadm41_JEY_shp/gadm41_JEY_0.shx (100%) rename epilepsy12/shape_files/{Countries_December_2022_UK_BUC => }/gadm41_JEY_shp/gadm41_JEY_1.cpg (100%) rename epilepsy12/shape_files/{Countries_December_2022_UK_BUC => }/gadm41_JEY_shp/gadm41_JEY_1.dbf (100%) rename epilepsy12/shape_files/{Countries_December_2022_UK_BUC => }/gadm41_JEY_shp/gadm41_JEY_1.prj (100%) rename epilepsy12/shape_files/{Countries_December_2022_UK_BUC => }/gadm41_JEY_shp/gadm41_JEY_1.shp (100%) rename epilepsy12/shape_files/{Countries_December_2022_UK_BUC => }/gadm41_JEY_shp/gadm41_JEY_1.shx (100%) diff --git a/documentation/docs/development/shape-files.md b/documentation/docs/development/shape-files.md new file mode 100644 index 000000000..8f952c85a --- /dev/null +++ b/documentation/docs/development/shape-files.md @@ -0,0 +1,186 @@ +--- +title: Level of Abstractions and their boundaries, using GIS +reviewers: Dr Simon Chapman +--- + +## Shape Files + +GeoDjango and GIS is a package that sits ontop of Django and is accompanied by an additional pack for Postgres. It has additional fields and methods for geographical data, such as longitude, latitude and conversions for mapping different coordinate systems (eg northings and eastings vs longitude and latitude). + +Shape files contain the coordinates to map boundaries of regions. Epilepsy12 has boundary files for the countries of the United Kingdom from the UK [Office of National Statistics](https://geoportal.statistics.gov.uk/). + +In November 2024 support was added for Jersey. The shape files for this were taken from [GADM](https://gadm.org/index.html), a well-known resource of geographical data. + +### Adding a new .shp file + +1. add the files to the `shape_files` directory. There are different file extensions but the key one is the `.shp` file for Epilepsy12 +2. Use `ogrinfo` on the command line to inspect the `.shp` file field structure. For example: + + `ogrinfo -al -so epilepsy12/shape_files/gadm41_JEY_shp/gadm41_JEY_0.shp` returns + + ```console + Layer name: gadm41_JEY_0 + Metadata: + DBF_DATE_LAST_UPDATE=2022-07-18 + Geometry: Polygon + Feature Count: 1 + Extent: (-2.255138, 49.147083) - (-1.924584, 49.292915) + Layer SRS WKT: + GEOGCRS["WGS 84", + DATUM["World Geodetic System 1984", + ELLIPSOID["WGS 84",6378137,298.257223563, + LENGTHUNIT["metre",1]]], + PRIMEM["Greenwich",0, + ANGLEUNIT["degree",0.0174532925199433]], + CS[ellipsoidal,2], + AXIS["latitude",north, + ORDER[1], + ANGLEUNIT["degree",0.0174532925199433]], + AXIS["longitude",east, + ORDER[2], + ANGLEUNIT["degree",0.0174532925199433]], + ID["EPSG",4326]] + Data axis to CRS axis mapping: 2,1 + GID_0: String (10.0) + COUNTRY: String (10.0) + ``` + + This is not an essential step - it just allows you to inspect the file and see the fields. + +3. Use `ogrinspect` to convert the `.shp` file to a `LayerMap` instance + `python manage.py ogrinspect epilepsy12/shape_files/gadm41_JEY_shp/gadm41_JEY_0.shp JerseyBoundary --srid=4326 --mapping --multi` + + This generates the code for the Model (change the name to the Model name you want). Note the flags - `srid` relates to the spatial reference identifier (SRID), a unique identifier associated with a specific coordinate system and resolution. The other flags create the boundary mapping object. This returns: + + ```console + # This is an auto-generated Django model module created by ogrinspect. + from django.contrib.gis.db import models + + + class JerseyBoundary(models.Model): + gid_0 = models.CharField(max_length=10) + country = models.CharField(max_length=10) + geom = models.MultiPolygonField(srid=4326) + + + # Auto-generated `LayerMapping` dictionary for JerseyBoundary model + jerseyboundary_mapping = { + 'gid_0': 'GID_0', + 'country': 'COUNTRY', + 'geom': 'MULTIPOLYGON', + } + ``` + +4. Add the new model to the `models_folder`. Generally the model with the boundaries in Epilepsy12 is an abstract model and inherited by the parent model which contains more information and the relationships: + + ```python + from django.contrib.gis.db import models + + class JerseyBoundary(models.Model): + gid_0 = models.CharField(max_length=10) + country = models.CharField(max_length=10) + geom = models.MultiPolygonField(srid=4326) + + class Meta: + abstract = True + + + class Jersey(JerseyBoundary): + class Meta: + indexes = [models.Index(fields=["gid_0"])] + verbose_name = "Jersey" + verbose_name_plural = "Jersey" + ordering = ("country",) + + def __str__(self) -> str: + return self.country + ``` + +5. Create a migration for the new models: + + `python manage.py makemigrations` + +6. Create a new custom migration for the shape mapping / data import process: + + `python manage.py makemigrations epilepsy12 --name jersey_shape_file_mapping --empty` + +7. Create a function within the migration that creates a new model, creates a new layer map and prescribes the path to the `.shp` file to import the data into the database. It would look like this: + + ```python + # Generated by Django 5.1.2 on 2024-11-23 13:16 + + # python imports + import os + + # django imports + from django.db import migrations + + # from django.apps import apps as django_apps + from django.apps import apps as django_apps + from django.contrib.gis.utils import LayerMapping + + Jersey = django_apps.get_model("epilepsy12", "Jersey") + + # Auto-generated `LayerMapping` dictionary for JerseyBoundary model + jerseyboundary_mapping = { + "gid_0": "GID_0", + "country": "COUNTRY", + "geom": "MULTIPOLYGON", + } + + # Get the path to the shape file + app_config = django_apps.get_app_config("epilepsy12") + app_path = app_config.path + jersey_shp_file_path = os.path.join( + app_path, "shape_files", "gadm41_JEY_shp", "gadm41_JEY_0.shp" + ) + + + def load_jersey_shape_file_mapping(apps, schema_editor): + """ + Load the Jersey shape file mapping into the database + """ + + # Load the Jersey shape file mapping into the database + lm = LayerMapping( + Jersey, + jersey_shp_file_path, + jerseyboundary_mapping, + transform=False, + encoding="utf-8", + ) + + lm.save(strict=True, verbose=True) + + + class Migration(migrations.Migration): + + dependencies = [ + ("epilepsy12", "0043_jersey"), + ] + + operations = [ + migrations.RunPython(load_jersey_shape_file_mapping), + ] + + ``` + +8. Run the migration + + `python manage.py migrate` + +```console +root@600d309878ba:/app# python manage.py makemigrations +Migrations for 'epilepsy12': + epilepsy12/migrations/0043_jersey.py + + Create model Jersey +root@600d309878ba:/app# python manage.py makemigrations epilepsy12 --name jersey_shape_file_mapping --empty +Migrations for 'epilepsy12': + epilepsy12/migrations/0044_jersey_shape_file_mapping.py +root@600d309878ba:/app# python manage.py migrate +Operations to perform: + Apply all migrations: admin, auth, authtoken, captcha, contenttypes, epilepsy12, otp_email, otp_static, otp_totp, phonenumber, sessions +Running migrations: + Applying epilepsy12.0043_jersey... OK + Applying epilepsy12.0044_jersey_shape_file_mapping...Saved: Jersey +``` diff --git a/epilepsy12/admin.py b/epilepsy12/admin.py index aedfbce1c..c431327e2 100644 --- a/epilepsy12/admin.py +++ b/epilepsy12/admin.py @@ -119,25 +119,35 @@ class CaseAdmin(SimpleHistoryAdmin): class OrganisationalAuditSubmissionAdmin(SimpleHistoryAdmin): - search_fields = ["trust__name", "local_health_board__name", "trust__ods_code", "local_health_board__ods_code"] + search_fields = [ + "trust__name", + "local_health_board__name", + "trust__ods_code", + "local_health_board__ods_code", + ] list_filter = ["submission_period"] + class OrganisationalAuditSubmissionPeriodAdmin(SimpleHistoryAdmin): actions = ["download"] @admin.action(description="Download submissions as CSV") def download(self, request, queryset): if queryset.count() > 1: - self.message_user(request, "Please select only one submission period to download", messages.ERROR) + self.message_user( + request, + "Please select only one submission period to download", + messages.ERROR, + ) else: submission_period = queryset.first() - + filename = f"e12-org-audit-{submission_period.year}.csv" - + data = export_submission_period_as_csv(submission_period) response = HttpResponse(data, content_type="text/csv") - response['Content-Disposition'] = f"attachment; filename={filename}" + response["Content-Disposition"] = f"attachment; filename={filename}" return response @@ -149,7 +159,13 @@ def download(self, request, queryset): admin.site.register(Comorbidity, SimpleHistoryAdmin) admin.site.register(EpilepsyContext, SimpleHistoryAdmin) admin.site.register(Investigations, SimpleHistoryAdmin) -admin.site.register(Organisation, SimpleHistoryAdmin) + + +class OrganisationAdmin(SimpleHistoryAdmin): + search_fields = ["name", "ods_code"] + + +admin.site.register(Organisation, OrganisationAdmin) admin.site.register(FirstPaediatricAssessment, SimpleHistoryAdmin) admin.site.register(Management, SimpleHistoryAdmin) admin.site.register(Registration, SimpleHistoryAdmin) @@ -178,6 +194,7 @@ def download(self, request, queryset): admin.site.register(Medicine) admin.site.register(Country) +admin.site.register(Jersey) admin.site.register(LondonBorough) admin.site.register(IntegratedCareBoard) admin.site.register(NHSEnglandRegion) @@ -186,7 +203,9 @@ def download(self, request, queryset): admin.site.register(OPENUKNetwork) admin.site.register(OrganisationalAuditSubmission, OrganisationalAuditSubmissionAdmin) -admin.site.register(OrganisationalAuditSubmissionPeriod, OrganisationalAuditSubmissionPeriodAdmin) +admin.site.register( + OrganisationalAuditSubmissionPeriod, OrganisationalAuditSubmissionPeriodAdmin +) admin.site.register(Banner) admin.site.site_header = "Epilepsy12 admin" diff --git a/epilepsy12/constants/rcpch_organisations.py b/epilepsy12/constants/rcpch_organisations.py index 6275ac926..a31c6ef3d 100644 --- a/epilepsy12/constants/rcpch_organisations.py +++ b/epilepsy12/constants/rcpch_organisations.py @@ -8477,6 +8477,31 @@ "Fax": "", "LocalAuthority": "", }, # 01/04/1994 Region: Y60 MIDLANDS COMMISSIONING REGION, ICB: QOC NHS SHROPSHIRE, TELFORD AND WREKIN INTEGRATED CARE BOARD, OPENUK: BRPNF - Birmingham Regional Paediatric Neurology Form + { + "OrganisationID": "", + "OrganisationCode": "RGT1W", + "OrganisationType": "", + "SubType": "", + "Sector": "", + "OrganisationStatus": "", + "IsPimsManaged": "", + "OrganisationName": "JERSEY GENERAL HOSPITAL", + "Address1": "THE PARADE", + "Address2": "", + "Address3": "", + "City": "ST HELIER", + "County": "JERSEY", + "Postcode": "JE1 3Q", + "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. + "ParentName": "JERSEY GENERAL HOSPITAL", + "Phone": "01534 442000", + "Email": "", + "Website": "", + "Fax": "", + "LocalAuthority": "", + }, # 01/04/2015 - Jersey General Hospital, Jersey, Channel Islands is a special case as it is both an organisation and a trust. It is member of the South West Interest Group Paediatric Epilepsy. It has no other organisational hierarchies ] """ diff --git a/epilepsy12/constants/trust.py b/epilepsy12/constants/trust.py index 4196d6280..dbc427ed4 100644 --- a/epilepsy12/constants/trust.py +++ b/epilepsy12/constants/trust.py @@ -5,6 +5,7 @@ Does not include Wales - these are in the Local Health Boards file Some ambulance trusts are here but commented out. """ + TRUSTS = [ { "ods_code": "RVV", @@ -2184,4 +2185,13 @@ "postcode": "TW11 8HU", "country": "ENGLAND", }, + { + "ods_code": "RGT1W", + "trust_name": "JERSEY GENERAL HOSPITAL", + "address_line_1": "THE PARADE", + "address_line_2": "", + "town": "ST HELIER", + "postcode": "JE1 3Q", + "country": "JERSEY", + }, # 01/04/2015 Jersey is a special case as it is both an organisation and a Trust. The ODS code is the same for both. ] diff --git a/epilepsy12/migrations/0001_initial.py b/epilepsy12/migrations/0001_initial.py index 0707d4ef6..eeac00198 100644 --- a/epilepsy12/migrations/0001_initial.py +++ b/epilepsy12/migrations/0001_initial.py @@ -1462,7 +1462,10 @@ class Migration(migrations.Migration): ), ("created_at", models.DateTimeField(auto_now_add=True)), ("updated_at", models.DateTimeField(auto_now=True)), - ("conceptId", models.CharField(blank=True, default=None, null=True, unique=True)), + ( + "conceptId", + models.CharField(blank=True, default=None, null=True, unique=True), + ), ("term", models.CharField(blank=True, default=None, null=True)), ( "preferredTerm", @@ -1519,7 +1522,10 @@ class Migration(migrations.Migration): ), ("created_at", models.DateTimeField(auto_now_add=True)), ("updated_at", models.DateTimeField(auto_now=True)), - ("conceptId", models.CharField(blank=True, default=None, null=True, unique=True)), + ( + "conceptId", + models.CharField(blank=True, default=None, null=True, unique=True), + ), ("term", models.CharField(blank=True, default=None, null=True)), ( "preferredTerm", @@ -2685,7 +2691,10 @@ class Migration(migrations.Migration): ), ("created_at", models.DateTimeField(blank=True, editable=False)), ("updated_at", models.DateTimeField(blank=True, editable=False)), - ("conceptId", models.CharField(blank=True, default=None, null=True, unique=True)), + ( + "conceptId", + models.CharField(blank=True, default=None, null=True, unique=True), + ), ("term", models.CharField(blank=True, default=None, null=True)), ( "preferredTerm", @@ -2844,7 +2853,10 @@ class Migration(migrations.Migration): ), ("created_at", models.DateTimeField(blank=True, editable=False)), ("updated_at", models.DateTimeField(blank=True, editable=False)), - ("conceptId", models.CharField(blank=True, default=None, null=True, unique=True)), + ( + "conceptId", + models.CharField(blank=True, default=None, null=True, unique=True), + ), ("term", models.CharField(blank=True, default=None, null=True)), ( "preferredTerm", @@ -3961,7 +3973,10 @@ class Migration(migrations.Migration): null=True, ), ), - ("conceptId", models.CharField(blank=True, default=None, null=True, unique=True)), + ( + "conceptId", + models.CharField(blank=True, default=None, null=True, unique=True), + ), ("term", models.CharField(blank=True, default=None, null=True)), ( "preferredTerm", @@ -5744,7 +5759,7 @@ class Migration(migrations.Migration): ), ("created_at", models.DateTimeField(auto_now_add=True)), ("updated_at", models.DateTimeField(auto_now=True)), - ("ods_code", models.CharField(max_length=3, unique=True)), + ("ods_code", models.CharField(max_length=10, unique=True)), ("name", models.CharField(max_length=100)), ( "address_line_1", @@ -6207,7 +6222,10 @@ class Migration(migrations.Migration): null=True, ), ), - ("conceptId", models.CharField(blank=True, default=None, null=True, unique=True)), + ( + "conceptId", + models.CharField(blank=True, default=None, null=True, unique=True), + ), ("term", models.CharField(blank=True, default=None, null=True)), ( "preferredTerm", diff --git a/epilepsy12/migrations/0043_jersey.py b/epilepsy12/migrations/0043_jersey.py new file mode 100644 index 000000000..2da671945 --- /dev/null +++ b/epilepsy12/migrations/0043_jersey.py @@ -0,0 +1,42 @@ +# Generated by Django 5.1.2 on 2024-11-23 13:15 + +import django.contrib.gis.db.models.fields +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("epilepsy12", "0042_set_input_achieved_true"), + ] + + operations = [ + migrations.CreateModel( + name="Jersey", + fields=[ + ( + "id", + models.BigAutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("gid_0", models.CharField(max_length=10)), + ("country", models.CharField(max_length=10)), + ( + "geom", + django.contrib.gis.db.models.fields.MultiPolygonField(srid=4326), + ), + ], + options={ + "verbose_name": "Jersey", + "verbose_name_plural": "Jersey", + "ordering": ("country",), + "indexes": [ + models.Index(fields=["gid_0"], name="epilepsy12__gid_0_7eae82_idx") + ], + }, + ), + ] diff --git a/epilepsy12/migrations/0044_jersey_shape_file_mapping.py b/epilepsy12/migrations/0044_jersey_shape_file_mapping.py new file mode 100644 index 000000000..954f9d633 --- /dev/null +++ b/epilepsy12/migrations/0044_jersey_shape_file_mapping.py @@ -0,0 +1,55 @@ +# Generated by Django 5.1.2 on 2024-11-23 13:16 + +# python imports +import os + +# django imports +from django.db import migrations + +# from django.apps import apps as django_apps +from django.apps import apps as django_apps +from django.contrib.gis.utils import LayerMapping + +Jersey = django_apps.get_model("epilepsy12", "Jersey") + +# Auto-generated `LayerMapping` dictionary for JerseyBoundary model +jerseyboundary_mapping = { + "gid_0": "GID_0", + "country": "COUNTRY", + "geom": "MULTIPOLYGON", +} + +# Get the path to the shape file +app_config = django_apps.get_app_config("epilepsy12") +app_path = app_config.path +jersey_shp_file_path = os.path.join( + app_path, "shape_files", "gadm41_JEY_shp", "gadm41_JEY_0.shp" +) + + +def load_jersey_shape_file_mapping(apps, schema_editor): + """ + Load the Jersey shape file mapping into the database + """ + + # Load the Jersey shape file mapping into the database + lm = LayerMapping( + Jersey, + jersey_shp_file_path, + jerseyboundary_mapping, + transform=False, + encoding="utf-8", + ) + + lm.save(strict=True, verbose=True) + + +class Migration(migrations.Migration): + + dependencies = [ + ("epilepsy12", "0043_jersey"), + ] + + operations = [ + migrations.RunPython(load_jersey_shape_file_mapping), + ] diff --git a/epilepsy12/models_folder/__init__.py b/epilepsy12/models_folder/__init__.py index edfda034e..cd26e2101 100644 --- a/epilepsy12/models_folder/__init__.py +++ b/epilepsy12/models_folder/__init__.py @@ -63,8 +63,8 @@ # IntegratedCareBoardEntity has a one to many relationship with NHSEnglandRegion from .entities.country import Country +from .entities.jersey import Jersey from .entities.london_borough import LondonBorough - from .entities.open_uk_network import OPENUKNetwork from .entities.integrated_care_board import IntegratedCareBoard from .entities.nhs_england_region import NHSEnglandRegion @@ -72,5 +72,8 @@ from .entities.trust import Trust from .entities.local_health_board import LocalHealthBoard -from .organisational_audit import OrganisationalAuditSubmissionPeriod, OrganisationalAuditSubmission -from .banner import Banner \ No newline at end of file +from .organisational_audit import ( + OrganisationalAuditSubmissionPeriod, + OrganisationalAuditSubmission, +) +from .banner import Banner diff --git a/epilepsy12/models_folder/entities/jersey.py b/epilepsy12/models_folder/entities/jersey.py new file mode 100644 index 000000000..01077c6dc --- /dev/null +++ b/epilepsy12/models_folder/entities/jersey.py @@ -0,0 +1,21 @@ +from django.contrib.gis.db import models + + +class JerseyBoundary(models.Model): + gid_0 = models.CharField(max_length=10) + country = models.CharField(max_length=10) + geom = models.MultiPolygonField(srid=4326) + + class Meta: + abstract = True + + +class Jersey(JerseyBoundary): + class Meta: + indexes = [models.Index(fields=["gid_0"])] + verbose_name = "Jersey" + verbose_name_plural = "Jersey" + ordering = ("country",) + + def __str__(self) -> str: + return self.country diff --git a/epilepsy12/models_folder/entities/trust.py b/epilepsy12/models_folder/entities/trust.py index e6c0620f6..0d0204ec5 100644 --- a/epilepsy12/models_folder/entities/trust.py +++ b/epilepsy12/models_folder/entities/trust.py @@ -3,7 +3,7 @@ class Trust(TimeStampAbstractBaseClass): - ods_code = models.CharField(max_length=3, unique=True) + ods_code = models.CharField(max_length=10, unique=True) name = models.CharField(max_length=100) address_line_1 = models.CharField( max_length=100, null=True, blank=True, default=None diff --git a/epilepsy12/shape_files/Countries_December_2022_UK_BUC/gadm41_JEY_shp/gadm41_JEY_0.cpg b/epilepsy12/shape_files/gadm41_JEY_shp/gadm41_JEY_0.cpg similarity index 100% rename from epilepsy12/shape_files/Countries_December_2022_UK_BUC/gadm41_JEY_shp/gadm41_JEY_0.cpg rename to epilepsy12/shape_files/gadm41_JEY_shp/gadm41_JEY_0.cpg diff --git a/epilepsy12/shape_files/Countries_December_2022_UK_BUC/gadm41_JEY_shp/gadm41_JEY_0.dbf b/epilepsy12/shape_files/gadm41_JEY_shp/gadm41_JEY_0.dbf similarity index 100% rename from epilepsy12/shape_files/Countries_December_2022_UK_BUC/gadm41_JEY_shp/gadm41_JEY_0.dbf rename to epilepsy12/shape_files/gadm41_JEY_shp/gadm41_JEY_0.dbf diff --git a/epilepsy12/shape_files/Countries_December_2022_UK_BUC/gadm41_JEY_shp/gadm41_JEY_0.prj b/epilepsy12/shape_files/gadm41_JEY_shp/gadm41_JEY_0.prj similarity index 100% rename from epilepsy12/shape_files/Countries_December_2022_UK_BUC/gadm41_JEY_shp/gadm41_JEY_0.prj rename to epilepsy12/shape_files/gadm41_JEY_shp/gadm41_JEY_0.prj diff --git a/epilepsy12/shape_files/Countries_December_2022_UK_BUC/gadm41_JEY_shp/gadm41_JEY_0.shp b/epilepsy12/shape_files/gadm41_JEY_shp/gadm41_JEY_0.shp similarity index 100% rename from epilepsy12/shape_files/Countries_December_2022_UK_BUC/gadm41_JEY_shp/gadm41_JEY_0.shp rename to epilepsy12/shape_files/gadm41_JEY_shp/gadm41_JEY_0.shp diff --git a/epilepsy12/shape_files/Countries_December_2022_UK_BUC/gadm41_JEY_shp/gadm41_JEY_0.shx b/epilepsy12/shape_files/gadm41_JEY_shp/gadm41_JEY_0.shx similarity index 100% rename from epilepsy12/shape_files/Countries_December_2022_UK_BUC/gadm41_JEY_shp/gadm41_JEY_0.shx rename to epilepsy12/shape_files/gadm41_JEY_shp/gadm41_JEY_0.shx diff --git a/epilepsy12/shape_files/Countries_December_2022_UK_BUC/gadm41_JEY_shp/gadm41_JEY_1.cpg b/epilepsy12/shape_files/gadm41_JEY_shp/gadm41_JEY_1.cpg similarity index 100% rename from epilepsy12/shape_files/Countries_December_2022_UK_BUC/gadm41_JEY_shp/gadm41_JEY_1.cpg rename to epilepsy12/shape_files/gadm41_JEY_shp/gadm41_JEY_1.cpg diff --git a/epilepsy12/shape_files/Countries_December_2022_UK_BUC/gadm41_JEY_shp/gadm41_JEY_1.dbf b/epilepsy12/shape_files/gadm41_JEY_shp/gadm41_JEY_1.dbf similarity index 100% rename from epilepsy12/shape_files/Countries_December_2022_UK_BUC/gadm41_JEY_shp/gadm41_JEY_1.dbf rename to epilepsy12/shape_files/gadm41_JEY_shp/gadm41_JEY_1.dbf diff --git a/epilepsy12/shape_files/Countries_December_2022_UK_BUC/gadm41_JEY_shp/gadm41_JEY_1.prj b/epilepsy12/shape_files/gadm41_JEY_shp/gadm41_JEY_1.prj similarity index 100% rename from epilepsy12/shape_files/Countries_December_2022_UK_BUC/gadm41_JEY_shp/gadm41_JEY_1.prj rename to epilepsy12/shape_files/gadm41_JEY_shp/gadm41_JEY_1.prj diff --git a/epilepsy12/shape_files/Countries_December_2022_UK_BUC/gadm41_JEY_shp/gadm41_JEY_1.shp b/epilepsy12/shape_files/gadm41_JEY_shp/gadm41_JEY_1.shp similarity index 100% rename from epilepsy12/shape_files/Countries_December_2022_UK_BUC/gadm41_JEY_shp/gadm41_JEY_1.shp rename to epilepsy12/shape_files/gadm41_JEY_shp/gadm41_JEY_1.shp diff --git a/epilepsy12/shape_files/Countries_December_2022_UK_BUC/gadm41_JEY_shp/gadm41_JEY_1.shx b/epilepsy12/shape_files/gadm41_JEY_shp/gadm41_JEY_1.shx similarity index 100% rename from epilepsy12/shape_files/Countries_December_2022_UK_BUC/gadm41_JEY_shp/gadm41_JEY_1.shx rename to epilepsy12/shape_files/gadm41_JEY_shp/gadm41_JEY_1.shx From 6abbd8fd85b28072430042e35b2127edba2c3f00 Mon Sep 17 00:00:00 2001 From: eatyourpeas Date: Sat, 23 Nov 2024 15:17:35 +0000 Subject: [PATCH 03/21] now loads home page without maps for jersey after multiple messy workarounds. --- .../map_from_shape_file.py | 7 ++ epilepsy12/views/organisation_views.py | 80 ++++++++++++------- 2 files changed, 57 insertions(+), 30 deletions(-) diff --git a/epilepsy12/common_view_functions/map_from_shape_file.py b/epilepsy12/common_view_functions/map_from_shape_file.py index 8725a5043..e0c830782 100644 --- a/epilepsy12/common_view_functions/map_from_shape_file.py +++ b/epilepsy12/common_view_functions/map_from_shape_file.py @@ -111,6 +111,13 @@ def generate_case_count_choropleth_map( else: identifier = None + # Jersey is a special case as it is not part of the UK and does not have an NHS England region, LHB or ICB. Country is the only level of abstraction that Jersey is part of. + if organisation.ods_code == "RGT1W": + # Jersey in the model is still part of England so we need to remap here to the Jersey Model + if abstraction_level == EnumAbstractionLevel.COUNTRY: + # TODO complete this + pass + # Highlight the region of the organisation by colouring the region boudary in a pink colour organisation_region = getattr(organisation, identifier) organisation_region_identifier = getattr(organisation_region, properties) diff --git a/epilepsy12/views/organisation_views.py b/epilepsy12/views/organisation_views.py index 88145b08a..8fbf73e1d 100644 --- a/epilepsy12/views/organisation_views.py +++ b/epilepsy12/views/organisation_views.py @@ -101,6 +101,7 @@ def selected_organisation_summary(request, organisation_id): ) ) + # differentiate between England and Wales if selected_organisation.country.boundary_identifier == "W92000004": # Wales abstraction_level = "local_health_board" # generate choropleth map of case counts for each level of abstraction @@ -110,28 +111,40 @@ def selected_organisation_summary(request, organisation_id): organisation=selected_organisation, cohort=cohort_number, ) - else: - # generate choropleth map of case counts for each level of abstraction - abstraction_level = "trust" - icb_heatmap = generate_case_count_choropleth_map( - properties="ods_code", - abstraction_level=EnumAbstractionLevel.ICB, - organisation=selected_organisation, - cohort=cohort_number, - ) - nhsregion_heatmap = generate_case_count_choropleth_map( - properties="region_code", - abstraction_level=EnumAbstractionLevel.NHS_ENGLAND_REGION, + country_heatmap = generate_case_count_choropleth_map( + properties="boundary_identifier", + abstraction_level=EnumAbstractionLevel.COUNTRY, organisation=selected_organisation, cohort=cohort_number, ) + else: + # generate choropleth map of case counts for each level of abstraction + if selected_organisation.ods_code == "RGT1W": + # Jersey is a special case and although is mapped to England, is in the Channel Islands and has no ICB, NHS Region or LHB + abstraction_level = "trust" + country_heatmap = None # TODO: add Jersey to the country_heatmap + pass + else: + abstraction_level = "trust" + icb_heatmap = generate_case_count_choropleth_map( + properties="ods_code", + abstraction_level=EnumAbstractionLevel.ICB, + organisation=selected_organisation, + cohort=cohort_number, + ) + nhsregion_heatmap = generate_case_count_choropleth_map( + properties="region_code", + abstraction_level=EnumAbstractionLevel.NHS_ENGLAND_REGION, + organisation=selected_organisation, + cohort=cohort_number, + ) - country_heatmap = generate_case_count_choropleth_map( - properties="boundary_identifier", - abstraction_level=EnumAbstractionLevel.COUNTRY, - organisation=selected_organisation, - cohort=cohort_number, - ) + country_heatmap = generate_case_count_choropleth_map( + properties="boundary_identifier", + abstraction_level=EnumAbstractionLevel.COUNTRY, + organisation=selected_organisation, + cohort=cohort_number, + ) # query to return all completed E12 cases in the current cohort in this organisation count_of_current_cohort_registered_completed_cases_in_this_organisation = ( @@ -142,22 +155,23 @@ def selected_organisation_summary(request, organisation_id): abstraction_level="organisation", ).count() ) - # query to return all completed E12 cases in the current cohort in this organisation trust - count_of_current_cohort_registered_completed_cases_in_this_trust = ( + # query to return all cases (including incomplete) registered in the current cohort at this organisation + count_of_all_current_cohort_registered_cases_in_this_organisation = ( all_registered_cases_for_cohort_and_abstraction_level( organisation_instance=selected_organisation, cohort=cohort_number, - case_complete=True, - abstraction_level=abstraction_level, + case_complete=False, + abstraction_level="organisation", ).count() ) - # query to return all cases (including incomplete) registered in the current cohort at this organisation - count_of_all_current_cohort_registered_cases_in_this_organisation = ( + + # query to return all completed E12 cases in the current cohort in this organisation trust + count_of_current_cohort_registered_completed_cases_in_this_trust = ( all_registered_cases_for_cohort_and_abstraction_level( organisation_instance=selected_organisation, cohort=cohort_number, - case_complete=False, - abstraction_level="organisation", + case_complete=True, + abstraction_level=abstraction_level, ).count() ) # query to return all cases (including incomplete) registered in the current cohort at this organisation trust @@ -248,7 +262,7 @@ def selected_organisation_summary(request, organisation_id): "individual_kpi_choices": INDIVIDUAL_KPI_MEASURES, "organisation_cases_map": scatterplot_of_cases_for_selected_organisation, "aggregated_distances": aggregated_distances, - "country_heatmap": country_heatmap, + "country_heatmap": country_heatmap if country_heatmap else None, } if selected_organisation.country.boundary_identifier == "W92000004": context["lhb_heatmap"] = lhb_heatmap @@ -256,9 +270,15 @@ def selected_organisation_summary(request, organisation_id): context["icb_heatmap"] = None context["nhsregion_heatmap"] = None else: - context["lhb_heatmap"] = None - context["icb_heatmap"] = icb_heatmap - context["nhsregion_heatmap"] = nhsregion_heatmap + if selected_organisation.ods_code == "RGT1W": + # Jersey is a special case and although is mapped to England, is in the Channel Islands and has no ICB, NHS Region or LHB + context["trust_heatmap"] = None + context["icb_heatmap"] = None + context["nhsregion_heatmap"] = None + else: + context["lhb_heatmap"] = None + context["icb_heatmap"] = icb_heatmap + context["nhsregion_heatmap"] = nhsregion_heatmap return render( request=request, From ae52abb9a4c8a51c649dbd96f46395edef1539db Mon Sep 17 00:00:00 2001 From: eatyourpeas Date: Sat, 23 Nov 2024 18:41:53 +0000 Subject: [PATCH 04/21] ok start again, redo the migrations, this time map jersey layer to country model fields, new migration to add boundaries, then another for the trust and the organisation. Note need extra migration to allow longer ods_code in Trust and empty fields in Country boundaries model --- documentation/docs/development/shape-files.md | 2 +- epilepsy12/admin.py | 1 - .../map_from_shape_file.py | 11 +++ epilepsy12/constants/rcpch_organisations.py | 51 +++++------ epilepsy12/constants/trust.py | 19 ++-- epilepsy12/migrations/0001_initial.py | 2 +- .../migrations/0005_seed_organisations.py | 2 +- ...ntry_bng_e_alter_country_bng_n_and_more.py | 48 ++++++++++ epilepsy12/migrations/0043_jersey.py | 42 --------- ...boundaries_to_country_remap_identifier.py} | 12 ++- ...rsey_general_hospital_and_relationships.py | 90 +++++++++++++++++++ epilepsy12/models_folder/__init__.py | 1 - epilepsy12/models_folder/entities/country.py | 12 +-- epilepsy12/models_folder/entities/jersey.py | 21 ----- epilepsy12/views/organisation_views.py | 3 +- 15 files changed, 201 insertions(+), 116 deletions(-) create mode 100644 epilepsy12/migrations/0043_alter_country_bng_e_alter_country_bng_n_and_more.py delete mode 100644 epilepsy12/migrations/0043_jersey.py rename epilepsy12/migrations/{0044_jersey_shape_file_mapping.py => 0044_add_jersey_boundaries_to_country_remap_identifier.py} (83%) create mode 100644 epilepsy12/migrations/0045_add_jersey_general_hospital_and_relationships.py delete mode 100644 epilepsy12/models_folder/entities/jersey.py diff --git a/documentation/docs/development/shape-files.md b/documentation/docs/development/shape-files.md index 8f952c85a..5d0e56845 100644 --- a/documentation/docs/development/shape-files.md +++ b/documentation/docs/development/shape-files.md @@ -9,7 +9,7 @@ GeoDjango and GIS is a package that sits ontop of Django and is accompanied by a Shape files contain the coordinates to map boundaries of regions. Epilepsy12 has boundary files for the countries of the United Kingdom from the UK [Office of National Statistics](https://geoportal.statistics.gov.uk/). -In November 2024 support was added for Jersey. The shape files for this were taken from [GADM](https://gadm.org/index.html), a well-known resource of geographical data. +In November 2024 support was added for Jersey. The shape files for this were taken from [GADM](https://gadm.org/index.html), a well-known resource of geographical data. This is a walk through of this as an example but note this is not how it is implemented actually. It is an example to show how to add a new model and boundary data to it. In reality, no new model was created for Jersey but instead the `.shp` file was mapped to existing fields in the `Country` model and then a new migration created to add the Organisation in Jersey with all its relationships. ### Adding a new .shp file diff --git a/epilepsy12/admin.py b/epilepsy12/admin.py index c431327e2..7a3aade26 100644 --- a/epilepsy12/admin.py +++ b/epilepsy12/admin.py @@ -194,7 +194,6 @@ class OrganisationAdmin(SimpleHistoryAdmin): admin.site.register(Medicine) admin.site.register(Country) -admin.site.register(Jersey) admin.site.register(LondonBorough) admin.site.register(IntegratedCareBoard) admin.site.register(NHSEnglandRegion) diff --git a/epilepsy12/common_view_functions/map_from_shape_file.py b/epilepsy12/common_view_functions/map_from_shape_file.py index e0c830782..fad5a4645 100644 --- a/epilepsy12/common_view_functions/map_from_shape_file.py +++ b/epilepsy12/common_view_functions/map_from_shape_file.py @@ -1,6 +1,7 @@ # python imports from datetime import date import json +import logging # django imports from django.apps import apps @@ -25,6 +26,8 @@ ) from .tiles_for_region import return_tile_for_region +logger = logging.getLogger(__name__) + def generate_case_count_choropleth_map( properties, organisation, abstraction_level, cohort @@ -35,6 +38,14 @@ def generate_case_count_choropleth_map( """ px.set_mapbox_access_token(settings.MAPBOX_API_KEY) + if organisation.ods_code == "RGT1W" and ( + abstraction_level != EnumAbstractionLevel.ORGANISATION + or abstraction_level != EnumAbstractionLevel.TRUST + ): + # Jersey is a special case as it is not part of the UK and does not have an NHS England region, LHB or ICB. + logger.warning("Jersey is only part of the country level of abstraction") + return None + region_tile = region_tile_for_abstraction_level(abstraction_level=abstraction_level) geojson_data = json.loads(region_tile) diff --git a/epilepsy12/constants/rcpch_organisations.py b/epilepsy12/constants/rcpch_organisations.py index a31c6ef3d..3f5f9e754 100644 --- a/epilepsy12/constants/rcpch_organisations.py +++ b/epilepsy12/constants/rcpch_organisations.py @@ -8477,33 +8477,34 @@ "Fax": "", "LocalAuthority": "", }, # 01/04/1994 Region: Y60 MIDLANDS COMMISSIONING REGION, ICB: QOC NHS SHROPSHIRE, TELFORD AND WREKIN INTEGRATED CARE BOARD, OPENUK: BRPNF - Birmingham Regional Paediatric Neurology Form - { - "OrganisationID": "", - "OrganisationCode": "RGT1W", - "OrganisationType": "", - "SubType": "", - "Sector": "", - "OrganisationStatus": "", - "IsPimsManaged": "", - "OrganisationName": "JERSEY GENERAL HOSPITAL", - "Address1": "THE PARADE", - "Address2": "", - "Address3": "", - "City": "ST HELIER", - "County": "JERSEY", - "Postcode": "JE1 3Q", - "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. - "ParentName": "JERSEY GENERAL HOSPITAL", - "Phone": "01534 442000", - "Email": "", - "Website": "", - "Fax": "", - "LocalAuthority": "", - }, # 01/04/2015 - Jersey General Hospital, Jersey, Channel Islands is a special case as it is both an organisation and a trust. It is member of the South West Interest Group Paediatric Epilepsy. It has no other organisational hierarchies ] +JERSEY_ORGANISATION = { + "OrganisationID": "", + "OrganisationCode": "RGT1W", + "OrganisationType": "", + "SubType": "", + "Sector": "", + "OrganisationStatus": "", + "IsPimsManaged": "", + "OrganisationName": "JERSEY GENERAL HOSPITAL", + "Address1": "THE PARADE", + "Address2": "", + "Address3": "", + "City": "ST HELIER", + "County": "JERSEY", + "Postcode": "JE1 3Q", + "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. + "ParentName": "JERSEY GENERAL HOSPITAL", + "Phone": "01534 442000", + "Email": "", + "Website": "", + "Fax": "", + "LocalAuthority": "", +} # 01/04/2015 - Jersey General Hospital, Jersey, Channel Islands is a special case as it is both an organisation and a trust. It is member of the South West Interest Group Paediatric Epilepsy. It has no other organisational hierarchies + """ Steps to update organisation in the console once created in the admin from django.contrib.gis.geos import Point diff --git a/epilepsy12/constants/trust.py b/epilepsy12/constants/trust.py index dbc427ed4..7c29636f2 100644 --- a/epilepsy12/constants/trust.py +++ b/epilepsy12/constants/trust.py @@ -2185,13 +2185,14 @@ "postcode": "TW11 8HU", "country": "ENGLAND", }, - { - "ods_code": "RGT1W", - "trust_name": "JERSEY GENERAL HOSPITAL", - "address_line_1": "THE PARADE", - "address_line_2": "", - "town": "ST HELIER", - "postcode": "JE1 3Q", - "country": "JERSEY", - }, # 01/04/2015 Jersey is a special case as it is both an organisation and a Trust. The ODS code is the same for both. ] + +JERSEY_NHS_TRUST = { + "ods_code": "RGT1W", + "trust_name": "JERSEY GENERAL HOSPITAL", + "address_line_1": "THE PARADE", + "address_line_2": "", + "town": "ST HELIER", + "postcode": "JE1 3Q", + "country": "JERSEY", +} # 01/04/2015 Jersey is a special case as it is both an organisation and a Trust. The ODS code is the same for both. diff --git a/epilepsy12/migrations/0001_initial.py b/epilepsy12/migrations/0001_initial.py index eeac00198..fe4b75b52 100644 --- a/epilepsy12/migrations/0001_initial.py +++ b/epilepsy12/migrations/0001_initial.py @@ -5759,7 +5759,7 @@ class Migration(migrations.Migration): ), ("created_at", models.DateTimeField(auto_now_add=True)), ("updated_at", models.DateTimeField(auto_now=True)), - ("ods_code", models.CharField(max_length=10, unique=True)), + ("ods_code", models.CharField(max_length=3, unique=True)), ("name", models.CharField(max_length=100)), ( "address_line_1", diff --git a/epilepsy12/migrations/0005_seed_organisations.py b/epilepsy12/migrations/0005_seed_organisations.py index 2d48d87c6..2ac2a051e 100644 --- a/epilepsy12/migrations/0005_seed_organisations.py +++ b/epilepsy12/migrations/0005_seed_organisations.py @@ -80,7 +80,7 @@ def seed_organisations(apps, schema_editor): geocode_coordinates=new_point, telephone=rcpch_organisation["Phone"], ) - # add trust or local health board + # add trust or local health board and country if ( LocalHealthBoard.objects.filter( ods_code=rcpch_organisation["ParentODSCode"] diff --git a/epilepsy12/migrations/0043_alter_country_bng_e_alter_country_bng_n_and_more.py b/epilepsy12/migrations/0043_alter_country_bng_e_alter_country_bng_n_and_more.py new file mode 100644 index 000000000..1cd696b05 --- /dev/null +++ b/epilepsy12/migrations/0043_alter_country_bng_e_alter_country_bng_n_and_more.py @@ -0,0 +1,48 @@ +# Generated by Django 5.1.2 on 2024-11-23 17:30 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("epilepsy12", "0042_set_input_achieved_true"), + ] + + operations = [ + migrations.AlterField( + model_name="country", + name="bng_e", + field=models.BigIntegerField(blank=True, null=True), + ), + migrations.AlterField( + model_name="country", + name="bng_n", + field=models.BigIntegerField(blank=True, null=True), + ), + migrations.AlterField( + model_name="country", + name="globalid", + field=models.CharField(blank=True, max_length=38, null=True), + ), + migrations.AlterField( + model_name="country", + name="lat", + field=models.FloatField(blank=True, null=True), + ), + migrations.AlterField( + model_name="country", + name="long", + field=models.FloatField(blank=True, null=True), + ), + migrations.AlterField( + model_name="country", + name="welsh_name", + field=models.CharField(blank=True, max_length=17, null=True), + ), + migrations.AlterField( + model_name="trust", + name="ods_code", + field=models.CharField(max_length=10, unique=True), + ), + ] diff --git a/epilepsy12/migrations/0043_jersey.py b/epilepsy12/migrations/0043_jersey.py deleted file mode 100644 index 2da671945..000000000 --- a/epilepsy12/migrations/0043_jersey.py +++ /dev/null @@ -1,42 +0,0 @@ -# Generated by Django 5.1.2 on 2024-11-23 13:15 - -import django.contrib.gis.db.models.fields -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ("epilepsy12", "0042_set_input_achieved_true"), - ] - - operations = [ - migrations.CreateModel( - name="Jersey", - fields=[ - ( - "id", - models.BigAutoField( - auto_created=True, - primary_key=True, - serialize=False, - verbose_name="ID", - ), - ), - ("gid_0", models.CharField(max_length=10)), - ("country", models.CharField(max_length=10)), - ( - "geom", - django.contrib.gis.db.models.fields.MultiPolygonField(srid=4326), - ), - ], - options={ - "verbose_name": "Jersey", - "verbose_name_plural": "Jersey", - "ordering": ("country",), - "indexes": [ - models.Index(fields=["gid_0"], name="epilepsy12__gid_0_7eae82_idx") - ], - }, - ), - ] diff --git a/epilepsy12/migrations/0044_jersey_shape_file_mapping.py b/epilepsy12/migrations/0044_add_jersey_boundaries_to_country_remap_identifier.py similarity index 83% rename from epilepsy12/migrations/0044_jersey_shape_file_mapping.py rename to epilepsy12/migrations/0044_add_jersey_boundaries_to_country_remap_identifier.py index 954f9d633..b18310869 100644 --- a/epilepsy12/migrations/0044_jersey_shape_file_mapping.py +++ b/epilepsy12/migrations/0044_add_jersey_boundaries_to_country_remap_identifier.py @@ -1,5 +1,3 @@ -# Generated by Django 5.1.2 on 2024-11-23 13:16 - # python imports import os @@ -10,12 +8,12 @@ from django.apps import apps as django_apps from django.contrib.gis.utils import LayerMapping -Jersey = django_apps.get_model("epilepsy12", "Jersey") +Country = django_apps.get_model("epilepsy12", "Country") # Auto-generated `LayerMapping` dictionary for JerseyBoundary model jerseyboundary_mapping = { - "gid_0": "GID_0", - "country": "COUNTRY", + "boundary_identifier": "GID_0", + "name": "COUNTRY", "geom": "MULTIPOLYGON", } @@ -34,7 +32,7 @@ def load_jersey_shape_file_mapping(apps, schema_editor): # Load the Jersey shape file mapping into the database lm = LayerMapping( - Jersey, + Country, jersey_shp_file_path, jerseyboundary_mapping, transform=False, @@ -47,7 +45,7 @@ def load_jersey_shape_file_mapping(apps, schema_editor): class Migration(migrations.Migration): dependencies = [ - ("epilepsy12", "0043_jersey"), + ("epilepsy12", "0043_alter_country_bng_e_alter_country_bng_n_and_more"), ] operations = [ diff --git a/epilepsy12/migrations/0045_add_jersey_general_hospital_and_relationships.py b/epilepsy12/migrations/0045_add_jersey_general_hospital_and_relationships.py new file mode 100644 index 000000000..b2a812bdc --- /dev/null +++ b/epilepsy12/migrations/0045_add_jersey_general_hospital_and_relationships.py @@ -0,0 +1,90 @@ +# Generated by Django 5.1.2 on 2024-11-23 17:41 + +# python imports +from datetime import date +import logging + +# Django imports +from django.db import migrations +from django.contrib.gis.geos import Point + +# Epilepsy12 imports +from epilepsy12.constants import JERSEY_ORGANISATION, JERSEY_NHS_TRUST + +logger = logging.getLogger(__name__) + + +def create_jersey_general_hospital(apps, schema_editor): + """ + Create the Jersey General Hospital + """ + Organisation = apps.get_model("epilepsy12", "Organisation") + Trust = apps.get_model("epilepsy12", "Trust") + Country = apps.get_model("epilepsy12", "Country") + OPENUKNetwork = apps.get_model("epilepsy12", "OPENUKNetwork") + + jersey = Country.objects.get(boundary_identifier="JEY") + swipe = OPENUKNetwork.objects.get(boundary_identifier="SWIPE") + + if Organisation.objects.filter( + ods_code=JERSEY_ORGANISATION["OrganisationCode"] + ).exists(): + logger.info("Jersey General Hospital already exists. Skipping creation.") + return + + # Create the Jersey General Hospital trust and assign it to Jersey, the country + jersey_trust = Trust.objects.create( + ods_code=JERSEY_NHS_TRUST["ods_code"], + name=JERSEY_NHS_TRUST["trust_name"], + address_line_1=JERSEY_NHS_TRUST["address_line_1"], + address_line_2=JERSEY_NHS_TRUST["address_line_2"], + town=JERSEY_NHS_TRUST["town"], + postcode=JERSEY_NHS_TRUST["postcode"], + country=jersey, + telephone=None, + website=None, + active=True, + published_at=date(2015, 4, 1), + ) + + # Create the Jersey General Hospital organisation and assign it to Jersey, the country and the Jersey General Hospital trust and the OPENUK Network + Organisation.objects.create( + ods_code=JERSEY_ORGANISATION["OrganisationCode"], + name=JERSEY_ORGANISATION["OrganisationName"], + website=JERSEY_ORGANISATION["Website"], + address1=JERSEY_ORGANISATION["Address1"], + address2=JERSEY_ORGANISATION["Address2"], + address3=JERSEY_ORGANISATION["Address3"], + city=JERSEY_ORGANISATION["City"], + county=JERSEY_ORGANISATION["County"], + latitude=float(JERSEY_ORGANISATION["Latitude"]), + longitude=float(JERSEY_ORGANISATION["Longitude"]), + postcode=JERSEY_ORGANISATION["Postcode"], + geocode_coordinates=Point( + x=float(JERSEY_ORGANISATION["Longitude"]), + y=float(JERSEY_ORGANISATION["Latitude"]), + ), + telephone=JERSEY_ORGANISATION["Phone"], + active=True, + published_at=date(2015, 4, 1), + country=jersey, + trust=jersey_trust, + local_health_board=None, + openuk_network=swipe, + nhs_england_region=None, + integrated_care_board=None, + london_borough=None, + ) + + logger.info("Jersey General Hospital created and all relationships added....") + + +class Migration(migrations.Migration): + + dependencies = [ + ("epilepsy12", "0044_add_jersey_boundaries_to_country_remap_identifier"), + ] + + operations = [ + migrations.RunPython(create_jersey_general_hospital), + ] diff --git a/epilepsy12/models_folder/__init__.py b/epilepsy12/models_folder/__init__.py index cd26e2101..7fbdd9b52 100644 --- a/epilepsy12/models_folder/__init__.py +++ b/epilepsy12/models_folder/__init__.py @@ -63,7 +63,6 @@ # IntegratedCareBoardEntity has a one to many relationship with NHSEnglandRegion from .entities.country import Country -from .entities.jersey import Jersey from .entities.london_borough import LondonBorough from .entities.open_uk_network import OPENUKNetwork from .entities.integrated_care_board import IntegratedCareBoard diff --git a/epilepsy12/models_folder/entities/country.py b/epilepsy12/models_folder/entities/country.py index 917b7379f..b192391e4 100644 --- a/epilepsy12/models_folder/entities/country.py +++ b/epilepsy12/models_folder/entities/country.py @@ -19,12 +19,12 @@ class CountryBoundaries(models.Model): boundary_identifier = models.CharField(max_length=9) name = models.CharField(max_length=16) - welsh_name = models.CharField(max_length=17) - bng_e = models.BigIntegerField() - bng_n = models.BigIntegerField() - long = models.FloatField() - lat = models.FloatField() - globalid = models.CharField(max_length=38) + welsh_name = models.CharField(max_length=17, null=True, blank=True) + bng_e = models.BigIntegerField(null=True, blank=True) + bng_n = models.BigIntegerField(null=True, blank=True) + long = models.FloatField(null=True, blank=True) + lat = models.FloatField(null=True, blank=True) + globalid = models.CharField(max_length=38, null=True, blank=True) geom = models.MultiPolygonField(srid=27700) class Meta: diff --git a/epilepsy12/models_folder/entities/jersey.py b/epilepsy12/models_folder/entities/jersey.py deleted file mode 100644 index 01077c6dc..000000000 --- a/epilepsy12/models_folder/entities/jersey.py +++ /dev/null @@ -1,21 +0,0 @@ -from django.contrib.gis.db import models - - -class JerseyBoundary(models.Model): - gid_0 = models.CharField(max_length=10) - country = models.CharField(max_length=10) - geom = models.MultiPolygonField(srid=4326) - - class Meta: - abstract = True - - -class Jersey(JerseyBoundary): - class Meta: - indexes = [models.Index(fields=["gid_0"])] - verbose_name = "Jersey" - verbose_name_plural = "Jersey" - ordering = ("country",) - - def __str__(self) -> str: - return self.country diff --git a/epilepsy12/views/organisation_views.py b/epilepsy12/views/organisation_views.py index 8fbf73e1d..9a84cc6cb 100644 --- a/epilepsy12/views/organisation_views.py +++ b/epilepsy12/views/organisation_views.py @@ -122,7 +122,8 @@ def selected_organisation_summary(request, organisation_id): if selected_organisation.ods_code == "RGT1W": # Jersey is a special case and although is mapped to England, is in the Channel Islands and has no ICB, NHS Region or LHB abstraction_level = "trust" - country_heatmap = None # TODO: add Jersey to the country_heatmap + + country_heatmap = None pass else: abstraction_level = "trust" From 96363ac830b89b170922c26005a554aa17baf313 Mon Sep 17 00:00:00 2001 From: eatyourpeas Date: Sat, 23 Nov 2024 19:56:17 +0000 Subject: [PATCH 05/21] now maps working. filter country map to individual country. remap srid of jersey to 27700 from 4326 file source. Replace trust addition of Jersey with name rather than country object --- epilepsy12/admin.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/epilepsy12/admin.py b/epilepsy12/admin.py index 7a3aade26..5837e5b02 100644 --- a/epilepsy12/admin.py +++ b/epilepsy12/admin.py @@ -197,7 +197,13 @@ class OrganisationAdmin(SimpleHistoryAdmin): admin.site.register(LondonBorough) admin.site.register(IntegratedCareBoard) admin.site.register(NHSEnglandRegion) -admin.site.register(Trust) + + +class TrustAdmin(SimpleHistoryAdmin): + search_fields = ["name", "ods_code"] + + +admin.site.register(Trust, TrustAdmin) admin.site.register(LocalHealthBoard) admin.site.register(OPENUKNetwork) From 1757cf377e93106a6e1c153fb05ef9d60dfbe9f4 Mon Sep 17 00:00:00 2001 From: eatyourpeas Date: Sat, 23 Nov 2024 19:56:42 +0000 Subject: [PATCH 06/21] fixed maps --- .../map_from_shape_file.py | 36 +++++++++---------- .../common_view_functions/tiles_for_region.py | 20 +++++++---- ..._boundaries_to_country_remap_identifier.py | 6 +++- ...rsey_general_hospital_and_relationships.py | 2 +- epilepsy12/views/organisation_views.py | 23 ++++-------- 5 files changed, 44 insertions(+), 43 deletions(-) diff --git a/epilepsy12/common_view_functions/map_from_shape_file.py b/epilepsy12/common_view_functions/map_from_shape_file.py index fad5a4645..339a8b86e 100644 --- a/epilepsy12/common_view_functions/map_from_shape_file.py +++ b/epilepsy12/common_view_functions/map_from_shape_file.py @@ -38,15 +38,17 @@ def generate_case_count_choropleth_map( """ px.set_mapbox_access_token(settings.MAPBOX_API_KEY) - if organisation.ods_code == "RGT1W" and ( - abstraction_level != EnumAbstractionLevel.ORGANISATION - or abstraction_level != EnumAbstractionLevel.TRUST - ): - # Jersey is a special case as it is not part of the UK and does not have an NHS England region, LHB or ICB. - logger.warning("Jersey is only part of the country level of abstraction") - return None - - region_tile = region_tile_for_abstraction_level(abstraction_level=abstraction_level) + # if organisation.ods_code == "RGT1W" and ( + # abstraction_level != EnumAbstractionLevel.ORGANISATION + # or abstraction_level != EnumAbstractionLevel.TRUST + # ): + # # Jersey is a special case as it is not part of the UK and does not have an NHS England region, LHB or ICB. + # logger.warning("Jersey is only part of the country level of abstraction") + # return None + + region_tile = region_tile_for_abstraction_level( + abstraction_level=abstraction_level, organisation=organisation + ) geojson_data = json.loads(region_tile) features = geojson_data["features"] @@ -122,16 +124,10 @@ def generate_case_count_choropleth_map( else: identifier = None - # Jersey is a special case as it is not part of the UK and does not have an NHS England region, LHB or ICB. Country is the only level of abstraction that Jersey is part of. - if organisation.ods_code == "RGT1W": - # Jersey in the model is still part of England so we need to remap here to the Jersey Model - if abstraction_level == EnumAbstractionLevel.COUNTRY: - # TODO complete this - pass - - # Highlight the region of the organisation by colouring the region boudary in a pink colour + # Highlight the region of the organisation by colouring the region boundary in a pink colour organisation_region = getattr(organisation, identifier) organisation_region_identifier = getattr(organisation_region, properties) + highlighted_region = dataframe[ dataframe["identifier"] == organisation_region_identifier ] @@ -294,7 +290,9 @@ def all_organisations_within_a_level_of_abstraction( return level_abstraction_organisations -def region_tile_for_abstraction_level(abstraction_level: EnumAbstractionLevel): +def region_tile_for_abstraction_level( + abstraction_level: EnumAbstractionLevel, organisation +): """ Returns the geojson tile for a given level of abstraction """ @@ -308,7 +306,7 @@ def region_tile_for_abstraction_level(abstraction_level: EnumAbstractionLevel): elif abstraction_level == EnumAbstractionLevel.NHS_ENGLAND_REGION: region_tile = return_tile_for_region("nhs_england_region") elif abstraction_level == EnumAbstractionLevel.COUNTRY: - region_tile = return_tile_for_region("country") + region_tile = return_tile_for_region("country", organisation) else: # pragma: no cover raise ValueError("Invalid abstraction level") diff --git a/epilepsy12/common_view_functions/tiles_for_region.py b/epilepsy12/common_view_functions/tiles_for_region.py index c2ac6dd84..81409dc65 100644 --- a/epilepsy12/common_view_functions/tiles_for_region.py +++ b/epilepsy12/common_view_functions/tiles_for_region.py @@ -12,7 +12,8 @@ def return_tile_for_region( abstraction_level: Literal[ "icb", "nhs_england_region", "london_borough", "lhb", "country" - ] + ], + organisation=None, ): """ Returns geojson data for a given region. @@ -23,18 +24,25 @@ def return_tile_for_region( LocalHealthBoard = apps.get_model("epilepsy12", "LocalHealthBoard") LondonBorough = apps.get_model("epilepsy12", "LondonBorough") - model = IntegratedCareBoard + model = IntegratedCareBoard.objects.all() if abstraction_level == "nhs_england_region": - model = NHSEnglandRegion + model = NHSEnglandRegion.objects.all() elif abstraction_level == "country": model = CountryBoundaries + if organisation: + model = CountryBoundaries.objects.filter( + boundary_identifier=organisation.country.boundary_identifier + ).all() + else: + model = CountryBoundaries.objects.all() elif abstraction_level == "lhb": - model = LocalHealthBoard + model = LocalHealthBoard.objects.all() elif abstraction_level == "london_borough": - model = LondonBorough + model = LondonBorough.objects.all() + + unedited_tile = serialize("geojson", model) - unedited_tile = serialize("geojson", model.objects.all()) geojson_dict = json.loads(unedited_tile) geojson_dict.pop("crs", None) diff --git a/epilepsy12/migrations/0044_add_jersey_boundaries_to_country_remap_identifier.py b/epilepsy12/migrations/0044_add_jersey_boundaries_to_country_remap_identifier.py index b18310869..3e8852248 100644 --- a/epilepsy12/migrations/0044_add_jersey_boundaries_to_country_remap_identifier.py +++ b/epilepsy12/migrations/0044_add_jersey_boundaries_to_country_remap_identifier.py @@ -35,9 +35,13 @@ def load_jersey_shape_file_mapping(apps, schema_editor): Country, jersey_shp_file_path, jerseyboundary_mapping, - transform=False, + transform=True, + source_srs=4326, encoding="utf-8", ) + # Note that the target srs is 27700 so that the boundaries are in the same projection as the rest of the boundaries + # in the database. By setting the SRID here of the source_srs to 4326, the LayerMapping will automatically transform + # the boundaries to the target srs of 27700. lm.save(strict=True, verbose=True) diff --git a/epilepsy12/migrations/0045_add_jersey_general_hospital_and_relationships.py b/epilepsy12/migrations/0045_add_jersey_general_hospital_and_relationships.py index b2a812bdc..6bb034703 100644 --- a/epilepsy12/migrations/0045_add_jersey_general_hospital_and_relationships.py +++ b/epilepsy12/migrations/0045_add_jersey_general_hospital_and_relationships.py @@ -40,7 +40,7 @@ def create_jersey_general_hospital(apps, schema_editor): address_line_2=JERSEY_NHS_TRUST["address_line_2"], town=JERSEY_NHS_TRUST["town"], postcode=JERSEY_NHS_TRUST["postcode"], - country=jersey, + country=jersey.name, telephone=None, website=None, active=True, diff --git a/epilepsy12/views/organisation_views.py b/epilepsy12/views/organisation_views.py index 9a84cc6cb..d75b9ee3d 100644 --- a/epilepsy12/views/organisation_views.py +++ b/epilepsy12/views/organisation_views.py @@ -111,20 +111,11 @@ def selected_organisation_summary(request, organisation_id): organisation=selected_organisation, cohort=cohort_number, ) - country_heatmap = generate_case_count_choropleth_map( - properties="boundary_identifier", - abstraction_level=EnumAbstractionLevel.COUNTRY, - organisation=selected_organisation, - cohort=cohort_number, - ) else: # generate choropleth map of case counts for each level of abstraction if selected_organisation.ods_code == "RGT1W": # Jersey is a special case and although is mapped to England, is in the Channel Islands and has no ICB, NHS Region or LHB abstraction_level = "trust" - - country_heatmap = None - pass else: abstraction_level = "trust" icb_heatmap = generate_case_count_choropleth_map( @@ -140,12 +131,12 @@ def selected_organisation_summary(request, organisation_id): cohort=cohort_number, ) - country_heatmap = generate_case_count_choropleth_map( - properties="boundary_identifier", - abstraction_level=EnumAbstractionLevel.COUNTRY, - organisation=selected_organisation, - cohort=cohort_number, - ) + country_heatmap = generate_case_count_choropleth_map( + properties="boundary_identifier", + abstraction_level=EnumAbstractionLevel.COUNTRY, + organisation=selected_organisation, + cohort=cohort_number, + ) # query to return all completed E12 cases in the current cohort in this organisation count_of_current_cohort_registered_completed_cases_in_this_organisation = ( @@ -263,7 +254,7 @@ def selected_organisation_summary(request, organisation_id): "individual_kpi_choices": INDIVIDUAL_KPI_MEASURES, "organisation_cases_map": scatterplot_of_cases_for_selected_organisation, "aggregated_distances": aggregated_distances, - "country_heatmap": country_heatmap if country_heatmap else None, + "country_heatmap": country_heatmap, } if selected_organisation.country.boundary_identifier == "W92000004": context["lhb_heatmap"] = lhb_heatmap From d25a6761fadc816b4d3a2f5c869ed35136647fb4 Mon Sep 17 00:00:00 2001 From: eatyourpeas Date: Sat, 23 Nov 2024 20:11:04 +0000 Subject: [PATCH 07/21] rejig templates to remove levels of abstraction for Jersey --- .../partials/selected_organisation_summary.html | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/templates/epilepsy12/partials/selected_organisation_summary.html b/templates/epilepsy12/partials/selected_organisation_summary.html index 09c5315dc..9bf87f7eb 100644 --- a/templates/epilepsy12/partials/selected_organisation_summary.html +++ b/templates/epilepsy12/partials/selected_organisation_summary.html @@ -344,6 +344,9 @@

{{selected_organisation}}
+ {% if selected_organisation.country.name != "Jersey" %} + +
@@ -361,17 +364,17 @@

- {% else %} + {% elif selected_organisation.local_health_board %}

Local Health Board

{% endif %}
- {% if selected_organisation.trust %} + {% if selected_organisation.trust %}
- {% else %} + {% elif selected_organisation.local_health_board %}
Local Health Board

+ {% if selected_organisation.trust %}
@@ -408,6 +412,8 @@

{% endif %} + {% endif %} +

From 346ba23dcb7e60a36374976cad24d42eec1dd252 Mon Sep 17 00:00:00 2001 From: eatyourpeas Date: Sat, 23 Nov 2024 20:36:03 +0000 Subject: [PATCH 08/21] fix scatter plot of cases to exclude those without postcode or geographical data --- epilepsy12/common_view_functions/filter_cases_for_geography.py | 1 + 1 file changed, 1 insertion(+) diff --git a/epilepsy12/common_view_functions/filter_cases_for_geography.py b/epilepsy12/common_view_functions/filter_cases_for_geography.py index 1ab54fb77..24db4b676 100644 --- a/epilepsy12/common_view_functions/filter_cases_for_geography.py +++ b/epilepsy12/common_view_functions/filter_cases_for_geography.py @@ -18,6 +18,7 @@ def filter_all_registered_cases_by_active_lead_site_and_cohort_and_level_of_abst filtered_cases = ( Case.objects.filter( ~Q(postcode__in=UNKNOWN_POSTCODES_NO_SPACES), + Q(~Q(location_wgs=None) | ~Q(location_wgs84=None) | ~Q(location_bng=None)), registration__isnull=False, registration__cohort=cohort, site__organisation=organisation, From 3f2f877707fe24524662dc8350f36ff64bef1db6 Mon Sep 17 00:00:00 2001 From: eatyourpeas Date: Sat, 23 Nov 2024 22:07:05 +0000 Subject: [PATCH 09/21] add search field for trusts in admin, add URN to Case model, check for organisation_id in CaseForm for validation of NHS number vs URN --- epilepsy12/admin.py | 8 ++- epilepsy12/forms_folder/case_form.py | 74 ++++++++++++++++++++++------ epilepsy12/models_folder/case.py | 15 +++++- epilepsy12/views/case_views.py | 2 +- 4 files changed, 82 insertions(+), 17 deletions(-) diff --git a/epilepsy12/admin.py b/epilepsy12/admin.py index 5837e5b02..26176f579 100644 --- a/epilepsy12/admin.py +++ b/epilepsy12/admin.py @@ -115,7 +115,13 @@ def get_form(self, request, obj=None, **kwargs): class CaseAdmin(SimpleHistoryAdmin): - search_fields = ["first_name", "surname", "nhs_number", "date_of_birth"] + search_fields = [ + "first_name", + "surname", + "nhs_number", + "unique_reference_number", + "date_of_birth", + ] class OrganisationalAuditSubmissionAdmin(SimpleHistoryAdmin): diff --git a/epilepsy12/forms_folder/case_form.py b/epilepsy12/forms_folder/case_form.py index d53d53f51..3b8f98207 100644 --- a/epilepsy12/forms_folder/case_form.py +++ b/epilepsy12/forms_folder/case_form.py @@ -1,20 +1,18 @@ from datetime import date -from random import randint +from random import randint from django import forms from django.conf import settings from django.forms import ValidationError import nhs_number -from ..models import Case +from ..models import Case, Organisation from ..constants import * from ..general_functions import is_valid_postcode, return_random_postcode class CaseForm(forms.ModelForm): - - - + unknown_postcode = forms.CharField(required=False) first_name = forms.CharField( @@ -56,7 +54,14 @@ class CaseForm(forms.ModelForm): "data-mask": "000 000 0000", } ), - required=True, + required=False, + ) + unique_reference_number = forms.CharField( + help_text="Enter the Unique Reference Number (URN).", + widget=forms.TextInput( + attrs={"class": "form-control", "placeholder": "URN", "type": "text"} + ), + required=False, ) postcode = forms.CharField( @@ -79,19 +84,25 @@ class CaseForm(forms.ModelForm): locked_by = forms.CharField(help_text="User who locked the record", required=False) def __init__(self, *args, **kwargs) -> None: - + super(CaseForm, self).__init__(*args, **kwargs) self.fields["ethnicity"].widget.attrs.update({"class": "ui rcpch dropdown"}) - + self.organisation_id = kwargs.pop( + "organisation_id", None + ) # This is the organisation_id self.existing_nhs_number = self.instance.nhs_number - + # Check if DEBUG is True and set the initial value conditionally if settings.DEBUG: - self.fields['first_name'].initial = 'Bob' - self.fields['surname'].initial = 'Dylan' - self.fields['date_of_birth'].initial = date(randint(2005, 2021), randint(1, 12), randint(1, 28)) - self.fields['postcode'].initial = return_random_postcode(country_boundary_identifier='E01000001') - self.fields['nhs_number'].initial = nhs_number.generate()[0] + self.fields["first_name"].initial = "Bob" + self.fields["surname"].initial = "Dylan" + self.fields["date_of_birth"].initial = date( + randint(2005, 2021), randint(1, 12), randint(1, 28) + ) + self.fields["postcode"].initial = return_random_postcode( + country_boundary_identifier="E01000001" + ) + self.fields["nhs_number"].initial = nhs_number.generate()[0] class Meta: model = Case @@ -146,3 +157,38 @@ def clean_nhs_number(self): return formatted_nhs_number else: raise ValidationError(f"{formatted_nhs_number} is not a valid NHS number") + + def clean(self): + cleaned_data = super().clean() + nhs_number = cleaned_data.get("nhs_number") + unique_reference_number = cleaned_data.get("unique_reference_number") + organisation = Organisation.objects.get(id=self.organisation_id) + if organisation.ods_code == "RGT1W": + # this is Jersey - NHS numbers are not used + if nhs_number: + raise ValidationError("NHS Numbers are not used in Jersey") + if unique_reference_number is None: + raise ValidationError("URN is a mandatory field.") + if self.instance.unique_reference_number: + # there is an existing URN + if unique_reference_number != self.instance.unique_reference_number: + # the new URN does not match the one stored + if Case.objects.filter( + unique_reference_number=unique_reference_number + ).exists(): + raise ValidationError("Unique Reference Number already taken!") + else: + # this is a new form - check this number is unique in the database + if Case.objects.filter( + unique_reference_number=self.instance.unique_reference_number + ).exists(): + raise ValidationError("Unique Reference Number already taken!") + else: + # this is England or Wales - NHS numbers are used + # validation of NHS number is done in the clean_nhs_number method + if unique_reference_number: + raise ValidationError("URN is not used in England or Wales") + if nhs_number is None: + raise ValidationError("NHS Number is a mandatory field.") + + return cleaned_data diff --git a/epilepsy12/models_folder/case.py b/epilepsy12/models_folder/case.py index 5cb143e60..90603674c 100644 --- a/epilepsy12/models_folder/case.py +++ b/epilepsy12/models_folder/case.py @@ -64,7 +64,20 @@ class Case(TimeStampAbstractBaseClass, UserStampAbstractBaseClass, HelpTextMixin blank=True, ) nhs_number = models.CharField( # the NHS number for England and Wales - "NHS Number", unique=True, blank=True, null=True, max_length=10 + "NHS Number", + unique=True, + blank=True, + null=True, + max_length=10, + help_text="This is the NHS number for England and Wales. It is used to identify the patient in the audit.", + ) + unique_reference_number = models.CharField( + "Unique Reference Number (URN)", + unique=True, + blank=True, + null=True, + max_length=20, + help_text="This is a unique reference number for Jersey patients. It is used to identify the patient in the audit.", ) first_name = CharField( "First name", diff --git a/epilepsy12/views/case_views.py b/epilepsy12/views/case_views.py index cf386a9ca..6c5986ef7 100644 --- a/epilepsy12/views/case_views.py +++ b/epilepsy12/views/case_views.py @@ -597,7 +597,7 @@ def create_case(request, organisation_id): country_choice, ("ZZ993VZ", "No fixed abode"), ) - form = CaseForm(request.POST or None) + form = CaseForm(request.POST or None, organisation_id=organisation_id) template_name = "epilepsy12/cases/case.html" From 357890cc424ee1a3ad34be2caf7f966747fd6915 Mon Sep 17 00:00:00 2001 From: eatyourpeas Date: Sat, 23 Nov 2024 23:16:54 +0000 Subject: [PATCH 10/21] create patient form, add urn to model, update form template - still not working --- epilepsy12/forms_folder/case_form.py | 26 +++++--- ...6_case_unique_reference_number_and_more.py | 61 +++++++++++++++++++ epilepsy12/views/case_views.py | 13 +++- templates/epilepsy12/forms/case_form.html | 21 +++++-- 4 files changed, 104 insertions(+), 17 deletions(-) create mode 100644 epilepsy12/migrations/0046_case_unique_reference_number_and_more.py diff --git a/epilepsy12/forms_folder/case_form.py b/epilepsy12/forms_folder/case_form.py index 3b8f98207..db92c82a4 100644 --- a/epilepsy12/forms_folder/case_form.py +++ b/epilepsy12/forms_folder/case_form.py @@ -84,13 +84,17 @@ class CaseForm(forms.ModelForm): locked_by = forms.CharField(help_text="User who locked the record", required=False) def __init__(self, *args, **kwargs) -> None: - - super(CaseForm, self).__init__(*args, **kwargs) - self.fields["ethnicity"].widget.attrs.update({"class": "ui rcpch dropdown"}) self.organisation_id = kwargs.pop( "organisation_id", None ) # This is the organisation_id + super(CaseForm, self).__init__(*args, **kwargs) self.existing_nhs_number = self.instance.nhs_number + self.fields["ethnicity"].widget.attrs.update({"class": "ui rcpch dropdown"}) + if Organisation.objects.get(id=self.organisation_id).ods_code == "RGT1W": + # this is Jersey - hide the NHS number field + self.fields["nhs_number"].widget = forms.HiddenInput() + self.fields["nhs_number"].required = False + self.fields["unique_reference_number"].required = True # Check if DEBUG is True and set the initial value conditionally if settings.DEBUG: @@ -102,7 +106,11 @@ def __init__(self, *args, **kwargs) -> None: self.fields["postcode"].initial = return_random_postcode( country_boundary_identifier="E01000001" ) - self.fields["nhs_number"].initial = nhs_number.generate()[0] + if Organisation.objects.get(id=self.organisation_id).ods_code == "RGT1W": + # this is Jersey + self.fields["unique_reference_number"].initial = randint(1000, 9999) + else: + self.fields["nhs_number"].initial = nhs_number.generate()[0] class Meta: model = Case @@ -135,6 +143,11 @@ def clean_date_of_birth(self): def clean_nhs_number(self): # remove spaces + organisation = Organisation.objects.get(id=self.organisation_id) + if organisation.ods_code == "RGT1W": + nhs_number = None + return nhs_number + formatted_nhs_number = ( str(self.cleaned_data["nhs_number"]).replace(" ", "").zfill(10) ) @@ -164,9 +177,6 @@ def clean(self): unique_reference_number = cleaned_data.get("unique_reference_number") organisation = Organisation.objects.get(id=self.organisation_id) if organisation.ods_code == "RGT1W": - # this is Jersey - NHS numbers are not used - if nhs_number: - raise ValidationError("NHS Numbers are not used in Jersey") if unique_reference_number is None: raise ValidationError("URN is a mandatory field.") if self.instance.unique_reference_number: @@ -186,8 +196,6 @@ def clean(self): else: # this is England or Wales - NHS numbers are used # validation of NHS number is done in the clean_nhs_number method - if unique_reference_number: - raise ValidationError("URN is not used in England or Wales") if nhs_number is None: raise ValidationError("NHS Number is a mandatory field.") diff --git a/epilepsy12/migrations/0046_case_unique_reference_number_and_more.py b/epilepsy12/migrations/0046_case_unique_reference_number_and_more.py new file mode 100644 index 000000000..26addf1cc --- /dev/null +++ b/epilepsy12/migrations/0046_case_unique_reference_number_and_more.py @@ -0,0 +1,61 @@ +# Generated by Django 5.1.2 on 2024-11-23 22:26 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("epilepsy12", "0045_add_jersey_general_hospital_and_relationships"), + ] + + operations = [ + migrations.AddField( + model_name="case", + name="unique_reference_number", + field=models.CharField( + blank=True, + help_text="This is a unique reference number for Jersey patients. It is used to identify the patient in the audit.", + max_length=20, + null=True, + unique=True, + verbose_name="Unique Reference Number (URN)", + ), + ), + migrations.AddField( + model_name="historicalcase", + name="unique_reference_number", + field=models.CharField( + blank=True, + db_index=True, + help_text="This is a unique reference number for Jersey patients. It is used to identify the patient in the audit.", + max_length=20, + null=True, + verbose_name="Unique Reference Number (URN)", + ), + ), + migrations.AlterField( + model_name="case", + name="nhs_number", + field=models.CharField( + blank=True, + help_text="This is the NHS number for England and Wales. It is used to identify the patient in the audit.", + max_length=10, + null=True, + unique=True, + verbose_name="NHS Number", + ), + ), + migrations.AlterField( + model_name="historicalcase", + name="nhs_number", + field=models.CharField( + blank=True, + db_index=True, + help_text="This is the NHS number for England and Wales. It is used to identify the patient in the audit.", + max_length=10, + null=True, + verbose_name="NHS Number", + ), + ), + ] diff --git a/epilepsy12/views/case_views.py b/epilepsy12/views/case_views.py index 6c5986ef7..90d980455 100644 --- a/epilepsy12/views/case_views.py +++ b/epilepsy12/views/case_views.py @@ -618,10 +618,19 @@ def create_case(request, organisation_id): messages.success(request, "You successfully created the case") return redirect("cases", organisation_id=organisation_id) else: + error_messages = [] + for field, errors in form.errors.items(): + for error in errors: + if field == "__all__": + error_messages.append(f"General error: {error}") + else: + error_messages.append(f"{field}: {error}") + error_messages = ", ".join(error_messages) messages.error( - request=request, message="It was not possible to save the case" + request=request, + message=f"It was not possible to save the case: {error_messages}", ) - logger.info(f"Invalid data provided to case form: {form.errors}") + # logger.info(f"Invalid data provided to case form: ".join(error_messages)) context = { "organisation_id": organisation_id, diff --git a/templates/epilepsy12/forms/case_form.html b/templates/epilepsy12/forms/case_form.html index d1ab13789..43b9cd6c9 100644 --- a/templates/epilepsy12/forms/case_form.html +++ b/templates/epilepsy12/forms/case_form.html @@ -65,12 +65,21 @@

-
- - {{form.nhs_number}} {% if form.nhs_number.errors %} -
{{form.nhs_number.errors}}
- {% endif %} -
+ {% if organisation.country.boundary_identifier == "JEY" %} +
+ + {{form.unique_reference_number}} {% if form.unique_reference_number.errors %} +
{{form.unique_reference_number.errors}}
+ {% endif %} +
+ {% else %} +
+ + {{form.nhs_number}} {% if form.nhs_number.errors %} +
{{form.nhs_number.errors}}
+ {% endif %} +
+ {% endif %}
From 298d7221d695f34637c6298c33896533890a0eec Mon Sep 17 00:00:00 2001 From: eatyourpeas Date: Sun, 24 Nov 2024 09:54:17 +0000 Subject: [PATCH 11/21] filter by URN, fix E12CaseFactory for Jersey, heading in case table --- epilepsy12/forms_folder/case_form.py | 24 +++++++++++++++--- epilepsy12/management/commands/seed.py | 3 +++ epilepsy12/tests/factories/E12CaseFactory.py | 17 +++++++++++++ epilepsy12/views/case_views.py | 25 +++++++++++++++---- templates/epilepsy12/partials/case_table.html | 4 +-- 5 files changed, 63 insertions(+), 10 deletions(-) diff --git a/epilepsy12/forms_folder/case_form.py b/epilepsy12/forms_folder/case_form.py index db92c82a4..62b56c452 100644 --- a/epilepsy12/forms_folder/case_form.py +++ b/epilepsy12/forms_folder/case_form.py @@ -95,6 +95,11 @@ def __init__(self, *args, **kwargs) -> None: self.fields["nhs_number"].widget = forms.HiddenInput() self.fields["nhs_number"].required = False self.fields["unique_reference_number"].required = True + else: + # this is England or Wales - hide the URN field + self.fields["unique_reference_number"].widget = forms.HiddenInput() + self.fields["unique_reference_number"].required = False + self.fields["nhs_number"].required = True # Check if DEBUG is True and set the initial value conditionally if settings.DEBUG: @@ -106,11 +111,24 @@ def __init__(self, *args, **kwargs) -> None: self.fields["postcode"].initial = return_random_postcode( country_boundary_identifier="E01000001" ) - if Organisation.objects.get(id=self.organisation_id).ods_code == "RGT1W": + is_jersey = ( + Organisation.objects.get( + id=self.organisation_id + ).country.boundary_identifier + == "JEY" + ) + if self.instance.nhs_number is None and is_jersey: # this is Jersey - self.fields["unique_reference_number"].initial = randint(1000, 9999) + if self.instance.unique_reference_number is None: + # this is a new form + self.fields["unique_reference_number"].initial = randint(1000, 9999) + self.fields["nhs_number"].initial = None else: - self.fields["nhs_number"].initial = nhs_number.generate()[0] + # this is England or Wales + self.fields["unique_reference_number"].initial = None + if self.instance.nhs_number is None: + # this is a new form + self.fields["nhs_number"].initial = nhs_number.generate()[0] class Meta: model = Case diff --git a/epilepsy12/management/commands/seed.py b/epilepsy12/management/commands/seed.py index db3581660..73ce67b22 100644 --- a/epilepsy12/management/commands/seed.py +++ b/epilepsy12/management/commands/seed.py @@ -181,6 +181,8 @@ def run_dummy_cases_seed(cases, organisations, noskip, verbose=True): ) index_of_multiple_deprivation_quintile = randint(1, 5) + is_jersey = org.country.boundary_identifier == "JEY" + E12CaseFactory.create_batch( num_cases_to_seed_in_org, locked=False, @@ -194,6 +196,7 @@ def run_dummy_cases_seed(cases, organisations, noskip, verbose=True): "seed_male": seed_male, "seed_female": seed_female, }, + is_jersey=is_jersey, ) diff --git a/epilepsy12/tests/factories/E12CaseFactory.py b/epilepsy12/tests/factories/E12CaseFactory.py index c7cf8a690..266d137a4 100644 --- a/epilepsy12/tests/factories/E12CaseFactory.py +++ b/epilepsy12/tests/factories/E12CaseFactory.py @@ -68,6 +68,23 @@ class Params: registration=None, ) + is_jersey = factory.Trait( + nhs_number=None, + unique_reference_number=factory.Sequence(lambda n: f"JERSEY-{n}"), + ) + + @factory.lazy_attribute + def unique_reference_number(self): + """Returns a unique reference number which has not been used in the db yet.""" + not_found_unique_ref_num = True + + while not_found_unique_ref_num: + candidate_num = f"JEY-{nhs_number.generate()[0]}" + + if not Case.objects.filter(unique_reference_number=candidate_num).exists(): + not_found_unique_ref_num = False + return candidate_num + @factory.lazy_attribute def nhs_number(self): """Returns a unique NHS number which has not been used in the db yet.""" diff --git a/epilepsy12/views/case_views.py b/epilepsy12/views/case_views.py index 90d980455..95b2942cf 100644 --- a/epilepsy12/views/case_views.py +++ b/epilepsy12/views/case_views.py @@ -85,6 +85,7 @@ def case_list(request, organisation_id): Q(first_name__icontains=filter_term) | Q(surname__icontains=filter_term) | Q(nhs_number__icontains=filter_term) + | Q(unique_reference_number__icontains=filter_term) | Q(id__icontains=filter_term) ) ) @@ -111,6 +112,7 @@ def case_list(request, organisation_id): Q(first_name__icontains=filter_term) | Q(surname__icontains=filter_term) | Q(nhs_number__icontains=filter_term) + | Q(unique_reference_number__icontains=filter_term) | Q(id__icontains=filter_term) ) ) @@ -127,6 +129,7 @@ def case_list(request, organisation_id): Q(first_name__icontains=filter_term) | Q(surname__icontains=filter_term) | Q(nhs_number__icontains=filter_term) + | Q(unique_reference_number__icontains=filter_term) | Q(id__icontains=filter_term) ) ) @@ -141,6 +144,8 @@ def case_list(request, organisation_id): Only RCPCH audit staff have this final option. """ + jersey_flag = organisation.country.boundary_identifier == "JEY" + if request.user.view_preference == 2: # this is an RCPCH audit team member requesting National level filtered_cases = Case.objects.all() @@ -174,12 +179,16 @@ def case_list(request, organisation_id): or request.GET.get("sort_flag") == "sort_by_nhs_number_up" ): all_cases = filtered_cases.order_by("nhs_number").all() + if jersey_flag: + all_cases = filtered_cases.order_by("unique_reference_number").all() sort_flag = "sort_by_nhs_number_up" elif ( request.htmx.trigger_name == "sort_by_nhs_number_down" or request.GET.get("sort_flag") == "sort_by_nhs_number_down" ): all_cases = filtered_cases.order_by("-nhs_number").all() + if jersey_flag: + all_cases = filtered_cases.order_by("-unique_reference_number").all() sort_flag = "sort_by_nhs_number_down" elif ( request.htmx.trigger_name == "sort_by_ethnicity_up" @@ -650,13 +659,15 @@ def update_case(request, organisation_id, case_id): """ Django function based view. Receives POST request to update view or delete """ - case = get_object_or_404(Case, pk=case_id) - form = CaseForm(instance=case) - organisation = Organisation.objects.filter(pk=organisation_id).get() + case = get_object_or_404(Case, pk=case_id) + form = CaseForm(instance=case, organisation_id=organisation_id) # set select boxes for situations when postcode unknown - country_choice = ("ZZ993CZ", "Address unspecified - England") + country_choice = ( + "ZZ993CZ", + "Address unspecified - England", + ) # TODO we are using the same value for England and Jersey. is that correct? if organisation.country.boundary_identifier == "W92000004": country_choice = ("ZZ993GZ", "Address unspecified - Wales") @@ -679,7 +690,7 @@ def update_case(request, organisation_id, case_id): return HttpResponseClientRedirect(redirect_to=url, status=200) if request.method == "POST": - form = CaseForm(request.POST, instance=case) + form = CaseForm(request.POST, instance=case, organisation_id=organisation_id) if form.is_valid(): obj = form.save() if case.locked != obj.locked: @@ -767,6 +778,10 @@ def opt_out(request, organisation_id, case_id): f"All data on {case} has been permanently removed from Epilepsy12. The Epilepsy12 unique identifier has been preserved to contribute to annual totals.", ) case.nhs_number = None + case.unique_reference_number = None + case.location_wgs84 = None + case.location_bng = None + case.location_wgs = None case.first_name = None case.surname = None case.sex = None diff --git a/templates/epilepsy12/partials/case_table.html b/templates/epilepsy12/partials/case_table.html index 577293c8d..ffa820d02 100644 --- a/templates/epilepsy12/partials/case_table.html +++ b/templates/epilepsy12/partials/case_table.html @@ -42,7 +42,7 @@
-

NHS Number

+ {% if organisation.country.boundary_identifier == 'JEY' %}

Unique Reference Number (URN)

{% else %}NHS Number{% endif %} {% if sort_flag and sort_flag == "sort_by_nhs_number_down" %} @@ -128,7 +128,7 @@ {% none_masked case.first_name %} {% none_masked case.surname %} {% none_masked case.get_sex_display %} - {% none_masked case.nhs_number %} + {% if organisation.country.boundary_identifier == 'JEY' %}{{ case.unique_reference_number }}{% else %}{% none_masked case.nhs_number %}{% endif %} {% none_masked case.registration.audit_submission_date|date:'D j M Y' %} {% if case.registration.cohort is not None %}{{ case.registration.cohort }}{% endif %} {% if case.registration.days_remaining_before_submission is not None %}{{ case.registration.days_remaining_before_submission }}{% endif %} From 65c83090a8d26c473c5d28e932c9f5a8f45b8256 Mon Sep 17 00:00:00 2001 From: eatyourpeas Date: Sun, 24 Nov 2024 10:35:12 +0000 Subject: [PATCH 12/21] fix seed functions for jersey postcodes and URNs. Fix holes in the CaseForm where --- epilepsy12/forms_folder/case_form.py | 41 ++++++++--- epilepsy12/general_functions/postcode.py | 71 +++++++++++++++++++- epilepsy12/management/commands/seed.py | 8 ++- epilepsy12/tests/factories/E12CaseFactory.py | 6 +- epilepsy12/views/case_views.py | 5 +- 5 files changed, 113 insertions(+), 18 deletions(-) diff --git a/epilepsy12/forms_folder/case_form.py b/epilepsy12/forms_folder/case_form.py index 62b56c452..ec5019d2d 100644 --- a/epilepsy12/forms_folder/case_form.py +++ b/epilepsy12/forms_folder/case_form.py @@ -4,7 +4,7 @@ from django import forms from django.conf import settings from django.forms import ValidationError -import nhs_number +import nhs_number as nhs_number_package from ..models import Case, Organisation from ..constants import * @@ -87,19 +87,31 @@ def __init__(self, *args, **kwargs) -> None: self.organisation_id = kwargs.pop( "organisation_id", None ) # This is the organisation_id + + # set a flag to check if this is Jersey + is_jersey = ( + Organisation.objects.get( + id=self.organisation_id + ).country.boundary_identifier + == "JEY" + ) super(CaseForm, self).__init__(*args, **kwargs) self.existing_nhs_number = self.instance.nhs_number self.fields["ethnicity"].widget.attrs.update({"class": "ui rcpch dropdown"}) - if Organisation.objects.get(id=self.organisation_id).ods_code == "RGT1W": + if is_jersey: # this is Jersey - hide the NHS number field self.fields["nhs_number"].widget = forms.HiddenInput() self.fields["nhs_number"].required = False self.fields["unique_reference_number"].required = True + self.fields["unique_reference_number"].initial = ( + self.instance.unique_reference_number + ) else: # this is England or Wales - hide the URN field self.fields["unique_reference_number"].widget = forms.HiddenInput() self.fields["unique_reference_number"].required = False self.fields["nhs_number"].required = True + self.fields["nhs_number"].initial = self.instance.nhs_number # Check if DEBUG is True and set the initial value conditionally if settings.DEBUG: @@ -108,27 +120,38 @@ def __init__(self, *args, **kwargs) -> None: self.fields["date_of_birth"].initial = date( randint(2005, 2021), randint(1, 12), randint(1, 28) ) - self.fields["postcode"].initial = return_random_postcode( - country_boundary_identifier="E01000001" - ) is_jersey = ( Organisation.objects.get( id=self.organisation_id ).country.boundary_identifier == "JEY" ) + # set a random postcode if DEBUG is True: E01000001 is the boundary identifier for England but if is_jersey is True + # then a Jersey postcode will be returned instead of an English/Welsh postcode + self.fields["postcode"].initial = return_random_postcode( + country_boundary_identifier=Organisation.objects.get( + id=self.organisation_id + ).country.boundary_identifier, + is_jersey=is_jersey, + ) if self.instance.nhs_number is None and is_jersey: # this is Jersey if self.instance.unique_reference_number is None: - # this is a new form - self.fields["unique_reference_number"].initial = randint(1000, 9999) + # this is a new form - create a new URN that is unique + while True: + urn = f"JEY-{nhs_number_package.generate()[0]}" + if not Case.objects.filter( + unique_reference_number=urn + ).exists(): + break + self.fields["unique_reference_number"].initial = urn self.fields["nhs_number"].initial = None else: # this is England or Wales self.fields["unique_reference_number"].initial = None if self.instance.nhs_number is None: # this is a new form - self.fields["nhs_number"].initial = nhs_number.generate()[0] + self.fields["nhs_number"].initial = nhs_number_package.generate()[0] class Meta: model = Case @@ -183,7 +206,7 @@ def clean_nhs_number(self): raise ValidationError("NHS Number already taken!") # check NHS number is valid - validity = nhs_number.is_valid(formatted_nhs_number) + validity = nhs_number_package.is_valid(formatted_nhs_number) if validity: return formatted_nhs_number else: diff --git a/epilepsy12/general_functions/postcode.py b/epilepsy12/general_functions/postcode.py index 163bd3ccd..9a8b599c2 100644 --- a/epilepsy12/general_functions/postcode.py +++ b/epilepsy12/general_functions/postcode.py @@ -1,5 +1,6 @@ import requests import logging +from random import randint from django.conf import settings from ..constants import UNKNOWN_POSTCODES_NO_SPACES @@ -62,8 +63,71 @@ def coordinates_for_postcode(postcode: str) -> bool: return None -def return_random_postcode(country_boundary_identifier: str): - """Returns random postcode (str) inside country_boundary_identifier or `None` if invalid.""" +def return_random_postcode( + country_boundary_identifier: str, is_jersey: bool = False +) -> str: + """ + Returns random postcode (str) inside country_boundary_identifier or `None` if invalid. + + Also accepts a boolean `is_jersey` to determine if the country is Jersey. In these circumstances the postcode + will be randomly chosen from a predefined list of Jersey postcodes since the API does not support Jersey postcodes. + """ + JERSEY_POSTCODES = [ + "JE1 1AA", + "JE1 1AB", + "JE1 2BA", + "JE2 3CD", + "JE2 4EF", + "JE3 5GH", + "JE3 5IJ", + "JE3 6KL", + "JE3 7MN", + "JE4 8OP", + "JE4 8QR", + "JE4 9ST", + "JE1 3UV", + "JE2 1WX", + "JE2 2YZ", + "JE3 3AA", + "JE3 4BB", + "JE3 5CC", + "JE3 6DD", + "JE4 7EE", + "JE4 8FF", + "JE1 1GG", + "JE1 2HH", + "JE1 3JJ", + "JE2 4KK", + "JE2 5LL", + "JE2 6MM", + "JE3 7NN", + "JE3 8OO", + "JE4 9PP", + "JE1 4QQ", + "JE1 5RR", + "JE1 6SS", + "JE2 7TT", + "JE2 8UU", + "JE3 9VV", + "JE4 1WW", + "JE4 2XX", + "JE4 3YY", + "JE4 4ZZ", + "JE1 7AA", + "JE1 8BB", + "JE2 9CC", + "JE2 1DD", + "JE3 2EE", + "JE3 3FF", + "JE3 4GG", + "JE4 5HH", + "JE4 6JJ", + "JE4 7KK", + ] + + if is_jersey: + return JERSEY_POSTCODES[randint(0, len(JERSEY_POSTCODES) - 1)] + url = f"{settings.POSTCODE_API_BASE_URL}/areas/{country_boundary_identifier}" response = requests.get(url=url) @@ -75,3 +139,6 @@ def return_random_postcode(country_boundary_identifier: str): return response.json()["data"]["relationships"]["example_postcodes"]["data"][0][ "id" ].replace(" ", "") + + +0 diff --git a/epilepsy12/management/commands/seed.py b/epilepsy12/management/commands/seed.py index 73ce67b22..1458f6b72 100644 --- a/epilepsy12/management/commands/seed.py +++ b/epilepsy12/management/commands/seed.py @@ -168,6 +168,9 @@ def run_dummy_cases_seed(cases, organisations, noskip, verbose=True): num_cases_to_seed_in_org = int(cases / len(organisations)) print(f"Creating {num_cases_to_seed_in_org} Cases in {org}") + # Check if the organisation is in Jersey - this is important for generating postcodes and URNs rather than NHS Numbers + is_jersey = org.country.boundary_identifier == "JEY" + # Create random attributes random_date = date(randint(2005, 2021), randint(1, 12), randint(1, 28)) date_of_birth = random_date @@ -177,12 +180,11 @@ def run_dummy_cases_seed(cases, organisations, noskip, verbose=True): random_ethnicity = randint(0, len(choice(ETHNICITIES))) ethnicity = ETHNICITIES[random_ethnicity][0] postcode = return_random_postcode( - country_boundary_identifier=org.country.boundary_identifier + country_boundary_identifier=org.country.boundary_identifier, + is_jersey=is_jersey, ) index_of_multiple_deprivation_quintile = randint(1, 5) - is_jersey = org.country.boundary_identifier == "JEY" - E12CaseFactory.create_batch( num_cases_to_seed_in_org, locked=False, diff --git a/epilepsy12/tests/factories/E12CaseFactory.py b/epilepsy12/tests/factories/E12CaseFactory.py index 266d137a4..592f0422f 100644 --- a/epilepsy12/tests/factories/E12CaseFactory.py +++ b/epilepsy12/tests/factories/E12CaseFactory.py @@ -11,7 +11,7 @@ from .E12SiteFactory import E12SiteFactory from .E12RegistrationFactory import E12RegistrationFactory from epilepsy12.constants import SEX_TYPE, DEPRIVATION_QUINTILES -import nhs_number +import nhs_number as nhs_number_package class E12CaseFactory(factory.django.DjangoModelFactory): @@ -79,7 +79,7 @@ def unique_reference_number(self): not_found_unique_ref_num = True while not_found_unique_ref_num: - candidate_num = f"JEY-{nhs_number.generate()[0]}" + candidate_num = f"JEY-{nhs_number_package.generate()[0]}" if not Case.objects.filter(unique_reference_number=candidate_num).exists(): not_found_unique_ref_num = False @@ -91,7 +91,7 @@ def nhs_number(self): not_found_unique_nhs_num = True while not_found_unique_nhs_num: - candidate_num = nhs_number.generate()[0] + candidate_num = nhs_number_package.generate()[0] if not Case.objects.filter(nhs_number=candidate_num).exists(): not_found_unique_nhs_num = False diff --git a/epilepsy12/views/case_views.py b/epilepsy12/views/case_views.py index 95b2942cf..d2d519869 100644 --- a/epilepsy12/views/case_views.py +++ b/epilepsy12/views/case_views.py @@ -624,7 +624,10 @@ def create_case(request, organisation_id): organisation=organisation, case=obj, ) - messages.success(request, "You successfully created the case") + messages.success( + request, + f"You successfully add {obj} to Epilepsy12. They can now be registered in to the current cohort.", + ) return redirect("cases", organisation_id=organisation_id) else: error_messages = [] From 6eb444959549be81dae4b7a75f7ba47f3477e9dc Mon Sep 17 00:00:00 2001 From: eatyourpeas Date: Sun, 24 Nov 2024 10:42:27 +0000 Subject: [PATCH 13/21] typo and test commit --- epilepsy12/general_functions/postcode.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/epilepsy12/general_functions/postcode.py b/epilepsy12/general_functions/postcode.py index 9a8b599c2..46d302f51 100644 --- a/epilepsy12/general_functions/postcode.py +++ b/epilepsy12/general_functions/postcode.py @@ -37,7 +37,7 @@ def is_valid_postcode(postcode: str) -> bool: def coordinates_for_postcode(postcode: str) -> bool: """ - Returns longitude and latitude for a valide postcode. + Returns longitude and latitude for a valid postcode. """ # convert to upper case and remove spaces From 211e0f9f1013d0bfdde0cf8369eb50f4f818125a Mon Sep 17 00:00:00 2001 From: eatyourpeas Date: Sun, 24 Nov 2024 12:09:50 +0000 Subject: [PATCH 14/21] validate jersey postcodes, prevent geocode lookup for jersey since, well, the only way to do this is to pay for it as there are no open source datasets. Fix form to actually save the new URN (d'oh) --- epilepsy12/forms_folder/case_form.py | 23 +++++++------------ epilepsy12/general_functions/postcode.py | 23 +++++++++++++++++-- .../commands/old_pt_data_scripts.py | 3 ++- epilepsy12/models_folder/case.py | 7 ++++++ epilepsy12/views/case_views.py | 3 +-- 5 files changed, 39 insertions(+), 20 deletions(-) diff --git a/epilepsy12/forms_folder/case_form.py b/epilepsy12/forms_folder/case_form.py index ec5019d2d..97b7c128c 100644 --- a/epilepsy12/forms_folder/case_form.py +++ b/epilepsy12/forms_folder/case_form.py @@ -89,7 +89,7 @@ def __init__(self, *args, **kwargs) -> None: ) # This is the organisation_id # set a flag to check if this is Jersey - is_jersey = ( + self.is_jersey = ( Organisation.objects.get( id=self.organisation_id ).country.boundary_identifier @@ -98,7 +98,7 @@ def __init__(self, *args, **kwargs) -> None: super(CaseForm, self).__init__(*args, **kwargs) self.existing_nhs_number = self.instance.nhs_number self.fields["ethnicity"].widget.attrs.update({"class": "ui rcpch dropdown"}) - if is_jersey: + if self.is_jersey: # this is Jersey - hide the NHS number field self.fields["nhs_number"].widget = forms.HiddenInput() self.fields["nhs_number"].required = False @@ -120,21 +120,15 @@ def __init__(self, *args, **kwargs) -> None: self.fields["date_of_birth"].initial = date( randint(2005, 2021), randint(1, 12), randint(1, 28) ) - is_jersey = ( - Organisation.objects.get( - id=self.organisation_id - ).country.boundary_identifier - == "JEY" - ) # set a random postcode if DEBUG is True: E01000001 is the boundary identifier for England but if is_jersey is True # then a Jersey postcode will be returned instead of an English/Welsh postcode self.fields["postcode"].initial = return_random_postcode( country_boundary_identifier=Organisation.objects.get( id=self.organisation_id ).country.boundary_identifier, - is_jersey=is_jersey, + is_jersey=self.is_jersey, ) - if self.instance.nhs_number is None and is_jersey: + if self.is_jersey: # this is Jersey if self.instance.unique_reference_number is None: # this is a new form - create a new URN that is unique @@ -161,6 +155,7 @@ class Meta: "date_of_birth", "sex", "nhs_number", + "unique_reference_number", "postcode", "ethnicity", "unknown_postcode", @@ -168,8 +163,7 @@ class Meta: def clean_postcode(self): postcode = self.cleaned_data["postcode"] - - if is_valid_postcode(postcode=postcode): + if is_valid_postcode(postcode=postcode, is_jersey=self.is_jersey): return postcode raise ValidationError("Invalid postcode") @@ -216,8 +210,7 @@ def clean(self): cleaned_data = super().clean() nhs_number = cleaned_data.get("nhs_number") unique_reference_number = cleaned_data.get("unique_reference_number") - organisation = Organisation.objects.get(id=self.organisation_id) - if organisation.ods_code == "RGT1W": + if self.is_jersey: if unique_reference_number is None: raise ValidationError("URN is a mandatory field.") if self.instance.unique_reference_number: @@ -231,7 +224,7 @@ def clean(self): else: # this is a new form - check this number is unique in the database if Case.objects.filter( - unique_reference_number=self.instance.unique_reference_number + unique_reference_number=unique_reference_number ).exists(): raise ValidationError("Unique Reference Number already taken!") else: diff --git a/epilepsy12/general_functions/postcode.py b/epilepsy12/general_functions/postcode.py index 46d302f51..bf8c2d2be 100644 --- a/epilepsy12/general_functions/postcode.py +++ b/epilepsy12/general_functions/postcode.py @@ -1,6 +1,7 @@ import requests import logging from random import randint +import re from django.conf import settings from ..constants import UNKNOWN_POSTCODES_NO_SPACES @@ -9,9 +10,11 @@ logger = logging.getLogger(__name__) -def is_valid_postcode(postcode: str) -> bool: +def is_valid_postcode(postcode: str, is_jersey=False) -> bool: """ - Returns True if postcode valid. + Returns True if postcode valid. False otherwise. + If is_jersey is True, the postcode is validated against the Jersey postcode format - this is less + rigorous than the API validation but is necessary since the API does not support Jersey postcodes. """ # convert to upper case and remove spaces @@ -20,6 +23,9 @@ def is_valid_postcode(postcode: str) -> bool: if formatted in UNKNOWN_POSTCODES_NO_SPACES: return True + if is_jersey: + return validate_jersey_postcode(value=postcode) + # check against API url = f"{settings.POSTCODE_API_BASE_URL}/postcodes/{postcode}" @@ -35,6 +41,19 @@ def is_valid_postcode(postcode: str) -> bool: return False +def validate_jersey_postcode(value): + """ + Validates if the given value matches the Jersey postcode format (JE# #AA or JE###AA without spaces). + """ + value = value.upper().replace(" ", "") # Convert to uppercase and remove all spaces + pattern = ( + r"^JE\d{1,2}\d[ABD-HJLNP-UW-Z]{2}$" # Regex for Jersey postcodes without spaces + ) + if not re.match(pattern, value): + return False + return True + + def coordinates_for_postcode(postcode: str) -> bool: """ Returns longitude and latitude for a valid postcode. diff --git a/epilepsy12/management/commands/old_pt_data_scripts.py b/epilepsy12/management/commands/old_pt_data_scripts.py index d2b36cdf3..343bec911 100644 --- a/epilepsy12/management/commands/old_pt_data_scripts.py +++ b/epilepsy12/management/commands/old_pt_data_scripts.py @@ -1,6 +1,7 @@ """ These scripts clean old patient data csv and convert into records which can be seeded into E12 db. """ + import pprint import nhs_number @@ -188,7 +189,7 @@ def insert_old_pt_data(csv_path: str = "data.csv"): } continue - if not is_valid_postcode(record["postcode"]): + if not is_valid_postcode(record["postcode"], is_jersey=False): reason = "Invalid postcode" print( f'Record: {record["nhs_number"]} - { reason } - Skipping insertion...' diff --git a/epilepsy12/models_folder/case.py b/epilepsy12/models_folder/case.py index 90603674c..09a9c4cf0 100644 --- a/epilepsy12/models_folder/case.py +++ b/epilepsy12/models_folder/case.py @@ -211,6 +211,12 @@ def save(self, *args, **kwargs) -> None: Both are included here and stored in the model, as the shape files for the UK health boundaries are produced as BNG, rather than WGS84. """ try: + # If the postcode begins with JE, it is a Jersey postcode. Skip the coordinates lookup. + if self.postcode.lower().startswith("je"): + self.location_wgs84 = None + self.location_bng = None + return super().save(*args, **kwargs) + # Fetch the coordinates (WGS 84) lon, lat = coordinates_for_postcode(postcode=self.postcode) @@ -231,6 +237,7 @@ def save(self, *args, **kwargs) -> None: logger.exception( f"Cannot get longitude and latitude for {self.postcode}: {error}" ) + pass else: # if the IMD quintile has previously been added and postcode now unknown, set # index_of_multiple_deprivation_quintile back to None diff --git a/epilepsy12/views/case_views.py b/epilepsy12/views/case_views.py index d2d519869..2d6581b0d 100644 --- a/epilepsy12/views/case_views.py +++ b/epilepsy12/views/case_views.py @@ -634,7 +634,7 @@ def create_case(request, organisation_id): for field, errors in form.errors.items(): for error in errors: if field == "__all__": - error_messages.append(f"General error: {error}") + error_messages.append(f"{error}") else: error_messages.append(f"{field}: {error}") error_messages = ", ".join(error_messages) @@ -642,7 +642,6 @@ def create_case(request, organisation_id): request=request, message=f"It was not possible to save the case: {error_messages}", ) - # logger.info(f"Invalid data provided to case form: ".join(error_messages)) context = { "organisation_id": organisation_id, From 3fb2de2aa0a0f48b118dc4c4bf932a2686b15b1d Mon Sep 17 00:00:00 2001 From: eatyourpeas Date: Sun, 24 Nov 2024 14:50:45 +0000 Subject: [PATCH 15/21] documentation updates and fixes esp postcodes, imd and mapping --- documentation/docs/admin-users/download.md | 2 +- .../clinician-users/entering-patient-data.md | 2 +- .../docs/development/docker-setup.md | 7 +++---- .../docs/development/getting-started.md | 2 +- documentation/docs/development/imd.md | 18 +++++++++++++++++ .../docs/development/organisations.md | 20 ++++++++++++++++++- documentation/docs/development/postcodes.md | 14 +++++++++++++ documentation/docs/development/user-groups.md | 2 +- .../docs/legal/privacy-impact-assessment.md | 2 +- documentation/docs/legal/privacy-notice.md | 4 ++-- documentation/mkdocs.yml | 4 ++++ 11 files changed, 65 insertions(+), 12 deletions(-) create mode 100644 documentation/docs/development/imd.md create mode 100644 documentation/docs/development/postcodes.md diff --git a/documentation/docs/admin-users/download.md b/documentation/docs/admin-users/download.md index 0f0ef74f9..388f834bd 100644 --- a/documentation/docs/admin-users/download.md +++ b/documentation/docs/admin-users/download.md @@ -7,4 +7,4 @@ author: Dr Marcus Baw You can download the entire Epilepsy12 documentation manual as a PDF for offline reading. Click the button below to download. -[:fontawesome-solid-file-pdf: Download documentation manual in PDF format](../../pdf/rcpch-epilepsy12-full-documentation.pdf){ .md-button .md-button--primary } \ No newline at end of file +[:fontawesome-solid-file-pdf: Download documentation manual in PDF format](../pdf/rcpch-epilepsy12-full-documentation.pdf){ .md-button .md-button--primary } \ No newline at end of file diff --git a/documentation/docs/clinician-users/entering-patient-data.md b/documentation/docs/clinician-users/entering-patient-data.md index 7fe54178d..4137c0801 100644 --- a/documentation/docs/clinician-users/entering-patient-data.md +++ b/documentation/docs/clinician-users/entering-patient-data.md @@ -26,7 +26,7 @@ The NHS number is checked against the NHS number [checksum](https://www.datadict ### Transferring a patient between centres -Only the [**Lead Clinician**](../user-group-permissions/#lead-clinician) has permissions to transfer children to another centre. +Only the [**Lead Clinician**](user-group-permissions.md#lead-clinician) has permissions to transfer children to another centre. The steps to do this for the lead clinician in Organisation A diff --git a/documentation/docs/development/docker-setup.md b/documentation/docs/development/docker-setup.md index 7d642ae2a..350cff90d 100644 --- a/documentation/docs/development/docker-setup.md +++ b/documentation/docs/development/docker-setup.md @@ -26,7 +26,7 @@ cd rcpch-audit-engine ``` !!! warning "Windows Setup" - **If you are on Windows**, after installing Docker and cloning the repository, please now skip to the [(Windows) Setup for development using Docker Compose](./docker-setup.md#windows-setup-for-development-using-docker-compose) section. + **If you are on Windows**, after installing Docker and cloning the repository, please now skip to the [(Windows) Setup for development using Docker Compose](./docker-setup-windows.md) section. ### Ensure you are on the default `development` branch @@ -48,7 +48,6 @@ cp envs/env-template envs/.env !!! warning "Mac Users" If using Mac and Safari, to access the Epilepsy 12 engine in your development, you must change the `SITE_DOMAIN` name in .env to 'localhost', and type this into your browser once you have executed `s/up` in the next step. This will load the E12 engine in your Safari browser. - However, for simplicity, we recommend using a different browser, such as Chrome, and leaving the .env file unaltered. ### Start the development environment for the first time using our startup script @@ -107,7 +106,6 @@ This Docker setup is quite new so please do open an issue if there is anything t !!! warning "Terminal is now occupied" If you have successfully run the Docker Compose deployment, your terminal will be showing the combined and colour-coded logging output for all the containers and will no longer show an interactive prompt, which is means you can not run any more commands in that terminal. To resolve this, simply **open another Terminal window** in the same working directory, in which you can run commands. - If opening another terminal is impractical or impossible, then in most Shell environments you can press `Ctrl`+`Z` to suspend the current process, and then `bg` to resume it in the background. This will return you to an interactive prompt. Once you've executed your further commands, you can then use `fg` to bring the console logging output back to the foreground again. ### Creating a superuser @@ -117,6 +115,7 @@ You can use our convenience script to create a superuser in the context of the ` ```console s/create-superuser ``` + The script will prompt you for required user attributes: ```console @@ -208,7 +207,7 @@ For testing of the UI it is often useful to have some dummy data in the database s/seed ``` -See the [Seeding the Database](../manual-setup/#seeding-the-database) section for more details on the usage of this script, for example setting a non-default Cohort Number. +See the [Seeding the Database](manual-setup.md#seeding-the-database) section for more details on the usage of this script, for example setting a non-default Cohort Number. ## Tips and Tricks, Gotchas and Caveats diff --git a/documentation/docs/development/getting-started.md b/documentation/docs/development/getting-started.md index 33cc61cee..7dbe63b39 100644 --- a/documentation/docs/development/getting-started.md +++ b/documentation/docs/development/getting-started.md @@ -39,4 +39,4 @@ Security is a very important aspect of managing projects such as these, and we u ## Legal -Details of Information Governance, Clinical Safety and Medical Device registration can be viewed in the [Legal](../legal/legal.md) section. +Details of Information Governance, Clinical Safety and Medical Device registration can be viewed in the [Legal](../legal/intellectual-property.md) section. diff --git a/documentation/docs/development/imd.md b/documentation/docs/development/imd.md new file mode 100644 index 000000000..43bbf6789 --- /dev/null +++ b/documentation/docs/development/imd.md @@ -0,0 +1,18 @@ +--- +title: Indices of Multiple Deprivation +reviewers: Dr Simon Chapman +--- + +## Calculation + +This is discussed more extensively in the RCPCH Census Platform which returns index of multiple deprivation quantiles against a UK postcode across the devolved nations. Currently Jersey is not supported. + +### Methodology + +Index of multiple deprivation is dependent on geography, and the underlying assumptions inherent in the index are underpinned with responses in the national censuses. The index is a composite of multiple domains from employment to housing to access to green space. Different countries even within the UK have different domains (though they are similar) and this means that findings in one country cannot be compared with another. There is a discussion of this in more detail in the [repository](https://github.com/rcpch/rcpch-census-platform) + +The country is first broken into areas of similar population size, known as low layer super output areas (LSOAs) and the findings for each measure summarized at this level. The LSOAs are then ranked in order by raw score, with the lower raw scores representing the least deprived. These are then broken into quantiles, depending on the size of the population / reporting priorities. It is typical to report as deciles or quintiles. + +The last English data was published in 2019, with Wales the same year. + +Jersey has recently been added to Epilepsy12 and is currently not supported by the RCPCH Census Platform. Because the population is small (~100,000), IMD is reported in vingtaines, rather than quintiles or deciles. The actual data is not published and is being requested for inclusion. In Epilepsy12 currently IMD quantiles are therefore not reported. diff --git a/documentation/docs/development/organisations.md b/documentation/docs/development/organisations.md index 4f0fe029a..8b577faeb 100644 --- a/documentation/docs/development/organisations.md +++ b/documentation/docs/development/organisations.md @@ -9,7 +9,7 @@ The organisational structure of health care in England and Wales influences repo ### Organisations and Trusts -This is the lowest level of abstraction and represents either an acute or a community hospital/organisation responsible for epilepsy care of children and young people. There are often several organisations in a Trust. Each organisation, like each Trust, has its own ODS code, and from year to year there is movement between trusts as organisations change their allegiances between trusts when mergers are carried out. The organisation model therefore has more than once instance for some organisations, as their parent status or other details such as name change. +This is the lowest level of abstraction and represents either an acute or a community hospital/organisation responsible for epilepsy care of children and young people. There are often several organisations in a Trust. Each organisation, like each Trust, has its own ODS code, and from year to year there is movement between trusts as organisations change their allegiances between trusts when mergers are carried out. The organisation model therefore has more than once instance for some organisations, as their parent status or other details such as name change. On a monthly basis the NHS ODS API is polled with any changes to organisational structure, and the local database record for that organisation is updated to reflect the latest changes. @@ -32,3 +32,21 @@ There are 7 of these in England and their model is taken from NHS Digital. Each ### Local Authorities Local authority codes for each organisation are not stored except for those organisations in London. Local authorities are administrative regions not related to health or the NHS. In London local authorities are usually referred to as London Boroughs. There is a boundary model for London Boroughs taken from NHS Digital and this is used only for mapping. As above, although versioned, there is no process in place to check for updates nationally and update the database record. + +### Jersey and the Channel Islands + +Jersey joined Epilepsy12 in 2024. Jersey is in the Channel Islands and part of the UK but does not participate in the NHS. There are reciprocal agreements about some hospital treatment, but inpatient care is free only to people who have been resident for 6 months and have a Health Card. + +***Organisational structure*** +The E12 structure is that organisations have a Trust or Local Health Board as their parent. There is a hierarchy above this which varies between England and Wales. Organisations and Trusts in England might have the same name, but will always have separate ODS codes, which are often similar. Jersey General Hospital in St Helier is a Trust which provides medical care directly. To work around this, Jersey General Hospital has been created both as an Organisation and a Trust, each with the same ODS code, so that it is its own parent. + +***Levels of Abstraction*** +Jersey has been added as a separate country, so that it can report at the level of organisation, Open UK Network, trust and country, though the numbers for these 3 hierarchies will be the same. + +## Maps + +Django GIS and the additional Postgres support for geoJSON are both reasons why these tools were used for this project. The Organisation View presents a dashboard that includes a scatterplot of patients specific to that organisation, with mean, median, minimum and maximum distances for patients to travel to clinic. There are also maps with boundaries demarcating health geographies such as NHS England regions and Integrated Care Boards. These are provided by `.shp` files which contain the coordinates for plotting the shapes on top of the maps. There is documentation on adding shape files [here](shape-files.md) + +The basic maps are provided by [MapBox](https://www.mapbox.com/) using their free tier, with the API key stored in credentials. An API key is required. + +E12 currently looks up postcodes against an API which returns longitude and latitude, and these are stored in the model (using SRID 27700) and this is used to plot them on the scatter plots. This currently does not function for Jersey as longitude and latitude for post codes are currently not stored. diff --git a/documentation/docs/development/postcodes.md b/documentation/docs/development/postcodes.md new file mode 100644 index 000000000..dceb19250 --- /dev/null +++ b/documentation/docs/development/postcodes.md @@ -0,0 +1,14 @@ +--- +title: Postcodes +reviewers: Dr Simon Chapman +--- + +This should be read in conjunction with sections on mapping and index of multiple deprivation. + +Postcodes for patients are stored in RCPCH-Audit-Engine securely as part of the national agreement for the RCPCH Epilepsy12 clinical audit. Opt out can be requested by patients and all data with the exception of the platform unique identifier (not the NHS number or Unique Reference Number) which is retained to generate an accurate denominator. + +Postcodes are used primarily to calculate indices of multiple deprivation, but are also used to provide a scatterplot for clinicians of patients in a given organisation to report maximum/minimum/mean and median distances patients have to travel for care. + +Postcodes are passed to an RCPCH hosted service which has its own instance of the open source project maintained at [postcodes.io](https://postcodes.io). This reports information against postcode which include LSOA (see Indices of Multiple Deprivation) as well as longitude and latitude. These latter data points are used for scatter plots. + +Jersey is currently not supported as there is no open source solution for mapping currently though this is tracked in a [github issue](https://github.com/rcpch/rcpch-audit-engine/issues/1107) \ No newline at end of file diff --git a/documentation/docs/development/user-groups.md b/documentation/docs/development/user-groups.md index 2cb8310fa..9fdc31b6a 100644 --- a/documentation/docs/development/user-groups.md +++ b/documentation/docs/development/user-groups.md @@ -3,7 +3,7 @@ title: User groups and permissions reviewers: Dr Simon Chapman --- -The user groups are summarised [here](../clinician-users/clinician-user-guide.md###Permission) +The user groups are summarised [here](../clinician-users/clinician-user-guide.md) Django allows permission-based and group based access. The user groups defined above are containers for permissions to all the models. Generic django permissions allow prescription of view, change, create and delete to each model (found in ```epilepsy12/constants/user_types.py```). diff --git a/documentation/docs/legal/privacy-impact-assessment.md b/documentation/docs/legal/privacy-impact-assessment.md index 210d9cc58..b2a3e8773 100644 --- a/documentation/docs/legal/privacy-impact-assessment.md +++ b/documentation/docs/legal/privacy-impact-assessment.md @@ -1,7 +1,7 @@ --- title: Data Protection Impact Assessment reviewers: Dr Marcus Baw -dpia_path: ../../reference-documents/2023-12-PIA-2018-02-epilepsy12-pia-v2-1_Final.pdf +dpia_path: ../reference-documents/2023-12-PIA-2018-02-epilepsy12-pia-v2-1_Final.pdf hide: - toc --- diff --git a/documentation/docs/legal/privacy-notice.md b/documentation/docs/legal/privacy-notice.md index f456bcfae..93a96c4f1 100644 --- a/documentation/docs/legal/privacy-notice.md +++ b/documentation/docs/legal/privacy-notice.md @@ -1,8 +1,8 @@ --- title: Privacy Notice reviewers: Dr Marcus Baw -privacy_notice_path: ../../reference-documents/20230118 E12 Privacy Notice.pdf -privacy_notice_welsh_path: ../../reference-documents/20230118 E12 Privacy Notice Welsh.pdf +privacy_notice_path: ../reference-documents/20230118 E12 Privacy Notice.pdf +privacy_notice_welsh_path: ../reference-documents/20230118 E12 Privacy Notice Welsh.pdf hide: - toc --- diff --git a/documentation/mkdocs.yml b/documentation/mkdocs.yml index dc37f3f2a..8dd46cefc 100644 --- a/documentation/mkdocs.yml +++ b/documentation/mkdocs.yml @@ -146,6 +146,7 @@ nav: - 'admin-users/admin-user-guide.md' - 'admin-users/download.md' - 'admin-users/how-to-edit-docs.md' + - 'admin-users/how-to-add-banners.md' - Developer Guide: - Getting Started: 'development/getting-started.md' - Architecture Overview: 'development/architecture-overview.md' @@ -173,9 +174,12 @@ nav: - Audit Forms: 'development/audit-forms.md' - Form Scoring: 'development/form-scoring.md' - Date Validations: 'development/date-validations.md' + - Indices of Multiple Deprivation: 'development/imd.md' - KPIs: 'development/key-performance-indicators.md' - Organisations, Trusts and Regions: 'development/organisations.md' + - Postcodes: 'development/postcodes.md' - Reporting: 'development/reporting.md' + - Shape Files and Maps: 'development/shape-files.md' - User Groups: 'development/user-groups.md' - Users: 'development/user-workflow.md' - Views: 'development/views.md' From 5b7e0223d41c00302b35e210d51c80ca8d7db5d0 Mon Sep 17 00:00:00 2001 From: eatyourpeas Date: Tue, 3 Dec 2024 21:44:01 +0000 Subject: [PATCH 16/21] accepting all suggestions --- documentation/docs/development/postcodes.md | 2 +- .../map_from_shape_file.py | 8 ---- epilepsy12/forms_folder/case_form.py | 46 ++++--------------- epilepsy12/management/commands/seed.py | 1 + epilepsy12/models_folder/case.py | 1 + 5 files changed, 11 insertions(+), 47 deletions(-) diff --git a/documentation/docs/development/postcodes.md b/documentation/docs/development/postcodes.md index dceb19250..e8392e163 100644 --- a/documentation/docs/development/postcodes.md +++ b/documentation/docs/development/postcodes.md @@ -9,6 +9,6 @@ Postcodes for patients are stored in RCPCH-Audit-Engine securely as part of the Postcodes are used primarily to calculate indices of multiple deprivation, but are also used to provide a scatterplot for clinicians of patients in a given organisation to report maximum/minimum/mean and median distances patients have to travel for care. -Postcodes are passed to an RCPCH hosted service which has its own instance of the open source project maintained at [postcodes.io](https://postcodes.io). This reports information against postcode which include LSOA (see Indices of Multiple Deprivation) as well as longitude and latitude. These latter data points are used for scatter plots. +Postcodes are passed to findthatpostcode.uk. This reports information against postcode which include LSOA (see Indices of Multiple Deprivation) as well as longitude and latitude. These latter data points are used for scatter plots. Jersey is currently not supported as there is no open source solution for mapping currently though this is tracked in a [github issue](https://github.com/rcpch/rcpch-audit-engine/issues/1107) \ No newline at end of file diff --git a/epilepsy12/common_view_functions/map_from_shape_file.py b/epilepsy12/common_view_functions/map_from_shape_file.py index 339a8b86e..ea5e26735 100644 --- a/epilepsy12/common_view_functions/map_from_shape_file.py +++ b/epilepsy12/common_view_functions/map_from_shape_file.py @@ -38,14 +38,6 @@ def generate_case_count_choropleth_map( """ px.set_mapbox_access_token(settings.MAPBOX_API_KEY) - # if organisation.ods_code == "RGT1W" and ( - # abstraction_level != EnumAbstractionLevel.ORGANISATION - # or abstraction_level != EnumAbstractionLevel.TRUST - # ): - # # Jersey is a special case as it is not part of the UK and does not have an NHS England region, LHB or ICB. - # logger.warning("Jersey is only part of the country level of abstraction") - # return None - region_tile = region_tile_for_abstraction_level( abstraction_level=abstraction_level, organisation=organisation ) diff --git a/epilepsy12/forms_folder/case_form.py b/epilepsy12/forms_folder/case_form.py index 97b7c128c..04aa5e5ed 100644 --- a/epilepsy12/forms_folder/case_form.py +++ b/epilepsy12/forms_folder/case_form.py @@ -131,15 +131,14 @@ def __init__(self, *args, **kwargs) -> None: if self.is_jersey: # this is Jersey if self.instance.unique_reference_number is None: - # this is a new form - create a new URN that is unique - while True: - urn = f"JEY-{nhs_number_package.generate()[0]}" - if not Case.objects.filter( - unique_reference_number=urn - ).exists(): - break - self.fields["unique_reference_number"].initial = urn - self.fields["nhs_number"].initial = None + # this is a new form - create a new URN that is unique (not in the database) + # we are trying to avoid a generator function here to keep the code simple + # so there is a small chance that the URN generated is not unique but + # this is acceptable for the purposes of this example since it is only for testing + urn = f"{randint(100000, 999999)}" # 6 digit URN + if not Case.objects.filter(unique_reference_number=urn).exists(): + self.fields["unique_reference_number"].initial = urn + self.fields["nhs_number"].initial = None else: # this is England or Wales self.fields["unique_reference_number"].initial = None @@ -205,32 +204,3 @@ def clean_nhs_number(self): return formatted_nhs_number else: raise ValidationError(f"{formatted_nhs_number} is not a valid NHS number") - - def clean(self): - cleaned_data = super().clean() - nhs_number = cleaned_data.get("nhs_number") - unique_reference_number = cleaned_data.get("unique_reference_number") - if self.is_jersey: - if unique_reference_number is None: - raise ValidationError("URN is a mandatory field.") - if self.instance.unique_reference_number: - # there is an existing URN - if unique_reference_number != self.instance.unique_reference_number: - # the new URN does not match the one stored - if Case.objects.filter( - unique_reference_number=unique_reference_number - ).exists(): - raise ValidationError("Unique Reference Number already taken!") - else: - # this is a new form - check this number is unique in the database - if Case.objects.filter( - unique_reference_number=unique_reference_number - ).exists(): - raise ValidationError("Unique Reference Number already taken!") - else: - # this is England or Wales - NHS numbers are used - # validation of NHS number is done in the clean_nhs_number method - if nhs_number is None: - raise ValidationError("NHS Number is a mandatory field.") - - return cleaned_data diff --git a/epilepsy12/management/commands/seed.py b/epilepsy12/management/commands/seed.py index 1458f6b72..cfbe3ee37 100644 --- a/epilepsy12/management/commands/seed.py +++ b/epilepsy12/management/commands/seed.py @@ -40,6 +40,7 @@ "7A2AJ", # Bronglais "7A6BJ", # Chepstow Community "7A6AV", # Ysbyty Ystrad Fawr + "RGT1W", # Jersey General Hospital ] diff --git a/epilepsy12/models_folder/case.py b/epilepsy12/models_folder/case.py index 09a9c4cf0..0c8fb3083 100644 --- a/epilepsy12/models_folder/case.py +++ b/epilepsy12/models_folder/case.py @@ -258,6 +258,7 @@ def delete(self, *args, **kwargs): class Meta: verbose_name = "Patient" verbose_name_plural = "Patients" + unique_together = ["nhs_number", "unique_reference_number"] # custom permissions for Case class permissions = [ CAN_LOCK_CHILD_CASE_DATA_FROM_EDITING, From dfe3bdae04f972eba1f48cee9e682fe1b8155f2a Mon Sep 17 00:00:00 2001 From: eatyourpeas Date: Tue, 3 Dec 2024 21:44:43 +0000 Subject: [PATCH 17/21] add migration --- .../0047_alter_case_unique_together.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 epilepsy12/migrations/0047_alter_case_unique_together.py diff --git a/epilepsy12/migrations/0047_alter_case_unique_together.py b/epilepsy12/migrations/0047_alter_case_unique_together.py new file mode 100644 index 000000000..a4c8677b7 --- /dev/null +++ b/epilepsy12/migrations/0047_alter_case_unique_together.py @@ -0,0 +1,17 @@ +# Generated by Django 5.1.2 on 2024-12-03 21:44 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ("epilepsy12", "0046_case_unique_reference_number_and_more"), + ] + + operations = [ + migrations.AlterUniqueTogether( + name="case", + unique_together={("nhs_number", "unique_reference_number")}, + ), + ] From 23db5c17ff01a42aaaef593381443f07b9fe4b69 Mon Sep 17 00:00:00 2001 From: eatyourpeas Date: Mon, 20 Jan 2025 18:40:40 +0000 Subject: [PATCH 18/21] move migrations to temp --- .../0043_alter_country_bng_e_alter_country_bng_n_and_more.py | 0 ...sion_s01jobplannedhoursperweekleadershipqiactivities_and_mo.py | 0 .../0044_add_jersey_boundaries_to_country_remap_identifier.py | 0 .../{ => temp}/0044_alter_comorbidity_comorbidityentity.py | 0 .../0045_add_jersey_general_hospital_and_relationships.py | 0 .../{ => temp}/0046_case_unique_reference_number_and_more.py | 0 .../migrations/{ => temp}/0047_alter_case_unique_together.py | 0 7 files changed, 0 insertions(+), 0 deletions(-) rename epilepsy12/migrations/{ => temp}/0043_alter_country_bng_e_alter_country_bng_n_and_more.py (100%) rename epilepsy12/migrations/{ => temp}/0043_alter_historicalorganisationalauditsubmission_s01jobplannedhoursperweekleadershipqiactivities_and_mo.py (100%) rename epilepsy12/migrations/{ => temp}/0044_add_jersey_boundaries_to_country_remap_identifier.py (100%) rename epilepsy12/migrations/{ => temp}/0044_alter_comorbidity_comorbidityentity.py (100%) rename epilepsy12/migrations/{ => temp}/0045_add_jersey_general_hospital_and_relationships.py (100%) rename epilepsy12/migrations/{ => temp}/0046_case_unique_reference_number_and_more.py (100%) rename epilepsy12/migrations/{ => temp}/0047_alter_case_unique_together.py (100%) diff --git a/epilepsy12/migrations/0043_alter_country_bng_e_alter_country_bng_n_and_more.py b/epilepsy12/migrations/temp/0043_alter_country_bng_e_alter_country_bng_n_and_more.py similarity index 100% rename from epilepsy12/migrations/0043_alter_country_bng_e_alter_country_bng_n_and_more.py rename to epilepsy12/migrations/temp/0043_alter_country_bng_e_alter_country_bng_n_and_more.py diff --git a/epilepsy12/migrations/0043_alter_historicalorganisationalauditsubmission_s01jobplannedhoursperweekleadershipqiactivities_and_mo.py b/epilepsy12/migrations/temp/0043_alter_historicalorganisationalauditsubmission_s01jobplannedhoursperweekleadershipqiactivities_and_mo.py similarity index 100% rename from epilepsy12/migrations/0043_alter_historicalorganisationalauditsubmission_s01jobplannedhoursperweekleadershipqiactivities_and_mo.py rename to epilepsy12/migrations/temp/0043_alter_historicalorganisationalauditsubmission_s01jobplannedhoursperweekleadershipqiactivities_and_mo.py diff --git a/epilepsy12/migrations/0044_add_jersey_boundaries_to_country_remap_identifier.py b/epilepsy12/migrations/temp/0044_add_jersey_boundaries_to_country_remap_identifier.py similarity index 100% rename from epilepsy12/migrations/0044_add_jersey_boundaries_to_country_remap_identifier.py rename to epilepsy12/migrations/temp/0044_add_jersey_boundaries_to_country_remap_identifier.py diff --git a/epilepsy12/migrations/0044_alter_comorbidity_comorbidityentity.py b/epilepsy12/migrations/temp/0044_alter_comorbidity_comorbidityentity.py similarity index 100% rename from epilepsy12/migrations/0044_alter_comorbidity_comorbidityentity.py rename to epilepsy12/migrations/temp/0044_alter_comorbidity_comorbidityentity.py diff --git a/epilepsy12/migrations/0045_add_jersey_general_hospital_and_relationships.py b/epilepsy12/migrations/temp/0045_add_jersey_general_hospital_and_relationships.py similarity index 100% rename from epilepsy12/migrations/0045_add_jersey_general_hospital_and_relationships.py rename to epilepsy12/migrations/temp/0045_add_jersey_general_hospital_and_relationships.py diff --git a/epilepsy12/migrations/0046_case_unique_reference_number_and_more.py b/epilepsy12/migrations/temp/0046_case_unique_reference_number_and_more.py similarity index 100% rename from epilepsy12/migrations/0046_case_unique_reference_number_and_more.py rename to epilepsy12/migrations/temp/0046_case_unique_reference_number_and_more.py diff --git a/epilepsy12/migrations/0047_alter_case_unique_together.py b/epilepsy12/migrations/temp/0047_alter_case_unique_together.py similarity index 100% rename from epilepsy12/migrations/0047_alter_case_unique_together.py rename to epilepsy12/migrations/temp/0047_alter_case_unique_together.py From 0948a05b01e9e1543362d84789cee450f3dbbe01 Mon Sep 17 00:00:00 2001 From: eatyourpeas Date: Mon, 20 Jan 2025 19:12:15 +0000 Subject: [PATCH 19/21] reorder migrations --- ...on_s01jobplannedhoursperweekleadershipqiactivities_and_mo.py | 0 .../{temp => }/0044_alter_comorbidity_comorbidityentity.py | 0 ...=> 0046_alter_country_bng_e_alter_country_bng_n_and_more.py} | 2 +- ...> 0047_add_jersey_boundaries_to_country_remap_identifier.py} | 2 +- ...py => 0048_add_jersey_general_hospital_and_relationships.py} | 2 +- ...nd_more.py => 0049_case_unique_reference_number_and_more.py} | 2 +- ...se_unique_together.py => 0050_alter_case_unique_together.py} | 2 +- 7 files changed, 5 insertions(+), 5 deletions(-) rename epilepsy12/migrations/{temp => }/0043_alter_historicalorganisationalauditsubmission_s01jobplannedhoursperweekleadershipqiactivities_and_mo.py (100%) rename epilepsy12/migrations/{temp => }/0044_alter_comorbidity_comorbidityentity.py (100%) rename epilepsy12/migrations/{temp/0043_alter_country_bng_e_alter_country_bng_n_and_more.py => 0046_alter_country_bng_e_alter_country_bng_n_and_more.py} (94%) rename epilepsy12/migrations/{temp/0044_add_jersey_boundaries_to_country_remap_identifier.py => 0047_add_jersey_boundaries_to_country_remap_identifier.py} (96%) rename epilepsy12/migrations/{temp/0045_add_jersey_general_hospital_and_relationships.py => 0048_add_jersey_general_hospital_and_relationships.py} (97%) rename epilepsy12/migrations/{temp/0046_case_unique_reference_number_and_more.py => 0049_case_unique_reference_number_and_more.py} (96%) rename epilepsy12/migrations/{temp/0047_alter_case_unique_together.py => 0050_alter_case_unique_together.py} (83%) diff --git a/epilepsy12/migrations/temp/0043_alter_historicalorganisationalauditsubmission_s01jobplannedhoursperweekleadershipqiactivities_and_mo.py b/epilepsy12/migrations/0043_alter_historicalorganisationalauditsubmission_s01jobplannedhoursperweekleadershipqiactivities_and_mo.py similarity index 100% rename from epilepsy12/migrations/temp/0043_alter_historicalorganisationalauditsubmission_s01jobplannedhoursperweekleadershipqiactivities_and_mo.py rename to epilepsy12/migrations/0043_alter_historicalorganisationalauditsubmission_s01jobplannedhoursperweekleadershipqiactivities_and_mo.py diff --git a/epilepsy12/migrations/temp/0044_alter_comorbidity_comorbidityentity.py b/epilepsy12/migrations/0044_alter_comorbidity_comorbidityentity.py similarity index 100% rename from epilepsy12/migrations/temp/0044_alter_comorbidity_comorbidityentity.py rename to epilepsy12/migrations/0044_alter_comorbidity_comorbidityentity.py diff --git a/epilepsy12/migrations/temp/0043_alter_country_bng_e_alter_country_bng_n_and_more.py b/epilepsy12/migrations/0046_alter_country_bng_e_alter_country_bng_n_and_more.py similarity index 94% rename from epilepsy12/migrations/temp/0043_alter_country_bng_e_alter_country_bng_n_and_more.py rename to epilepsy12/migrations/0046_alter_country_bng_e_alter_country_bng_n_and_more.py index 1cd696b05..a3412d43c 100644 --- a/epilepsy12/migrations/temp/0043_alter_country_bng_e_alter_country_bng_n_and_more.py +++ b/epilepsy12/migrations/0046_alter_country_bng_e_alter_country_bng_n_and_more.py @@ -6,7 +6,7 @@ class Migration(migrations.Migration): dependencies = [ - ("epilepsy12", "0042_set_input_achieved_true"), + ("epilepsy12", "0045_alter_kpi_assessment_of_mental_health_issues_and_more"), ] operations = [ diff --git a/epilepsy12/migrations/temp/0044_add_jersey_boundaries_to_country_remap_identifier.py b/epilepsy12/migrations/0047_add_jersey_boundaries_to_country_remap_identifier.py similarity index 96% rename from epilepsy12/migrations/temp/0044_add_jersey_boundaries_to_country_remap_identifier.py rename to epilepsy12/migrations/0047_add_jersey_boundaries_to_country_remap_identifier.py index 3e8852248..1052e6a0e 100644 --- a/epilepsy12/migrations/temp/0044_add_jersey_boundaries_to_country_remap_identifier.py +++ b/epilepsy12/migrations/0047_add_jersey_boundaries_to_country_remap_identifier.py @@ -49,7 +49,7 @@ def load_jersey_shape_file_mapping(apps, schema_editor): class Migration(migrations.Migration): dependencies = [ - ("epilepsy12", "0043_alter_country_bng_e_alter_country_bng_n_and_more"), + ("epilepsy12", "0046_alter_country_bng_e_alter_country_bng_n_and_more"), ] operations = [ diff --git a/epilepsy12/migrations/temp/0045_add_jersey_general_hospital_and_relationships.py b/epilepsy12/migrations/0048_add_jersey_general_hospital_and_relationships.py similarity index 97% rename from epilepsy12/migrations/temp/0045_add_jersey_general_hospital_and_relationships.py rename to epilepsy12/migrations/0048_add_jersey_general_hospital_and_relationships.py index 6bb034703..bdd3d0755 100644 --- a/epilepsy12/migrations/temp/0045_add_jersey_general_hospital_and_relationships.py +++ b/epilepsy12/migrations/0048_add_jersey_general_hospital_and_relationships.py @@ -82,7 +82,7 @@ def create_jersey_general_hospital(apps, schema_editor): class Migration(migrations.Migration): dependencies = [ - ("epilepsy12", "0044_add_jersey_boundaries_to_country_remap_identifier"), + ("epilepsy12", "0047_add_jersey_boundaries_to_country_remap_identifier"), ] operations = [ diff --git a/epilepsy12/migrations/temp/0046_case_unique_reference_number_and_more.py b/epilepsy12/migrations/0049_case_unique_reference_number_and_more.py similarity index 96% rename from epilepsy12/migrations/temp/0046_case_unique_reference_number_and_more.py rename to epilepsy12/migrations/0049_case_unique_reference_number_and_more.py index 26addf1cc..fd10788da 100644 --- a/epilepsy12/migrations/temp/0046_case_unique_reference_number_and_more.py +++ b/epilepsy12/migrations/0049_case_unique_reference_number_and_more.py @@ -6,7 +6,7 @@ class Migration(migrations.Migration): dependencies = [ - ("epilepsy12", "0045_add_jersey_general_hospital_and_relationships"), + ("epilepsy12", "0048_add_jersey_general_hospital_and_relationships"), ] operations = [ diff --git a/epilepsy12/migrations/temp/0047_alter_case_unique_together.py b/epilepsy12/migrations/0050_alter_case_unique_together.py similarity index 83% rename from epilepsy12/migrations/temp/0047_alter_case_unique_together.py rename to epilepsy12/migrations/0050_alter_case_unique_together.py index a4c8677b7..747642e6b 100644 --- a/epilepsy12/migrations/temp/0047_alter_case_unique_together.py +++ b/epilepsy12/migrations/0050_alter_case_unique_together.py @@ -6,7 +6,7 @@ class Migration(migrations.Migration): dependencies = [ - ("epilepsy12", "0046_case_unique_reference_number_and_more"), + ("epilepsy12", "0049_case_unique_reference_number_and_more"), ] operations = [ From 9dda236736c7ed5613e7687bee99ed17bea88c1c Mon Sep 17 00:00:00 2001 From: eatyourpeas Date: Mon, 20 Jan 2025 22:10:55 +0000 Subject: [PATCH 20/21] template edits to incorporate jersey --- .../common_view_functions/map_from_shape_file.py | 2 +- templates/epilepsy12/audit_section.html | 2 +- templates/epilepsy12/partials/kpis/kpi_table_head.html | 2 ++ templates/epilepsy12/partials/kpis/kpis.html | 10 +++++----- 4 files changed, 9 insertions(+), 7 deletions(-) diff --git a/epilepsy12/common_view_functions/map_from_shape_file.py b/epilepsy12/common_view_functions/map_from_shape_file.py index ea5e26735..5040cd172 100644 --- a/epilepsy12/common_view_functions/map_from_shape_file.py +++ b/epilepsy12/common_view_functions/map_from_shape_file.py @@ -6,7 +6,6 @@ # django imports from django.apps import apps from django.conf import settings -from django.db.models import Count, Q # third party imports import pandas as pd @@ -163,6 +162,7 @@ def generate_case_counts_for_each_region_in_each_abstraction_level( """ Case = apps.get_model("epilepsy12", "Case") + Organisation = apps.get_model("epilepsy12", "Organisation") # Create a new DataFrame to store the results df = pd.DataFrame(columns=["identifier", "name", "cases"]) diff --git a/templates/epilepsy12/audit_section.html b/templates/epilepsy12/audit_section.html index 90b32524f..ffcb2cd15 100644 --- a/templates/epilepsy12/audit_section.html +++ b/templates/epilepsy12/audit_section.html @@ -18,7 +18,7 @@
{% if registration %} - {{ registration.case.first_name }} {{ registration.case.surname }} {{ registration.case.nhs_number}} + {{ registration.case.first_name }} {{ registration.case.surname }} {% if registration.case.unique_reference_number is not None %}{{ registration.case.unique_reference_number }}{% else %}{{ registration.case.nhs_number }}{% endif %} {% elif case %} {{ case.first_name }} {{ case.surname }} {{ case.nhs_number }} {% endif %} diff --git a/templates/epilepsy12/partials/kpis/kpi_table_head.html b/templates/epilepsy12/partials/kpis/kpi_table_head.html index 8e4d73ed6..72b3f72f9 100644 --- a/templates/epilepsy12/partials/kpis/kpi_table_head.html +++ b/templates/epilepsy12/partials/kpis/kpi_table_head.html @@ -29,6 +29,7 @@ _="init js $('#totals_trust_{{kpi_head_id}}').popup(); end" >({{all_data.TRUST_KPIS.total_cases_registered}})

+ {% if country.boundary_identifier != 'JEY' %}

Integrated Care Board

({{all_data.NHS_ENGLAND_REGION_KPIS.total_cases_registered}})

+ {% endif %} {% else %}

Local Health Board

diff --git a/templates/epilepsy12/partials/kpis/kpis.html b/templates/epilepsy12/partials/kpis/kpis.html index 32b07ca01..1b8417e9f 100644 --- a/templates/epilepsy12/partials/kpis/kpis.html +++ b/templates/epilepsy12/partials/kpis/kpis.html @@ -50,7 +50,7 @@

Clinical Team

- {% include './kpi_table_head.html' with kpi_head_id=1 %} + {% include './kpi_table_head.html' with kpi_head_id=1 country=organisation.country %} {% comment %} [ @@ -232,7 +232,7 @@

Clinical Team

Investigations

{% comment %} [ECG, MRI,] {% endcomment %}
- {% include './kpi_table_head.html' with kpi_head_id=2 %} + {% include './kpi_table_head.html' with kpi_head_id=2 country=organisation.country %} {% for kpi_name in kpi_names_list|slice:"4:6" %} @@ -294,7 +294,7 @@

Investigations

Mental Health

{% comment %} ['assessment_of_mental_health_issues', 'mental_health_support'] {% endcomment %}
- {% include './kpi_table_head.html' with kpi_head_id=3 %} + {% include './kpi_table_head.html' with country=organisation.country kpi_head_id=3 %} {% for kpi_name in kpi_names_list|slice:"6:8" %} @@ -356,7 +356,7 @@

Mental Health

Medication

{% comment %} [Sodium valproate] {% endcomment %}
- {% include './kpi_table_head.html' with kpi_head_id=4 %} + {% include './kpi_table_head.html' with country=organisation.country kpi_head_id=4 %} {% comment %} {% endcomment %} {% for kpi_name in kpi_names_list|slice:"8:9" %} @@ -418,7 +418,7 @@

Medication

Care Planning

{% comment %} ['comprehensive_care_planning_agreement', 'patient_held_individualised_epilepsy_document', 'patient_carer_parent_agreement_to_the_care_planning', 'care_planning_has_been_updated_when_necessary', 'comprehensive_care_planning_content', 'parental_prolonged_seizures_care_plan', 'water_safety', 'first_aid', 'general_participation_and_risk', 'sudep', 'service_contact_details', 'school_individual_healthcare_plan'] {% endcomment %}
- {% include './kpi_table_head.html' with kpi_head_id=5 %} + {% include './kpi_table_head.html' with country=organisation.country kpi_head_id=5 %} {% comment %} comprehensive_care_planning_agreement {% endcomment %} From c4d853d3c850dcbf2afa5d0da049992d5eb73176 Mon Sep 17 00:00:00 2001 From: eatyourpeas Date: Thu, 23 Jan 2025 18:27:19 +0000 Subject: [PATCH 21/21] remove extraneous print statements for debugging --- epilepsy12/common_view_functions/aggregate_by.py | 1 + .../calculate_kpi_tests/test_measure_10.py | 2 -- epilepsy12/tests/model_tests/test_investigations.py | 2 -- epilepsy12/tests/model_tests/test_registration.py | 13 ------------- .../view_tests/db_actions/test_update_actions.py | 8 +++++--- .../form_calculations/test_completed_fields.py | 1 - .../test_perms_download_e12users_list.py | 6 +++--- epilepsy12/views/multiaxial_diagnosis_views.py | 2 -- epilepsy12/views/organisation_views.py | 2 +- 9 files changed, 10 insertions(+), 27 deletions(-) diff --git a/epilepsy12/common_view_functions/aggregate_by.py b/epilepsy12/common_view_functions/aggregate_by.py index 035a61444..1bf2d382e 100644 --- a/epilepsy12/common_view_functions/aggregate_by.py +++ b/epilepsy12/common_view_functions/aggregate_by.py @@ -634,6 +634,7 @@ def get_all_kpi_aggregation_data_for_view( updates the relevant AbstractionModel and returns the KPI model as a dict. """ + ALL_DATA = {} for enum_abstraction_level in EnumAbstractionLevel: # For the given abstraction, get the {ABSTRACTION}KPIAggregation model diff --git a/epilepsy12/tests/common_view_functions_tests/calculate_kpi_tests/test_measure_10.py b/epilepsy12/tests/common_view_functions_tests/calculate_kpi_tests/test_measure_10.py index e0f390af3..ab0a871e4 100644 --- a/epilepsy12/tests/common_view_functions_tests/calculate_kpi_tests/test_measure_10.py +++ b/epilepsy12/tests/common_view_functions_tests/calculate_kpi_tests/test_measure_10.py @@ -42,8 +42,6 @@ def test_measure_10_school_individual_healthcare_plan( FIRST_PAEDIATRIC_ASSESSMENT_DATE = date(2023, 1, 1) DATE_OF_BIRTH = FIRST_PAEDIATRIC_ASSESSMENT_DATE - age - print(f"age: {age}") - # create case case = e12_case_factory( date_of_birth=DATE_OF_BIRTH, diff --git a/epilepsy12/tests/model_tests/test_investigations.py b/epilepsy12/tests/model_tests/test_investigations.py index 92f609027..843d3b6a9 100644 --- a/epilepsy12/tests/model_tests/test_investigations.py +++ b/epilepsy12/tests/model_tests/test_investigations.py @@ -147,8 +147,6 @@ def test_validation_request_or_performed_in_future( # assign performed date to 2 months after today performed_date = date(2023, 9, 15) + relativedelta(months=2) - print(Investigations.get_current_date()) - with pytest.raises(ValidationError): # try to save an investigation whose request date is after today case = e12_case_factory( diff --git a/epilepsy12/tests/model_tests/test_registration.py b/epilepsy12/tests/model_tests/test_registration.py index 969ad4a54..57605be75 100644 --- a/epilepsy12/tests/model_tests/test_registration.py +++ b/epilepsy12/tests/model_tests/test_registration.py @@ -541,10 +541,6 @@ def test_accept_registration_transfer_response_previously_involved( assert kch_site.case == case, "KCH is still historically associated with the case" # GOSH is now the primary site of care - for filter_site in Site.objects.filter(organisation=GOSH, case=case): - print( - f"primary: {filter_site.site_is_primary_centre_of_epilepsy_care}, general: {filter_site.site_is_general_paediatric_centre}, active: {filter_site.site_is_actively_involved_in_epilepsy_care}, primary: {filter_site.site_is_primary_centre_of_epilepsy_care}" - ) new_site = Site.objects.get( organisation=GOSH, case=case, @@ -732,10 +728,6 @@ def test_accept_registration_transfer_response_transfer_centre_still_involved( assert kch_site.case == case, "KCH is still a general paediatric centre" # GOSH is now the primary site of care - for filter_site in Site.objects.filter(organisation=GOSH, case=case): - print( - f"primary: {filter_site.site_is_primary_centre_of_epilepsy_care}, general: {filter_site.site_is_general_paediatric_centre}, active: {filter_site.site_is_actively_involved_in_epilepsy_care}, primary: {filter_site.site_is_primary_centre_of_epilepsy_care}" - ) new_site = Site.objects.get( organisation=GOSH, case=case, @@ -1181,11 +1173,6 @@ def test_reject_registration_transfer_response_transfer_centre_still_involved( assert response.status_code == 200 - for mysite in Site.objects.all(): - print( - f"name: {mysite} active transfer {mysite.active_transfer}, general paediatrics {mysite.site_is_general_paediatric_centre}, primary epilepsy {mysite.site_is_primary_centre_of_epilepsy_care}, actively involved {mysite.site_is_actively_involved_in_epilepsy_care}" - ) - # Verify the Site instance kch_sites = Site.objects.filter( organisation=KCH, diff --git a/epilepsy12/tests/view_tests/db_actions/test_update_actions.py b/epilepsy12/tests/view_tests/db_actions/test_update_actions.py index 004ae8959..4f94ca113 100644 --- a/epilepsy12/tests/view_tests/db_actions/test_update_actions.py +++ b/epilepsy12/tests/view_tests/db_actions/test_update_actions.py @@ -366,6 +366,7 @@ [x] Assert user can change 'has_a_valproate_annual_risk_acknowledgement_form_been_completed' to False """ + # Python imports from datetime import date from dateutil import relativedelta @@ -385,7 +386,9 @@ from epilepsy12.tests.factories import ( E12CaseFactory, ) -from epilepsy12.tests.view_tests.permissions_tests.perm_tests_utils import twofactor_signin +from epilepsy12.tests.view_tests.permissions_tests.perm_tests_utils import ( + twofactor_signin, +) # E12 imports from epilepsy12.models import ( @@ -864,7 +867,6 @@ def test_user_updates_toggles_true_success(client): client.force_login(test_user) for index, url in enumerate(TOGGLES): - print(url.get("field_name")) model = get_model_from_model( case=CASE_FROM_TEST_USER_ORGANISATION, model_name=url.get("model") ) @@ -1239,7 +1241,7 @@ def test_age_at_registration_cannot_be_gt_24yo(client, GOSH): ) client.force_login(test_user) - + # 2fa enable twofactor_signin(client, test_user) diff --git a/epilepsy12/tests/view_tests/form_calculations/test_completed_fields.py b/epilepsy12/tests/view_tests/form_calculations/test_completed_fields.py index 0b280019a..3e2606997 100644 --- a/epilepsy12/tests/view_tests/form_calculations/test_completed_fields.py +++ b/epilepsy12/tests/view_tests/form_calculations/test_completed_fields.py @@ -519,7 +519,6 @@ def test_completed_fields_assessment_random_fields(e12_case_factory, GOSH): ANSWER = random.choice([None, True]) factory_attributes.update({KEY_NAME: ANSWER}) if ANSWER is not None: - print(f"Adding 1 because {KEY_NAME} is not None") EXPECTED_SCORE += 1 # All other bool fields have dependent date fields diff --git a/epilepsy12/tests/view_tests/permissions_tests/test_perms_download_e12users_list.py b/epilepsy12/tests/view_tests/permissions_tests/test_perms_download_e12users_list.py index 6a10b553e..9e9b346b1 100644 --- a/epilepsy12/tests/view_tests/permissions_tests/test_perms_download_e12users_list.py +++ b/epilepsy12/tests/view_tests/permissions_tests/test_perms_download_e12users_list.py @@ -18,7 +18,9 @@ Epilepsy12User, Organisation, ) -from epilepsy12.tests.view_tests.permissions_tests.perm_tests_utils import twofactor_signin +from epilepsy12.tests.view_tests.permissions_tests.perm_tests_utils import ( + twofactor_signin, +) @pytest.mark.django_db @@ -52,8 +54,6 @@ def test_download_button_access( ) ) if test_user.first_name.endswith(test_user_rcpch_audit_team_data.role_str): - - print(response) assert ( response["Content-Disposition"] == 'attachment; filename="epilepsy12users.csv"' diff --git a/epilepsy12/views/multiaxial_diagnosis_views.py b/epilepsy12/views/multiaxial_diagnosis_views.py index 09bb8f9b3..5409fcfc4 100644 --- a/epilepsy12/views/multiaxial_diagnosis_views.py +++ b/epilepsy12/views/multiaxial_diagnosis_views.py @@ -1622,8 +1622,6 @@ def close_comorbidity(request, comorbidity_id): comorbidity = Comorbidity.objects.get(pk=comorbidity_id) multiaxial_diagnosis = comorbidity.multiaxial_diagnosis - print() - # if all the fields are none this was not completed - delete the record if ( completed_fields(comorbidity) == 0 diff --git a/epilepsy12/views/organisation_views.py b/epilepsy12/views/organisation_views.py index 52eec4d94..f62907493 100644 --- a/epilepsy12/views/organisation_views.py +++ b/epilepsy12/views/organisation_views.py @@ -267,7 +267,7 @@ def selected_organisation_summary(request, organisation_id): context["nhsregion_heatmap"] = None else: if selected_organisation.ods_code == "RGT1W": - # Jersey is a special case and although is mapped to England, is in the Channel Islands and has no ICB, NHS Region or LHB + # Jersey is a special case as it is in the Channel Islands and has no ICB, NHS Region or LHB context["trust_heatmap"] = None context["icb_heatmap"] = None context["nhsregion_heatmap"] = None