diff --git a/project/npda/kpi_class/__init__.py b/project/npda/kpi_class/__init__.py new file mode 100644 index 00000000..fa9468b1 --- /dev/null +++ b/project/npda/kpi_class/__init__.py @@ -0,0 +1 @@ +from .kpis import * diff --git a/project/npda/general_functions/kpis.py b/project/npda/kpi_class/kpis.py similarity index 89% rename from project/npda/general_functions/kpis.py rename to project/npda/kpi_class/kpis.py index c251cb7b..37c1a9a4 100644 --- a/project/npda/general_functions/kpis.py +++ b/project/npda/kpi_class/kpis.py @@ -5,44 +5,62 @@ import time from dataclasses import asdict, dataclass, is_dataclass from datetime import date, datetime, timedelta + # Python imports from decimal import Decimal from pprint import pformat from typing import Tuple, Union from dateutil.relativedelta import relativedelta + # Django imports -from django.db.models import (Avg, Case, Count, Exists, F, Func, IntegerField, - OuterRef, Q, QuerySet, Subquery, Sum, When) +from django.apps import apps +from django.db.models import ( + Avg, + Case, + Count, + Exists, + F, + Func, + IntegerField, + OuterRef, + Q, + QuerySet, + Subquery, + Sum, + When, +) from django.shortcuts import render from django.views.generic import TemplateView # NPDA Imports from project.constants.albuminuria_stage import ALBUMINURIA_STAGES from project.constants.diabetes_types import DIABETES_TYPES -from project.constants.hospital_admission_reasons import \ - HOSPITAL_ADMISSION_REASONS -from project.constants.retinal_screening_results import \ - RETINAL_SCREENING_RESULTS +from project.constants.hospital_admission_reasons import HOSPITAL_ADMISSION_REASONS +from project.constants.retinal_screening_results import RETINAL_SCREENING_RESULTS from project.constants.smoking_status import SMOKING_STATUS from project.constants.types.kpi_types import KPICalculationsObject, KPIResult from project.constants.yes_no_unknown import YES_NO_UNKNOWN from project.npda.general_functions import get_audit_period_for_date -from project.npda.models import Patient -from project.npda.models.visit import Visit +from project.npda.models import Patient, Visit + # Logging logger = logging.getLogger(__name__) class CalculateKPIS: + """ + Calculates KPIs for a given PZ code + """ - def __init__(self, pz_code: str, calculation_date: date = None): + def __init__(self, pz_code: str, calculation_date: date = None, patients=None): """Calculates KPIs for given pz_code Params: * pz_code (str) - PZ code for KPIS * calculation_date (date) - used to define start and end date of audit period + * patients (QuerySet) - optional, used to define patients for KPI calculations: this can be used to calculate KPIs for a subset of patients or an individual patient - if not provided, all patients for the given PZ code will be used """ if not pz_code: raise AttributeError("pz_code must be provided") @@ -65,10 +83,14 @@ def __init__(self, pz_code: str, calculation_date: date = None): # `Transfer` link table, the second `paediatric_diabetes_unit` actually # accesses the `PaediatricDiabetesUnit` model. # TODO: should this filter out patients who have left the service? - self.patients = Patient.objects.filter( - paediatric_diabetes_units__paediatric_diabetes_unit__pz_code=pz_code - ).distinct() - self.total_patients_count = self.patients.count() + if patients is None: + self.patients = Patient.objects.filter( + paediatric_diabetes_units__paediatric_diabetes_unit__pz_code=pz_code + ).distinct() + self.total_patients_count = self.patients.count() + else: + self.patients = patients + self.total_patients_count = self.patients.count() def _get_audit_start_and_end_dates(self) -> tuple[date, date]: return get_audit_period_for_date(input_date=self.calculation_date) @@ -152,6 +174,65 @@ def _get_kpi_attribute_names(self) -> dict[int, str]: return kpis + def title_for_kpi(self, kpi_number: int) -> str: + """Returns a readable title for a given KPI number""" + # Hard coding these for simplicty and readability + kpi_titles = { + 1: "Total number of eligible patients", + 2: "Total number of new diagnoses within the audit period", + 3: "Total number of eligible patients with Type 1 diabetes", + 4: "Number of patients aged 12+ with Type 1 diabetes", + 5: "Total number of patients with T1DM who have completed a year of care", + 6: "Total number of patients with T1DM who have completed a year of care and are aged 12 or older", + 7: "Total number of new diagnoses of T1DM", + 8: "Number of patients who died within audit period", + 9: "Number of patients who transitioned/left service within audit period", + 10: "Total number of coeliacs", + 11: "Number of patients with thyroid disease", + 12: "Number of patients with ketone test equipment", + 13: "Number of patients on one to three injections per day", + 14: "Number of patients on four or more injections per day", + 15: "Number of patients on insulin pump", + 16: "Number of patients on one to three injections plus other medication", + 17: "Number of patients on four or more injections plus other medication", + 18: "Number of patients on insulin pump plus other medication", + 19: "Number of patients on dietary management alone", + 20: "Number of patients on dietary management plus other medication", + 21: "Number of patients on flash glucose monitor", + 22: "Number of patients on real-time CGM with alarms", + 23: "Number of patients on Type 1 real-time CGM with alarms", + 24: "Number of patients on hybrid closed loop system", + 25: "Number of patients with HbA1c", + 26: "Number of patients with BMI", + 27: "Number of patients with thyroid screen", + 28: "Number of patients with blood pressure", + 29: "Number of patients with urinary albumin", + 30: "Number of patients with retinal screening", + 31: "Number of patients with foot examination", + 321: "Care processes completion rate", + 322: "Care processes in patients < 12 years old", + 323: "Care processes in patients ≥ 12 years old", + 33: "Number of patients with 4 or more HbA1c measurements", + 34: "Number of patients offered a psychological assessment", + 35: "Number of patients asked about smoking status", + 36: "Number of patients referred to a smoking cessation service", + 37: "Number of patients offered an additional dietetic appointment", + 38: "Number of patients attending an additional dietetic appointment", + 39: "Number of patients recommended influenza immunisation", + 40: "Number of patients given sick day rules advice", + 41: "Number of patients with coeliac disease screening", + 42: "Number of patients with thyroid disease screening", + 43: "Number of patients with carbohydrate counting education", + 44: "Mean HbA1c", + 45: "Median HbA1c", + 46: "Number of admissions", + 47: "Number of DKA admissions", + 48: "Number of patients requiring additional psychological support", + 49: "Number of patients with albuminuria", + } + + return kpi_titles.get(kpi_number, "Unknown KPI") + def _run_kpi_calculation_method( self, kpi_method_name: str ) -> Union[KPIResult | str]: @@ -226,6 +307,9 @@ def calculate_kpis_for_patients(self) -> KPICalculationsObject: return_obj["calculated_kpi_values"] = {} for kpi_name, kpi_result in calculated_kpis.items(): return_obj["calculated_kpi_values"][kpi_name] = kpi_result + return_obj["calculated_kpi_values"][kpi_name]["kpi_label"] = ( + self.title_for_kpi(int(kpi_name.split("_")[1])) + ) return return_obj @@ -248,17 +332,12 @@ def calculate_kpi_1_total_eligible(self) -> KPIResult: # Visit / admisison date within audit period & Q(visit__visit_date__range=(self.AUDIT_DATE_RANGE)) # Below the age of 25 at the start of the audit period - & Q( - date_of_birth__gt=self.audit_start_date - - relativedelta(years=25) - ) + & Q(date_of_birth__gt=self.audit_start_date - relativedelta(years=25)) ) # Count eligible patients and set as attribute # to be used in subsequent KPI calculations - self.kpi_1_total_eligible = ( - self.total_kpi_1_eligible_pts_base_query_set.count() - ) + self.kpi_1_total_eligible = self.total_kpi_1_eligible_pts_base_query_set.count() total_eligible = self.kpi_1_total_eligible # Calculate ineligible patients @@ -293,16 +372,12 @@ def calculate_kpi_2_total_new_diagnoses(self) -> KPIResult: ) # This is same as KPI1 but with an additional filter for diagnosis date - self.total_kpi_2_eligible_pts_base_query_set = ( - base_eligible_patients.filter( - Q(diagnosis_date__range=(self.AUDIT_DATE_RANGE)) - ) + self.total_kpi_2_eligible_pts_base_query_set = base_eligible_patients.filter( + Q(diagnosis_date__range=(self.AUDIT_DATE_RANGE)) ) # Count eligible patients - self.kpi_2_total_eligible = ( - self.total_kpi_2_eligible_pts_base_query_set.count() - ) + self.kpi_2_total_eligible = self.total_kpi_2_eligible_pts_base_query_set.count() total_eligible = self.kpi_2_total_eligible # Calculate ineligible patients @@ -381,10 +456,7 @@ def calculate_kpi_4_total_t1dm_gte_12yo(self) -> KPIResult: # Diagnosis of Type 1 diabetes Q(diabetes_type=DIABETES_TYPES[0][0]) # Age 12 and above years at the start of the audit period - & Q( - date_of_birth__lte=self.audit_start_date - - relativedelta(years=12) - ) + & Q(date_of_birth__lte=self.audit_start_date - relativedelta(years=12)) ) # Count eligible patients @@ -507,10 +579,7 @@ def calculate_kpi_6_total_t1dm_complete_year_gte_12yo(self) -> dict: Q(nhs_number__isnull=False) & Q(date_of_birth__isnull=False) # Age 12 and above at the start of the audit period - & Q( - date_of_birth__lte=self.audit_start_date - - relativedelta(years=12) - ) + & Q(date_of_birth__lte=self.audit_start_date - relativedelta(years=12)) # Diagnosis of Type 1 diabetes & Q(diabetes_type=DIABETES_TYPES[0][0]) ) @@ -520,32 +589,12 @@ def calculate_kpi_6_total_t1dm_complete_year_gte_12yo(self) -> dict: # patient valid_visit_subquery = Visit.objects.filter( Q( - Q( - height_weight_observation_date__range=( - self.AUDIT_DATE_RANGE - ) - ) + Q(height_weight_observation_date__range=(self.AUDIT_DATE_RANGE)) | Q(hba1c_date__range=(self.AUDIT_DATE_RANGE)) - | Q( - blood_pressure_observation_date__range=( - self.AUDIT_DATE_RANGE - ) - ) - | Q( - foot_examination_observation_date__range=( - self.AUDIT_DATE_RANGE - ) - ) - | Q( - retinal_screening_observation_date__range=( - self.AUDIT_DATE_RANGE - ) - ) - | Q( - albumin_creatinine_ratio_date__range=( - self.AUDIT_DATE_RANGE - ) - ) + | Q(blood_pressure_observation_date__range=(self.AUDIT_DATE_RANGE)) + | Q(foot_examination_observation_date__range=(self.AUDIT_DATE_RANGE)) + | Q(retinal_screening_observation_date__range=(self.AUDIT_DATE_RANGE)) + | Q(albumin_creatinine_ratio_date__range=(self.AUDIT_DATE_RANGE)) | Q(total_cholesterol_date__range=(self.AUDIT_DATE_RANGE)) | Q(thyroid_function_date__range=(self.AUDIT_DATE_RANGE)) | Q(coeliac_screen_date__range=(self.AUDIT_DATE_RANGE)) @@ -610,10 +659,7 @@ def calculate_kpi_7_total_new_diagnoses_t1dm(self) -> dict: Q(nhs_number__isnull=False) & Q(date_of_birth__isnull=False) # * Age < 25y years at the start of the audit period - & Q( - date_of_birth__gt=self.audit_start_date - - relativedelta(years=25) - ) + & Q(date_of_birth__gt=self.audit_start_date - relativedelta(years=25)) # Diagnosis of Type 1 diabetes & Q(diabetes_type=DIABETES_TYPES[0][0]) & Q(diagnosis_date__range=self.AUDIT_DATE_RANGE) @@ -622,11 +668,7 @@ def calculate_kpi_7_total_new_diagnoses_t1dm(self) -> dict: # this requires checking for a date in any of the Visit model's # observation fields (found simply by searching for date fields # with the word 'observation' in the field verbose_name) - Q( - visit__height_weight_observation_date__range=( - self.AUDIT_DATE_RANGE - ) - ) + Q(visit__height_weight_observation_date__range=(self.AUDIT_DATE_RANGE)) | Q(visit__hba1c_date__range=(self.AUDIT_DATE_RANGE)) | Q( visit__blood_pressure_observation_date__range=( @@ -643,19 +685,9 @@ def calculate_kpi_7_total_new_diagnoses_t1dm(self) -> dict: self.AUDIT_DATE_RANGE ) ) - | Q( - visit__albumin_creatinine_ratio_date__range=( - self.AUDIT_DATE_RANGE - ) - ) - | Q( - visit__total_cholesterol_date__range=( - self.AUDIT_DATE_RANGE - ) - ) - | Q( - visit__thyroid_function_date__range=(self.AUDIT_DATE_RANGE) - ) + | Q(visit__albumin_creatinine_ratio_date__range=(self.AUDIT_DATE_RANGE)) + | Q(visit__total_cholesterol_date__range=(self.AUDIT_DATE_RANGE)) + | Q(visit__thyroid_function_date__range=(self.AUDIT_DATE_RANGE)) | Q(visit__coeliac_screen_date__range=(self.AUDIT_DATE_RANGE)) | Q( visit__psychological_screening_assessment_date__range=( @@ -780,9 +812,7 @@ def calculate_kpi_10_total_coeliacs(self) -> dict: eligible_patients = base_query_set.filter( Q( id__in=Subquery( - Patient.objects.filter( - visit__in=latest_visit_subquery - ).values("id") + Patient.objects.filter(visit__in=latest_visit_subquery).values("id") ) ) ) @@ -829,9 +859,7 @@ def calculate_kpi_11_total_thyroids(self) -> dict: eligible_patients = base_query_set.filter( Q( id__in=Subquery( - Patient.objects.filter( - visit__in=latest_visit_subquery - ).values("id") + Patient.objects.filter(visit__in=latest_visit_subquery).values("id") ) ) ) @@ -864,9 +892,7 @@ def calculate_kpi_12_total_ketone_test_equipment(self) -> dict: """ # Define the subquery to find the latest visit where ketone_meter_training = 1 latest_visit_subquery = ( - Visit.objects.filter( - patient=OuterRef("pk"), ketone_meter_training=1 - ) + Visit.objects.filter(patient=OuterRef("pk"), ketone_meter_training=1) .order_by("-visit_date") .values("pk")[:1] ) @@ -878,9 +904,7 @@ def calculate_kpi_12_total_ketone_test_equipment(self) -> dict: eligible_patients = base_query_set.filter( Q( id__in=Subquery( - Patient.objects.filter( - visit__in=latest_visit_subquery - ).values("id") + Patient.objects.filter(visit__in=latest_visit_subquery).values("id") ) ) ) @@ -928,9 +952,7 @@ def calculate_kpi_13_one_to_three_injections_per_day(self) -> dict: total_passed = eligible_patients.filter( Q( id__in=Subquery( - Patient.objects.filter( - visit__in=latest_visit_subquery - ).values("id") + Patient.objects.filter(visit__in=latest_visit_subquery).values("id") ) ) ).count() @@ -968,9 +990,7 @@ def calculate_kpi_14_four_or_more_injections_per_day(self) -> dict: total_passed = eligible_patients.filter( Q( id__in=Subquery( - Patient.objects.filter( - visit__in=latest_visit_subquery - ).values("id") + Patient.objects.filter(visit__in=latest_visit_subquery).values("id") ) ) ).count() @@ -1008,9 +1028,7 @@ def calculate_kpi_15_insulin_pump(self) -> dict: total_passed = eligible_patients.filter( Q( id__in=Subquery( - Patient.objects.filter( - visit__in=latest_visit_subquery - ).values("id") + Patient.objects.filter(visit__in=latest_visit_subquery).values("id") ) ) ).count() @@ -1049,9 +1067,7 @@ def calculate_kpi_16_one_to_three_injections_plus_other_medication( total_passed = eligible_patients.filter( Q( id__in=Subquery( - Patient.objects.filter( - visit__in=latest_visit_subquery - ).values("id") + Patient.objects.filter(visit__in=latest_visit_subquery).values("id") ) ) ).count() @@ -1089,9 +1105,7 @@ def calculate_kpi_17_four_or_more_injections_plus_other_medication( total_passed = eligible_patients.filter( Q( id__in=Subquery( - Patient.objects.filter( - visit__in=latest_visit_subquery - ).values("id") + Patient.objects.filter(visit__in=latest_visit_subquery).values("id") ) ) ).count() @@ -1129,9 +1143,7 @@ def calculate_kpi_18_insulin_pump_plus_other_medication( total_passed = eligible_patients.filter( Q( id__in=Subquery( - Patient.objects.filter( - visit__in=latest_visit_subquery - ).values("id") + Patient.objects.filter(visit__in=latest_visit_subquery).values("id") ) ) ).count() @@ -1169,9 +1181,7 @@ def calculate_kpi_19_dietary_management_alone( total_passed = eligible_patients.filter( Q( id__in=Subquery( - Patient.objects.filter( - visit__in=latest_visit_subquery - ).values("id") + Patient.objects.filter(visit__in=latest_visit_subquery).values("id") ) ) ).count() @@ -1209,9 +1219,7 @@ def calculate_kpi_20_dietary_management_plus_other_medication( total_passed = eligible_patients.filter( Q( id__in=Subquery( - Patient.objects.filter( - visit__in=latest_visit_subquery - ).values("id") + Patient.objects.filter(visit__in=latest_visit_subquery).values("id") ) ) ).count() @@ -1241,9 +1249,7 @@ def calculate_kpi_21_flash_glucose_monitor( # Define the subquery to find the latest visit where blood glucose monitoring (item 22) is either 2 = Flash glucose monitor or 3 = Modified flash glucose monitor (e.g. with MiaoMiao, Blucon etc.) latest_visit_subquery = ( - Visit.objects.filter( - patient=OuterRef("pk"), glucose_monitoring__in=[2, 3] - ) + Visit.objects.filter(patient=OuterRef("pk"), glucose_monitoring__in=[2, 3]) .order_by("-visit_date") .values("pk")[:1] ) @@ -1251,9 +1257,7 @@ def calculate_kpi_21_flash_glucose_monitor( total_passed = eligible_patients.filter( Q( id__in=Subquery( - Patient.objects.filter( - visit__in=latest_visit_subquery - ).values("id") + Patient.objects.filter(visit__in=latest_visit_subquery).values("id") ) ) ).count() @@ -1291,9 +1295,7 @@ def calculate_kpi_22_real_time_cgm_with_alarms( total_passed = eligible_patients.filter( Q( id__in=Subquery( - Patient.objects.filter( - visit__in=latest_visit_subquery - ).values("id") + Patient.objects.filter(visit__in=latest_visit_subquery).values("id") ) ) ).count() @@ -1332,9 +1334,7 @@ def calculate_kpi_23_type1_real_time_cgm_with_alarms( total_passed = eligible_patients.filter( Q( id__in=Subquery( - Patient.objects.filter( - visit__in=latest_visit_subquery - ).values("id") + Patient.objects.filter(visit__in=latest_visit_subquery).values("id") ) ) ).count() @@ -1384,14 +1384,12 @@ def calculate_kpi_24_hybrid_closed_loop_system( .order_by("-visit_date") .values("pk")[:1] ) - eligible_patients_kpi_24 = ( - total_kpi_1_eligible_pts_base_query_set.filter( - Q( - id__in=Subquery( - Patient.objects.filter( - visit__in=eligible_kpi_24_latest_visit_subquery - ).values("id") - ) + eligible_patients_kpi_24 = total_kpi_1_eligible_pts_base_query_set.filter( + Q( + id__in=Subquery( + Patient.objects.filter( + visit__in=eligible_kpi_24_latest_visit_subquery + ).values("id") ) ) ) @@ -1402,9 +1400,9 @@ def calculate_kpi_24_hybrid_closed_loop_system( # PLUS # the subset of total_kpi_1_eligible_pts_base_query_set # who are ineligible for kpi24 (not on an insulin pump or insulin pump therapy) - total_ineligible = ( - self.total_patients_count - total_eligible_kpi_1 - ) + (total_eligible_kpi_1 - total_eligible_kpi_24) + total_ineligible = (self.total_patients_count - total_eligible_kpi_1) + ( + total_eligible_kpi_1 - total_eligible_kpi_24 + ) # Passing patients are the subset of kpi_24 eligible who are on closed loop system passing_patients = eligible_patients_kpi_24.filter( @@ -1497,11 +1495,7 @@ def calculate_kpi_26_bmi( Q(visit__height__isnull=False), Q(visit__weight__isnull=False), # Within audit period - Q( - visit__height_weight_observation_date__range=( - self.AUDIT_DATE_RANGE - ) - ), + Q(visit__height_weight_observation_date__range=(self.AUDIT_DATE_RANGE)), ) total_passed = total_passed_query_set.count() @@ -1572,11 +1566,7 @@ def calculate_kpi_28_blood_pressure( total_passed_query_set = eligible_patients.filter( # Within audit period Q(visit__systolic_blood_pressure__isnull=False), - Q( - visit__blood_pressure_observation_date__range=( - self.AUDIT_DATE_RANGE - ) - ), + Q(visit__blood_pressure_observation_date__range=(self.AUDIT_DATE_RANGE)), ) total_passed = total_passed_query_set.count() @@ -1612,11 +1602,7 @@ def calculate_kpi_29_urinary_albumin( total_passed_query_set = eligible_patients.filter( Q(visit__albumin_creatinine_ratio__isnull=False), # Within audit period - Q( - visit__albumin_creatinine_ratio_date__range=( - self.AUDIT_DATE_RANGE - ) - ), + Q(visit__albumin_creatinine_ratio_date__range=(self.AUDIT_DATE_RANGE)), ) total_passed = total_passed_query_set.count() @@ -1656,11 +1642,7 @@ def calculate_kpi_30_retinal_screening( ] ), # Within audit period - Q( - visit__retinal_screening_observation_date__range=( - self.AUDIT_DATE_RANGE - ) - ), + Q(visit__retinal_screening_observation_date__range=(self.AUDIT_DATE_RANGE)), ) total_passed = total_passed_query_set.count() @@ -1694,11 +1676,7 @@ def calculate_kpi_31_foot_examination( # Find patients with at least one for Foot Examination Date (item 26) within the audit period total_passed_query_set = eligible_patients.filter( # Within audit period - Q( - visit__foot_examination_observation_date__range=( - self.AUDIT_DATE_RANGE - ) - ), + Q(visit__foot_examination_observation_date__range=(self.AUDIT_DATE_RANGE)), ) total_passed = total_passed_query_set.count() @@ -1746,9 +1724,7 @@ def calculate_kpi_32_1_health_check_completion_rate( # Separate the patients into those < 12yo and those >= 12yo eligible_patients_lt_12yo = self._get_eligible_pts_measure_5_lt_12yo() - eligible_patients_gte_12yo = ( - self._get_eligible_pts_measure_5_gte_12yo() - ) + eligible_patients_gte_12yo = self._get_eligible_pts_measure_5_gte_12yo() # Count health checks for patients < 12yo # Involves looking at all their Visits, finding if at least 1 of each @@ -1789,12 +1765,10 @@ def calculate_kpi_32_1_health_check_completion_rate( ) # Annotate each check count and sum them up - actual_health_checks_lt_12yo = ( - annotated_eligible_pts_lt_12yo.aggregate( - total_hba1c_checks=Sum("hba1c_check"), - total_bmi_checks=Sum("bmi_check"), - total_thyroid_checks=Sum("thyroid_check"), - ) + actual_health_checks_lt_12yo = annotated_eligible_pts_lt_12yo.aggregate( + total_hba1c_checks=Sum("hba1c_check"), + total_bmi_checks=Sum("bmi_check"), + total_thyroid_checks=Sum("thyroid_check"), ) # Sum the counts to get the total health checks @@ -1877,15 +1851,13 @@ def calculate_kpi_32_1_health_check_completion_rate( ) # Annotate each check count and sum them up - actual_health_checks_gte_12yo = ( - annotated_eligible_pts_gte_12yo.aggregate( - total_hba1c_checks=Sum("hba1c_check"), - total_bmi_checks=Sum("bmi_check"), - total_thyroid_checks=Sum("thyroid_check"), - total_bp_checks=Sum("bp_check"), - total_urinary_albumin_checks=Sum("urinary_albumin_check"), - total_foot_exam_checks=Sum("foot_exam_check"), - ) + actual_health_checks_gte_12yo = annotated_eligible_pts_gte_12yo.aggregate( + total_hba1c_checks=Sum("hba1c_check"), + total_bmi_checks=Sum("bmi_check"), + total_thyroid_checks=Sum("thyroid_check"), + total_bp_checks=Sum("bp_check"), + total_urinary_albumin_checks=Sum("urinary_albumin_check"), + total_foot_exam_checks=Sum("foot_exam_check"), ) # Sum the counts to get the total health checks @@ -1914,8 +1886,7 @@ def calculate_kpi_32_1_health_check_completion_rate( total_eligible=expected_total_health_checks, total_ineligible=total_ineligible, total_passed=actual_health_checks_overall, - total_failed=expected_total_health_checks - - actual_health_checks_overall, + total_failed=expected_total_health_checks - actual_health_checks_overall, ) def calculate_kpi_32_2_health_check_lt_12yo(self) -> dict: @@ -1978,9 +1949,12 @@ def calculate_kpi_32_2_health_check_lt_12yo(self) -> dict: ), ) - total_passed = annotated_eligible_pts.aggregate( - total_pts_all_hcs_completed=Sum("all_3_hcs_completed") - ).get("total_pts_all_hcs_completed") or 0 + total_passed = ( + annotated_eligible_pts.aggregate( + total_pts_all_hcs_completed=Sum("all_3_hcs_completed") + ).get("total_pts_all_hcs_completed") + or 0 + ) return KPIResult( total_eligible=total_eligible, @@ -2084,9 +2058,12 @@ def calculate_kpi_32_3_health_check_gte_12yo(self) -> dict: ), ) - total_passed = annotated_eligible_pts.aggregate( - total_pts_all_hcs_completed=Sum("all_6_hcs_completed") - ).get("total_pts_all_hcs_completed") or 0 + total_passed = ( + annotated_eligible_pts.aggregate( + total_pts_all_hcs_completed=Sum("all_6_hcs_completed") + ).get("total_pts_all_hcs_completed") + or 0 + ) return KPIResult( total_eligible=total_eligible, @@ -2107,10 +2084,7 @@ def _get_eligible_pts_measure_5_lt_12yo(self): ) self.eligible_patients_lt_12yo = base_eligible_query_set.filter( - Q( - date_of_birth__gt=self.audit_start_date - - relativedelta(years=12) - ) + Q(date_of_birth__gt=self.audit_start_date - relativedelta(years=12)) ) return self.eligible_patients_lt_12yo @@ -2127,10 +2101,7 @@ def _get_eligible_pts_measure_5_gte_12yo(self): ) self.eligible_patients_gte_12yo = base_eligible_query_set.filter( - Q( - date_of_birth__lte=self.audit_start_date - - relativedelta(years=12) - ) + Q(date_of_birth__lte=self.audit_start_date - relativedelta(years=12)) ) return self.eligible_patients_gte_12yo @@ -2210,10 +2181,8 @@ def calculate_kpi_34_psychological_assessment( ), ) ) - total_passed_query_set = ( - eligible_pts_annotated_psych_screen_visits.filter( - psych_valid_visits__gte=1 - ) + total_passed_query_set = eligible_pts_annotated_psych_screen_visits.filter( + psych_valid_visits__gte=1 ) total_passed = total_passed_query_set.count() @@ -2262,10 +2231,8 @@ def calculate_kpi_35_smoking_status_screened( # would be founted. Exists() implementation here solved this. ) - total_passed_query_set = ( - eligible_pts_annotated_smoke_screen_visits.filter( - smoke_valid_visits__gte=1 - ) + total_passed_query_set = eligible_pts_annotated_smoke_screen_visits.filter( + smoke_valid_visits__gte=1 ) total_passed = total_passed_query_set.count() @@ -2303,18 +2270,12 @@ def calculate_kpi_36_referral_to_smoking_cessation_service( smoking_cessation_referral_date__range=self.AUDIT_DATE_RANGE, ) # Find patients with a valid entry for Smoking Cessation Referral - eligible_pts_annotated_smoke_screen_visits = ( - eligible_patients.annotate( - smoke_cessation_referral_valid_visits=Exists( - smoke_cessation_visits - ) - ) + eligible_pts_annotated_smoke_screen_visits = eligible_patients.annotate( + smoke_cessation_referral_valid_visits=Exists(smoke_cessation_visits) ) - total_passed_query_set = ( - eligible_pts_annotated_smoke_screen_visits.filter( - smoke_cessation_referral_valid_visits__gte=1 - ) + total_passed_query_set = eligible_pts_annotated_smoke_screen_visits.filter( + smoke_cessation_referral_valid_visits__gte=1 ) total_passed = total_passed_query_set.count() @@ -2346,21 +2307,17 @@ def calculate_kpi_37_additional_dietetic_appointment_offered( total_ineligible = self.total_patients_count - total_eligible # Find patients with at least one entry for Additional Dietitian Appointment Offered (item 43) that is 1 = Yes within the audit period (based on visit date) - eligible_pts_annotated_dietician_offered_visits = ( - eligible_patients.annotate( - dietician_offered_valid_visits=Count( - "visit", - filter=Q( - visit__visit_date__range=self.AUDIT_DATE_RANGE, - visit__dietician_additional_appointment_offered=1, - ), - ) + eligible_pts_annotated_dietician_offered_visits = eligible_patients.annotate( + dietician_offered_valid_visits=Count( + "visit", + filter=Q( + visit__visit_date__range=self.AUDIT_DATE_RANGE, + visit__dietician_additional_appointment_offered=1, + ), ) ) - total_passed_query_set = ( - eligible_pts_annotated_dietician_offered_visits.filter( - dietician_offered_valid_visits__gte=1 - ) + total_passed_query_set = eligible_pts_annotated_dietician_offered_visits.filter( + dietician_offered_valid_visits__gte=1 ) total_passed = total_passed_query_set.count() @@ -2447,8 +2404,10 @@ def calculate_kpi_39_influenza_immunisation_recommended( ), ) ) - total_passed_query_set = eligible_pts_annotated_flu_immunisation_recommended_date_visits.filter( - flu_immunisation_recommended_date_valid_visits__gte=1 + total_passed_query_set = ( + eligible_pts_annotated_flu_immunisation_recommended_date_visits.filter( + flu_immunisation_recommended_date_valid_visits__gte=1 + ) ) total_passed = total_passed_query_set.count() @@ -2491,10 +2450,8 @@ def calculate_kpi_40_sick_day_rules_advice( ), ) ) - total_passed_query_set = ( - eligible_pts_annotated_sick_day_rules_visits.filter( - sick_day_rules_valid_visits__gte=1 - ) + total_passed_query_set = eligible_pts_annotated_sick_day_rules_visits.filter( + sick_day_rules_valid_visits__gte=1 ) total_passed = total_passed_query_set.count() @@ -2540,10 +2497,8 @@ def calculate_kpi_41_coeliac_disease_screening( ), ) ) - total_passed_query_set = ( - eligible_pts_annotated_coeliac_screen_visits.filter( - coeliac_screen_valid_visits__gte=1 - ) + total_passed_query_set = eligible_pts_annotated_coeliac_screen_visits.filter( + coeliac_screen_valid_visits__gte=1 ) total_passed = total_passed_query_set.count() @@ -2587,10 +2542,8 @@ def calculate_kpi_42_thyroid_disease_screening( ), ) ) - total_passed_query_set = ( - eligible_pts_annotated_thyroid_fn_date_visits.filter( - thyroid_fn_date_valid_visits__gte=1 - ) + total_passed_query_set = eligible_pts_annotated_thyroid_fn_date_visits.filter( + thyroid_fn_date_valid_visits__gte=1 ) total_passed = total_passed_query_set.count() @@ -2653,9 +2606,7 @@ def calculate_kpi_43_carbohydrate_counting_education( ) # Filter patients who have at least one valid Visit - total_passed_query_set = eligible_pts_annotated.filter( - has_valid_visit=True - ) + total_passed_query_set = eligible_pts_annotated.filter(has_valid_visit=True) total_passed = total_passed_query_set.count() total_failed = total_eligible - total_passed @@ -2698,9 +2649,7 @@ def calculate_kpi_44_mean_hba1c( Visit.objects.filter( visit_date__range=self.AUDIT_DATE_RANGE, hba1c_date__gte=F("patient__diagnosis_date") - + timedelta( - days=90 - ), # Ensure HbA1c is taken >90 days after diagnosis + + timedelta(days=90), # Ensure HbA1c is taken >90 days after diagnosis patient=OuterRef("pk"), ) # Clear any implicit ordering, select only 'hba1c' for calculating @@ -2711,16 +2660,19 @@ def calculate_kpi_44_mean_hba1c( # Annotate eligible patients with the median HbA1c value eligible_pts_annotated = eligible_patients.annotate( median_hba1c=Subquery( - valid_hba1c_subquery.annotate( - median_hba1c=Median("hba1c") - ).values("median_hba1c")[:1] + valid_hba1c_subquery.annotate(median_hba1c=Median("hba1c")).values( + "median_hba1c" + )[:1] ) ) # Calculate the median of the medians and convert to float (as Decimal) - median_of_median_hba1cs = eligible_pts_annotated.aggregate( - median_of_median_hba1cs=Avg("median_hba1c") - ).get("median_of_median_hba1cs") or 0 + median_of_median_hba1cs = ( + eligible_pts_annotated.aggregate( + median_of_median_hba1cs=Avg("median_hba1c") + ).get("median_of_median_hba1cs") + or 0 + ) return KPIResult( total_eligible=total_eligible, @@ -2760,9 +2712,7 @@ def calculate_kpi_45_median_hba1c( Visit.objects.filter( visit_date__range=self.AUDIT_DATE_RANGE, hba1c_date__gte=F("patient__diagnosis_date") - + timedelta( - days=90 - ), # Ensure HbA1c is taken >90 days after diagnosis + + timedelta(days=90), # Ensure HbA1c is taken >90 days after diagnosis patient=OuterRef("pk"), ) # Clear any implicit ordering, select only 'hba1c' for calculating @@ -2773,16 +2723,19 @@ def calculate_kpi_45_median_hba1c( # Annotate eligible patients with the median HbA1c value eligible_pts_annotated = eligible_patients.annotate( median_hba1c=Subquery( - valid_hba1c_subquery.annotate( - median_hba1c=Median("hba1c") - ).values("median_hba1c")[:1] + valid_hba1c_subquery.annotate(median_hba1c=Median("hba1c")).values( + "median_hba1c" + )[:1] ) ) # Calculate the mean of the medians and convert to float (as Decimal) - mean_of_median_hba1cs = eligible_pts_annotated.aggregate( - mean_of_median_hba1cs=Avg("median_hba1c") - ).get("mean_of_median_hba1cs") or 0 + mean_of_median_hba1cs = ( + eligible_pts_annotated.aggregate( + mean_of_median_hba1cs=Avg("median_hba1c") + ).get("mean_of_median_hba1cs") + or 0 + ) return KPIResult( total_eligible=total_eligible, @@ -2837,9 +2790,7 @@ def calculate_kpi_46_number_of_admissions( ) # Filter patients who have at least one valid Visit - total_passed_query_set = eligible_pts_annotated.filter( - has_valid_visit=True - ) + total_passed_query_set = eligible_pts_annotated.filter(has_valid_visit=True) total_passed = total_passed_query_set.count() total_failed = total_eligible - total_passed @@ -2893,9 +2844,7 @@ def calculate_kpi_47_number_of_dka_admissions( ) # Filter patients who have at least one valid Visit - total_passed_query_set = eligible_pts_annotated.filter( - has_valid_visit=True - ) + total_passed_query_set = eligible_pts_annotated.filter(has_valid_visit=True) total_passed = total_passed_query_set.count() total_failed = total_eligible - total_passed @@ -2941,9 +2890,7 @@ def calculate_kpi_48_required_additional_psychological_support( ) # Filter patients who have at least one valid Visit - total_passed_query_set = eligible_pts_annotated.filter( - has_valid_visit=True - ) + total_passed_query_set = eligible_pts_annotated.filter(has_valid_visit=True) total_passed = total_passed_query_set.count() total_failed = total_eligible - total_passed @@ -2992,9 +2939,7 @@ def calculate_kpi_49_albuminuria_present( ) # Filter patients who have at least one valid Visit - total_passed_query_set = eligible_pts_annotated.filter( - has_valid_visit=True - ) + total_passed_query_set = eligible_pts_annotated.filter(has_valid_visit=True) total_passed = total_passed_query_set.count() total_failed = total_eligible - total_passed @@ -3158,11 +3103,8 @@ def _get_total_pts_new_t1dm_diag_90D_before_audit_end_base_query_set_and_total_c ) # Filter for those diagnoses at least 90 days before audit end date - self.t1dm_pts_diagnosed_90D_before_end_base_query_set = ( - base_query_set.filter( - diagnosis_date__lt=self.audit_end_date - - relativedelta(days=90), - ) + self.t1dm_pts_diagnosed_90D_before_end_base_query_set = base_query_set.filter( + diagnosis_date__lt=self.audit_end_date - relativedelta(days=90), ) self.t1dm_pts_diagnosed_90D_before_end_total_eligible = ( self.t1dm_pts_diagnosed_90D_before_end_base_query_set.count() @@ -3205,9 +3147,7 @@ def get(self, request, *args, **kwargs): pz_code = kwargs.get("pz_code", None) start_time = time.time() # Record the start time for calc - aggregated_data = CalculateKPIS( - pz_code=pz_code - ).calculate_kpis_for_patients() + aggregated_data = CalculateKPIS(pz_code=pz_code).calculate_kpis_for_patients() end_time = time.time() # Record the end time calculation_time = round( (end_time - start_time) * 1000, 2 diff --git a/project/npda/templates/partials/kpi_patient.html b/project/npda/templates/partials/kpi_patient.html new file mode 100644 index 00000000..90a44ae2 --- /dev/null +++ b/project/npda/templates/partials/kpi_patient.html @@ -0,0 +1,145 @@ +
+ + Key Performance Indicators for {{patient.nhs_number}} ({{ kpi_results.pz_code }}) + + + + + + + + + + {% for kpi_key, kpi_value in kpi_results.calculated_kpi_values.items %} + {% if not kpi_key|slice:":6" == "kpi_32" %} + + + + + {% endif %} + {% endfor %} + + + + + + + + + + + + + + + + + + +
KPIStatus
{{ kpi_value.kpi_label }} + {% if kpi_value.total_failed == 1 %} + + {% elif kpi_value.total_passed == 1 %} + + + + + + {% elif kpi_value.total_ineligible == 1 %} + + + + + + + {% else %} + + + + {% endif %} +
KPI 32 + {% if kpi_results.calculated_kpi_values.kpi_32_1_health_check_completion_rate.total_failed == 1 %} + + {% elif kpi_results.calculated_kpi_values.kpi_32_1_health_check_completion_rate.total_passed == 1 %} + + + + + {% elif kpi_results.calculated_kpi_values.kpi_32_1_health_check_completion_rate.total_ineligible == 1 %} + + + + + + + {% else %} + + + + {% endif %} +
Care Process Completion Rate + {% if kpi_results.calculated_kpi_values.kpi_32_1_health_check_completion_rate.total_failed == 1 %} + + {% elif kpi_results.calculated_kpi_values.kpi_32_1_health_check_completion_rate.total_passed == 1 %} + + + + + + {% elif kpi_results.calculated_kpi_values.kpi_32_1_health_check_completion_rate.total_ineligible == 1 %} + + + + + + + {% else %} + + + + {% endif %} +
Health Check < 12yo + {% if kpi_results.calculated_kpi_values.kpi_32_2_health_check_lt_12yo.total_failed == 1 %} + + {% elif kpi_results.calculated_kpi_values.kpi_32_2_health_check_lt_12yo.total_passed == 1 %} + + + + + + {% elif kpi_results.calculated_kpi_values.kpi_32_2_health_check_lt_12yo.total_ineligible == 1 %} + + + + + + + {% else %} + + + + {% endif %} +
Health Check >= 12yo + {% if kpi_results.calculated_kpi_values.kpi_32_3_health_check_gte_12yo.total_failed == 1 %} + + {% elif kpi_results.calculated_kpi_values.kpi_32_3_health_check_gte_12yo.total_passed == 1 %} + + + + + + {% elif kpi_results.calculated_kpi_values.kpi_32_3_health_check_gte_12yo.total_ineligible == 1 %} + + + + + + + {% else %} + + + + {% endif %} +
+
\ No newline at end of file diff --git a/project/npda/templates/visits.html b/project/npda/templates/visits.html index 7af39d22..25806f79 100644 --- a/project/npda/templates/visits.html +++ b/project/npda/templates/visits.html @@ -97,8 +97,8 @@
This child has had no visits yet!
-
- Patient KPIs will go here... +
+ {% include 'partials/kpi_patient.html' with kpi_results=kpi_results patient=patient %}
diff --git a/project/npda/tests/kpi_calculations/test_kpi_calculations.py b/project/npda/tests/kpi_calculations/test_kpi_calculations.py index e5b2f02e..a9e97e32 100644 --- a/project/npda/tests/kpi_calculations/test_kpi_calculations.py +++ b/project/npda/tests/kpi_calculations/test_kpi_calculations.py @@ -7,7 +7,7 @@ import pytest -from project.npda.general_functions.kpis import CalculateKPIS, KPIResult +from project.npda.kpi_class.kpis import CalculateKPIS, KPIResult from project.npda.models.patient import Patient # Logging @@ -29,9 +29,7 @@ def assert_kpi_result_equal(expected: KPIResult, actual: KPIResult) -> None: f"expected must be of type KPIResult (current: {type(expected)}" ) if isinstance(actual, KPIResult) is False: - raise TypeError( - f"actual must be of type KPIResult (current: {type(actual)}" - ) + raise TypeError(f"actual must be of type KPIResult (current: {type(actual)}") mismatches = [] @@ -90,13 +88,12 @@ def test_kpi_calculations_dont_break_when_no_patients(AUDIT_START_DATE): pz_code="PZ130", calculation_date=AUDIT_START_DATE ).calculate_kpis_for_patients() - for kpi, results in kpi_calculations_object[ - "calculated_kpi_values" - ].items(): + for kpi, results in kpi_calculations_object["calculated_kpi_values"].items(): + # remove the kpi_label key from the results + results.pop("kpi_label", None) + values = list(results.values()) + assert all( - [ - isinstance(value, int) or isinstance(value, float) - for value in values - ] + [isinstance(value, int) or isinstance(value, float) for value in values] ), f"KPI {kpi} has non-integer values: {results}" diff --git a/project/npda/tests/kpi_calculations/test_kpis_13_20.py b/project/npda/tests/kpi_calculations/test_kpis_13_20.py index 423592ae..66f4ac23 100644 --- a/project/npda/tests/kpi_calculations/test_kpis_13_20.py +++ b/project/npda/tests/kpi_calculations/test_kpis_13_20.py @@ -1,13 +1,15 @@ """Tests for the Treatment Regimen KPIS.""" + import pytest from dateutil.relativedelta import relativedelta from project.constants.diabetes_treatment import TREATMENT_TYPES -from project.npda.general_functions.kpis import CalculateKPIS, KPIResult +from project.npda.kpi_class.kpis import CalculateKPIS, KPIResult from project.npda.models import Patient from project.npda.tests.factories.patient_factory import PatientFactory -from project.npda.tests.kpi_calculations.test_kpi_calculations import \ - assert_kpi_result_equal +from project.npda.tests.kpi_calculations.test_kpi_calculations import ( + assert_kpi_result_equal, +) # Set up test params for kpis 13-20, as they all have the same denominator # and the only thing being changed is value for visit__treatment @@ -77,9 +79,7 @@ def test_kpi_calculations_13_to_20( ) # The default pz_code is "PZ130" for PaediatricsDiabetesUnitFactory - calc_kpis = CalculateKPIS( - pz_code="PZ130", calculation_date=AUDIT_START_DATE - ) + calc_kpis = CalculateKPIS(pz_code="PZ130", calculation_date=AUDIT_START_DATE) # Dynamically get the kpi calc method based on treatment type # `treatment` is an int between 1-8 diff --git a/project/npda/tests/kpi_calculations/test_kpis_1_12.py b/project/npda/tests/kpi_calculations/test_kpis_1_12.py index a9b0f233..5e560144 100644 --- a/project/npda/tests/kpi_calculations/test_kpis_1_12.py +++ b/project/npda/tests/kpi_calculations/test_kpis_1_12.py @@ -6,12 +6,13 @@ from dateutil.relativedelta import relativedelta from project.constants.diabetes_types import DIABETES_TYPES -from project.npda.general_functions.kpis import CalculateKPIS, KPIResult +from project.npda.kpi_class.kpis import CalculateKPIS, KPIResult from project.npda.models import Patient from project.npda.tests.factories.patient_factory import PatientFactory from project.npda.tests.factories.visit_factory import VisitFactory -from project.npda.tests.kpi_calculations.test_kpi_calculations import \ - assert_kpi_result_equal +from project.npda.tests.kpi_calculations.test_kpi_calculations import ( + assert_kpi_result_equal, +) @pytest.mark.django_db @@ -33,11 +34,9 @@ def test_kpi_calculation_1(AUDIT_START_DATE): # Create Patients and Visits that should FAIL KPI1 # Visit date before audit period - ineligible_patients_visit_date: List[Patient] = ( - PatientFactory.create_batch( - size=N_PATIENTS_INELIGIBLE, - visit__visit_date=AUDIT_START_DATE - relativedelta(days=10), - ) + ineligible_patients_visit_date: List[Patient] = PatientFactory.create_batch( + size=N_PATIENTS_INELIGIBLE, + visit__visit_date=AUDIT_START_DATE - relativedelta(days=10), ) # Above age 25 at start of audit period ineligible_patients_too_old: List[Patient] = PatientFactory.create_batch( @@ -46,9 +45,7 @@ def test_kpi_calculation_1(AUDIT_START_DATE): ) # The default pz_code is "PZ130" for PaediatricsDiabetesUnitFactory - calc_kpis = CalculateKPIS( - pz_code="PZ130", calculation_date=AUDIT_START_DATE - ) + calc_kpis = CalculateKPIS(pz_code="PZ130", calculation_date=AUDIT_START_DATE) EXPECTED_KPIRESULT = KPIResult( total_eligible=N_PATIENTS_ELIGIBLE, @@ -87,18 +84,14 @@ def test_kpi_calculation_2(AUDIT_START_DATE): # Create Patients and Visits that should FAIL KPI2 # Visit date before audit period - ineligible_patients_visit_date: List[Patient] = ( - PatientFactory.create_batch( - size=N_PATIENTS_INELIGIBLE, - visit__visit_date=AUDIT_START_DATE - relativedelta(days=10), - ) + ineligible_patients_visit_date: List[Patient] = PatientFactory.create_batch( + size=N_PATIENTS_INELIGIBLE, + visit__visit_date=AUDIT_START_DATE - relativedelta(days=10), ) # Diagnosis date before audit period - ineligible_patients_diagnosis_date: List[Patient] = ( - PatientFactory.create_batch( - size=N_PATIENTS_INELIGIBLE, - diagnosis_date=AUDIT_START_DATE - relativedelta(days=10), - ) + ineligible_patients_diagnosis_date: List[Patient] = PatientFactory.create_batch( + size=N_PATIENTS_INELIGIBLE, + diagnosis_date=AUDIT_START_DATE - relativedelta(days=10), ) # Above age 25 at start of audit period ineligible_patients_too_old: List[Patient] = PatientFactory.create_batch( @@ -107,9 +100,7 @@ def test_kpi_calculation_2(AUDIT_START_DATE): ) # The default pz_code is "PZ130" for PaediatricsDiabetesUnitFactory - calc_kpis = CalculateKPIS( - pz_code="PZ130", calculation_date=AUDIT_START_DATE - ) + calc_kpis = CalculateKPIS(pz_code="PZ130", calculation_date=AUDIT_START_DATE) EXPECTED_KPIRESULT = KPIResult( total_eligible=N_PATIENTS_ELIGIBLE, @@ -150,11 +141,9 @@ def test_kpi_calculation_3(AUDIT_START_DATE): # Create Patients and Visits that should FAIL KPI3 # Visit date before audit period - ineligible_patients_visit_date: List[Patient] = ( - PatientFactory.create_batch( - size=N_PATIENTS_INELIGIBLE, - visit__visit_date=AUDIT_START_DATE - relativedelta(days=10), - ) + ineligible_patients_visit_date: List[Patient] = PatientFactory.create_batch( + size=N_PATIENTS_INELIGIBLE, + visit__visit_date=AUDIT_START_DATE - relativedelta(days=10), ) # Above age 25 at start of audit period ineligible_patients_too_old: List[Patient] = PatientFactory.create_batch( @@ -167,9 +156,7 @@ def test_kpi_calculation_3(AUDIT_START_DATE): ) # The default pz_code is "PZ130" for PaediatricsDiabetesUnitFactory - calc_kpis = CalculateKPIS( - pz_code="PZ130", calculation_date=AUDIT_START_DATE - ) + calc_kpis = CalculateKPIS(pz_code="PZ130", calculation_date=AUDIT_START_DATE) EXPECTED_KPIRESULT = KPIResult( total_eligible=N_PATIENTS_ELIGIBLE, @@ -213,11 +200,9 @@ def test_kpi_calculation_4(AUDIT_START_DATE): # Create Patients and Visits that should FAIL KPI4 # Visit date before audit period - ineligible_patients_visit_date: List[Patient] = ( - PatientFactory.create_batch( - size=N_PATIENTS_INELIGIBLE, - visit__visit_date=AUDIT_START_DATE - relativedelta(days=10), - ) + ineligible_patients_visit_date: List[Patient] = PatientFactory.create_batch( + size=N_PATIENTS_INELIGIBLE, + visit__visit_date=AUDIT_START_DATE - relativedelta(days=10), ) # Above age 25 at start of audit period ineligible_patients_too_old: List[Patient] = PatientFactory.create_batch( @@ -235,9 +220,7 @@ def test_kpi_calculation_4(AUDIT_START_DATE): ) # The default pz_code is "PZ130" for PaediatricsDiabetesUnitFactory - calc_kpis = CalculateKPIS( - pz_code="PZ130", calculation_date=AUDIT_START_DATE - ) + calc_kpis = CalculateKPIS(pz_code="PZ130", calculation_date=AUDIT_START_DATE) EXPECTED_KPIRESULT = KPIResult( total_eligible=N_PATIENTS_ELIGIBLE, @@ -324,8 +307,7 @@ def test_kpi_calculation_5(AUDIT_START_DATE): visit__visit_date=AUDIT_START_DATE + relativedelta(days=2), date_of_birth=AUDIT_START_DATE - relativedelta(days=365 * 10), # Date of leaving service within the audit period - transfer__date_leaving_service=AUDIT_START_DATE - + relativedelta(days=2), + transfer__date_leaving_service=AUDIT_START_DATE + relativedelta(days=2), ) ineligible_patient_death_within_audit_period = PatientFactory( postcode="ineligible_patient_death_within_audit_period", @@ -337,9 +319,7 @@ def test_kpi_calculation_5(AUDIT_START_DATE): ) # The default pz_code is "PZ130" for PaediatricsDiabetesUnitFactory - calc_kpis = CalculateKPIS( - pz_code="PZ130", calculation_date=AUDIT_START_DATE - ) + calc_kpis = CalculateKPIS(pz_code="PZ130", calculation_date=AUDIT_START_DATE) EXPECTED_TOTAL_ELIGIBLE = 3 EXPECTED_TOTAL_INELIGIBLE = 5 @@ -400,8 +380,7 @@ def test_kpi_calculation_6(AUDIT_START_DATE): diabetes_type=DIABETES_TYPES[0][0], # an observation within the audit period **{ - f"visit__{field_name}": AUDIT_START_DATE - + relativedelta(days=2), + f"visit__{field_name}": AUDIT_START_DATE + relativedelta(days=2), f"visit__visit_date": AUDIT_START_DATE + relativedelta(days=2), }, ) @@ -430,8 +409,7 @@ def test_kpi_calculation_6(AUDIT_START_DATE): VisitFactory( patient=eligible_patient_second_visit_observation, visit_date=AUDIT_START_DATE + relativedelta(months=2), - height_weight_observation_date=AUDIT_START_DATE - + relativedelta(months=2), + height_weight_observation_date=AUDIT_START_DATE + relativedelta(months=2), psychological_screening_assessment_date=AUDIT_START_DATE + relativedelta(months=2), ) @@ -451,8 +429,7 @@ def test_kpi_calculation_6(AUDIT_START_DATE): visit__visit_date=AUDIT_START_DATE + relativedelta(days=2), date_of_birth=AUDIT_START_DATE - relativedelta(days=365 * 10), # Date of leaving service within the audit period - transfer__date_leaving_service=AUDIT_START_DATE - + relativedelta(days=2), + transfer__date_leaving_service=AUDIT_START_DATE + relativedelta(days=2), ) ineligible_patient_death_within_audit_period = PatientFactory( postcode="ineligible_patient_death_within_audit_period", @@ -464,9 +441,7 @@ def test_kpi_calculation_6(AUDIT_START_DATE): ) # The default pz_code is "PZ130" for PaediatricsDiabetesUnitFactory - calc_kpis = CalculateKPIS( - pz_code="PZ130", calculation_date=AUDIT_START_DATE - ) + calc_kpis = CalculateKPIS(pz_code="PZ130", calculation_date=AUDIT_START_DATE) EXPECTED_TOTAL_ELIGIBLE = len(observation_field_names) + 1 EXPECTED_TOTAL_INELIGIBLE = 3 @@ -523,10 +498,7 @@ def test_kpi_calculation_7(AUDIT_START_DATE): # Diagnosis date within audit date range diagnosis_date=AUDIT_START_DATE + relativedelta(days=2), # an observation within the audit period - **{ - f"visit__{field_name}": AUDIT_START_DATE - + relativedelta(days=2) - }, + **{f"visit__{field_name}": AUDIT_START_DATE + relativedelta(days=2)}, ) # Create Patients and Visits that should BE EXCLUDED @@ -550,9 +522,7 @@ def test_kpi_calculation_7(AUDIT_START_DATE): ) # The default pz_code is "PZ130" for PaediatricsDiabetesUnitFactory - calc_kpis = CalculateKPIS( - pz_code="PZ130", calculation_date=AUDIT_START_DATE - ) + calc_kpis = CalculateKPIS(pz_code="PZ130", calculation_date=AUDIT_START_DATE) EXPECTED_TOTAL_ELIGIBLE = len(observation_field_names) EXPECTED_TOTAL_INELIGIBLE = 2 @@ -614,9 +584,7 @@ def test_kpi_calculation_8(AUDIT_START_DATE): ) # The default pz_code is "PZ130" for PaediatricsDiabetesUnitFactory - calc_kpis = CalculateKPIS( - pz_code="PZ130", calculation_date=AUDIT_START_DATE - ) + calc_kpis = CalculateKPIS(pz_code="PZ130", calculation_date=AUDIT_START_DATE) EXPECTED_TOTAL_ELIGIBLE = 1 EXPECTED_TOTAL_INELIGIBLE = 3 @@ -652,8 +620,7 @@ def test_kpi_calculation_9(AUDIT_START_DATE): visit__visit_date=AUDIT_START_DATE + relativedelta(days=2), date_of_birth=AUDIT_START_DATE - relativedelta(days=365 * 10), # leaving_date within the audit period - transfer__date_leaving_service=AUDIT_START_DATE - + relativedelta(days=2), + transfer__date_leaving_service=AUDIT_START_DATE + relativedelta(days=2), ) # Create Patients and Visits that should be excluded @@ -683,14 +650,11 @@ def test_kpi_calculation_9(AUDIT_START_DATE): visit__visit_date=AUDIT_START_DATE + relativedelta(days=2), date_of_birth=AUDIT_START_DATE - relativedelta(days=365 * 10), # Date of leaving_date outside the audit period" - transfer__date_leaving_service=AUDIT_START_DATE - - relativedelta(days=2), + transfer__date_leaving_service=AUDIT_START_DATE - relativedelta(days=2), ) # The default pz_code is "PZ130" for PaediatricsDiabetesUnitFactory - calc_kpis = CalculateKPIS( - pz_code="PZ130", calculation_date=AUDIT_START_DATE - ) + calc_kpis = CalculateKPIS(pz_code="PZ130", calculation_date=AUDIT_START_DATE) EXPECTED_TOTAL_ELIGIBLE = 1 EXPECTED_TOTAL_INELIGIBLE = 4 @@ -753,9 +717,7 @@ def test_kpi_calculation_10(AUDIT_START_DATE): ) # The default pz_code is "PZ130" for PaediatricsDiabetesUnitFactory - calc_kpis = CalculateKPIS( - pz_code="PZ130", calculation_date=AUDIT_START_DATE - ) + calc_kpis = CalculateKPIS(pz_code="PZ130", calculation_date=AUDIT_START_DATE) EXPECTED_TOTAL_ELIGIBLE = 1 EXPECTED_TOTAL_INELIGIBLE = 3 @@ -827,9 +789,7 @@ def test_kpi_calculation_11(AUDIT_START_DATE): ) # The default pz_code is "PZ130" for PaediatricsDiabetesUnitFactory - calc_kpis = CalculateKPIS( - pz_code="PZ130", calculation_date=AUDIT_START_DATE - ) + calc_kpis = CalculateKPIS(pz_code="PZ130", calculation_date=AUDIT_START_DATE) EXPECTED_TOTAL_ELIGIBLE = 2 EXPECTED_TOTAL_INELIGIBLE = 3 @@ -892,9 +852,7 @@ def test_kpi_calculation_12(AUDIT_START_DATE): ) # The default pz_code is "PZ130" for PaediatricsDiabetesUnitFactory - calc_kpis = CalculateKPIS( - pz_code="PZ130", calculation_date=AUDIT_START_DATE - ) + calc_kpis = CalculateKPIS(pz_code="PZ130", calculation_date=AUDIT_START_DATE) EXPECTED_TOTAL_ELIGIBLE = 1 EXPECTED_TOTAL_INELIGIBLE = 3 diff --git a/project/npda/tests/kpi_calculations/test_kpis_21_23.py b/project/npda/tests/kpi_calculations/test_kpis_21_23.py index 555b6138..53ec3b18 100644 --- a/project/npda/tests/kpi_calculations/test_kpis_21_23.py +++ b/project/npda/tests/kpi_calculations/test_kpis_21_23.py @@ -1,12 +1,14 @@ """Tests for the Glucose Monitoring KPIS.""" + import pytest from dateutil.relativedelta import relativedelta -from project.npda.general_functions.kpis import CalculateKPIS, KPIResult +from project.npda.kpi_class.kpis import CalculateKPIS, KPIResult from project.npda.models import Patient from project.npda.tests.factories.patient_factory import PatientFactory -from project.npda.tests.kpi_calculations.test_kpi_calculations import \ - assert_kpi_result_equal +from project.npda.tests.kpi_calculations.test_kpi_calculations import ( + assert_kpi_result_equal, +) @pytest.mark.django_db @@ -62,9 +64,7 @@ def test_kpi_calculation_21(AUDIT_START_DATE): ) # The default pz_code is "PZ130" for PaediatricsDiabetesUnitFactory - calc_kpis = CalculateKPIS( - pz_code="PZ130", calculation_date=AUDIT_START_DATE - ) + calc_kpis = CalculateKPIS(pz_code="PZ130", calculation_date=AUDIT_START_DATE) EXPECTED_TOTAL_ELIGIBLE = 6 EXPECTED_TOTAL_INELIGIBLE = 2 @@ -131,9 +131,7 @@ def test_kpi_calculation_22(AUDIT_START_DATE): ) # The default pz_code is "PZ130" for PaediatricsDiabetesUnitFactory - calc_kpis = CalculateKPIS( - pz_code="PZ130", calculation_date=AUDIT_START_DATE - ) + calc_kpis = CalculateKPIS(pz_code="PZ130", calculation_date=AUDIT_START_DATE) EXPECTED_TOTAL_ELIGIBLE = 6 EXPECTED_TOTAL_INELIGIBLE = 2 @@ -202,9 +200,7 @@ def test_kpi_calculation_23(AUDIT_START_DATE): ) # The default pz_code is "PZ130" for PaediatricsDiabetesUnitFactory - calc_kpis = CalculateKPIS( - pz_code="PZ130", calculation_date=AUDIT_START_DATE - ) + calc_kpis = CalculateKPIS(pz_code="PZ130", calculation_date=AUDIT_START_DATE) EXPECTED_TOTAL_ELIGIBLE = 6 EXPECTED_TOTAL_INELIGIBLE = 2 diff --git a/project/npda/tests/kpi_calculations/test_kpis_24.py b/project/npda/tests/kpi_calculations/test_kpis_24.py index f5cf1b70..0c32c765 100644 --- a/project/npda/tests/kpi_calculations/test_kpis_24.py +++ b/project/npda/tests/kpi_calculations/test_kpis_24.py @@ -1,12 +1,14 @@ """Tests for the HCL KPI.""" + import pytest from dateutil.relativedelta import relativedelta -from project.npda.general_functions.kpis import CalculateKPIS, KPIResult +from project.npda.kpi_class.kpis import CalculateKPIS, KPIResult from project.npda.models import Patient from project.npda.tests.factories.patient_factory import PatientFactory -from project.npda.tests.kpi_calculations.test_kpi_calculations import \ - assert_kpi_result_equal +from project.npda.tests.kpi_calculations.test_kpi_calculations import ( + assert_kpi_result_equal, +) @pytest.mark.django_db @@ -103,9 +105,7 @@ def test_kpi_calculation_24(AUDIT_START_DATE): ) # The default pz_code is "PZ130" for PaediatricsDiabetesUnitFactory - calc_kpis = CalculateKPIS( - pz_code="PZ130", calculation_date=AUDIT_START_DATE - ) + calc_kpis = CalculateKPIS(pz_code="PZ130", calculation_date=AUDIT_START_DATE) EXPECTED_TOTAL_ELIGIBLE = 8 EXPECTED_TOTAL_INELIGIBLE = 8 diff --git a/project/npda/tests/kpi_calculations/test_kpis_25_32.py b/project/npda/tests/kpi_calculations/test_kpis_25_32.py index af594940..d1b65abd 100644 --- a/project/npda/tests/kpi_calculations/test_kpis_25_32.py +++ b/project/npda/tests/kpi_calculations/test_kpis_25_32.py @@ -5,14 +5,14 @@ from project.constants.diabetes_types import DIABETES_TYPES from project.constants.hba1c_format import HBA1C_FORMATS -from project.constants.retinal_screening_results import \ - RETINAL_SCREENING_RESULTS -from project.npda.general_functions.kpis import CalculateKPIS, KPIResult +from project.constants.retinal_screening_results import RETINAL_SCREENING_RESULTS +from project.npda.kpi_class.kpis import CalculateKPIS, KPIResult from project.npda.models import Patient from project.npda.tests.factories.patient_factory import PatientFactory from project.npda.tests.factories.visit_factory import VisitFactory -from project.npda.tests.kpi_calculations.test_kpi_calculations import \ - assert_kpi_result_equal +from project.npda.tests.kpi_calculations.test_kpi_calculations import ( + assert_kpi_result_equal, +) @pytest.mark.django_db @@ -104,8 +104,7 @@ def test_kpi_calculation_25(AUDIT_START_DATE): visit__visit_date=AUDIT_START_DATE + relativedelta(days=2), date_of_birth=AUDIT_START_DATE - relativedelta(days=365 * 10), # Date of leaving service within the audit period - transfer__date_leaving_service=AUDIT_START_DATE - + relativedelta(days=2), + transfer__date_leaving_service=AUDIT_START_DATE + relativedelta(days=2), ) ineligible_patient_death_within_audit_period = PatientFactory( postcode="ineligible_patient_death_within_audit_period", @@ -117,9 +116,7 @@ def test_kpi_calculation_25(AUDIT_START_DATE): ) # The default pz_code is "PZ130" for PaediatricsDiabetesUnitFactory - calc_kpis = CalculateKPIS( - pz_code="PZ130", calculation_date=AUDIT_START_DATE - ) + calc_kpis = CalculateKPIS(pz_code="PZ130", calculation_date=AUDIT_START_DATE) EXPECTED_TOTAL_ELIGIBLE = 3 EXPECTED_TOTAL_INELIGIBLE = 5 @@ -174,8 +171,7 @@ def test_kpi_calculation_26(AUDIT_START_DATE): # valid ht wt values within audit period visit__height=140.0, visit__weight=40.0, - visit__height_weight_observation_date=AUDIT_START_DATE - + relativedelta(days=2), + visit__height_weight_observation_date=AUDIT_START_DATE + relativedelta(days=2), ) passing_patient_valid_ht_wt_within_audit_period_2 = PatientFactory( postcode="passing_patient_valid_ht_wt_within_audit_period_2", @@ -184,8 +180,7 @@ def test_kpi_calculation_26(AUDIT_START_DATE): # valid ht wt values within audit period visit__height=160.0, visit__weight=50.0, - visit__height_weight_observation_date=AUDIT_START_DATE - + relativedelta(days=2), + visit__height_weight_observation_date=AUDIT_START_DATE + relativedelta(days=2), ) # Failing patients @@ -207,8 +202,7 @@ def test_kpi_calculation_26(AUDIT_START_DATE): # ht wt value before audit period visit__height=160.0, visit__weight=50.0, - visit__height_weight_observation_date=AUDIT_START_DATE - - relativedelta(days=2), + visit__height_weight_observation_date=AUDIT_START_DATE - relativedelta(days=2), ) # Create Patients and Visits that should be ineligble @@ -239,8 +233,7 @@ def test_kpi_calculation_26(AUDIT_START_DATE): visit__visit_date=AUDIT_START_DATE + relativedelta(days=2), date_of_birth=AUDIT_START_DATE - relativedelta(days=365 * 10), # Date of leaving service within the audit period - transfer__date_leaving_service=AUDIT_START_DATE - + relativedelta(days=2), + transfer__date_leaving_service=AUDIT_START_DATE + relativedelta(days=2), ) ineligible_patient_death_within_audit_period = PatientFactory( postcode="ineligible_patient_death_within_audit_period", @@ -252,9 +245,7 @@ def test_kpi_calculation_26(AUDIT_START_DATE): ) # The default pz_code is "PZ130" for PaediatricsDiabetesUnitFactory - calc_kpis = CalculateKPIS( - pz_code="PZ130", calculation_date=AUDIT_START_DATE - ) + calc_kpis = CalculateKPIS(pz_code="PZ130", calculation_date=AUDIT_START_DATE) EXPECTED_TOTAL_ELIGIBLE = 4 EXPECTED_TOTAL_INELIGIBLE = 5 @@ -363,8 +354,7 @@ def test_kpi_calculation_27(AUDIT_START_DATE): visit__visit_date=AUDIT_START_DATE + relativedelta(days=2), date_of_birth=AUDIT_START_DATE - relativedelta(days=365 * 10), # Date of leaving service within the audit period - transfer__date_leaving_service=AUDIT_START_DATE - + relativedelta(days=2), + transfer__date_leaving_service=AUDIT_START_DATE + relativedelta(days=2), ) ineligible_patient_death_within_audit_period = PatientFactory( postcode="ineligible_patient_death_within_audit_period", @@ -376,9 +366,7 @@ def test_kpi_calculation_27(AUDIT_START_DATE): ) # The default pz_code is "PZ130" for PaediatricsDiabetesUnitFactory - calc_kpis = CalculateKPIS( - pz_code="PZ130", calculation_date=AUDIT_START_DATE - ) + calc_kpis = CalculateKPIS(pz_code="PZ130", calculation_date=AUDIT_START_DATE) EXPECTED_TOTAL_ELIGIBLE = 4 EXPECTED_TOTAL_INELIGIBLE = 5 @@ -440,8 +428,7 @@ def test_kpi_calculation_28(AUDIT_START_DATE): **eligible_criteria, # valid bp date within audit period visit__systolic_blood_pressure=120, - visit__blood_pressure_observation_date=AUDIT_START_DATE - + relativedelta(days=2), + visit__blood_pressure_observation_date=AUDIT_START_DATE + relativedelta(days=2), ) passing_patient_systolic_bp_within_audit_period_2 = PatientFactory( postcode="passing_patient_systolic_bp_within_audit_period_2", @@ -449,8 +436,7 @@ def test_kpi_calculation_28(AUDIT_START_DATE): **eligible_criteria, # valid bp date within audit period visit__systolic_blood_pressure=130, - visit__blood_pressure_observation_date=AUDIT_START_DATE - + relativedelta(days=5), + visit__blood_pressure_observation_date=AUDIT_START_DATE + relativedelta(days=5), ) # Failing patients @@ -470,8 +456,7 @@ def test_kpi_calculation_28(AUDIT_START_DATE): **eligible_criteria, # systolic date before audit period visit__systolic_blood_pressure=120, - visit__blood_pressure_observation_date=AUDIT_START_DATE - - relativedelta(days=2), + visit__blood_pressure_observation_date=AUDIT_START_DATE - relativedelta(days=2), ) # Create Patients and Visits that should be ineligble @@ -489,8 +474,7 @@ def test_kpi_calculation_28(AUDIT_START_DATE): visit__visit_date=AUDIT_START_DATE + relativedelta(days=2), date_of_birth=AUDIT_START_DATE - relativedelta(days=365 * 10), # Date of leaving service within the audit period - transfer__date_leaving_service=AUDIT_START_DATE - + relativedelta(days=2), + transfer__date_leaving_service=AUDIT_START_DATE + relativedelta(days=2), ) ineligible_patient_death_within_audit_period = PatientFactory( postcode="ineligible_patient_death_within_audit_period", @@ -502,9 +486,7 @@ def test_kpi_calculation_28(AUDIT_START_DATE): ) # The default pz_code is "PZ130" for PaediatricsDiabetesUnitFactory - calc_kpis = CalculateKPIS( - pz_code="PZ130", calculation_date=AUDIT_START_DATE - ) + calc_kpis = CalculateKPIS(pz_code="PZ130", calculation_date=AUDIT_START_DATE) EXPECTED_TOTAL_ELIGIBLE = 4 EXPECTED_TOTAL_INELIGIBLE = 3 @@ -564,8 +546,7 @@ def test_kpi_calculation_29(AUDIT_START_DATE): **eligible_criteria, # valid ACR within audit period visit__albumin_creatinine_ratio=2, - visit__albumin_creatinine_ratio_date=AUDIT_START_DATE - + relativedelta(days=2), + visit__albumin_creatinine_ratio_date=AUDIT_START_DATE + relativedelta(days=2), ) passing_patient_urinary_albumin_within_audit_period_2 = PatientFactory( postcode="passing_patient_urinary_albumin_within_audit_period_2", @@ -573,8 +554,7 @@ def test_kpi_calculation_29(AUDIT_START_DATE): **eligible_criteria, # valid ACR within audit period visit__albumin_creatinine_ratio=3, - visit__albumin_creatinine_ratio_date=AUDIT_START_DATE - + relativedelta(days=27), + visit__albumin_creatinine_ratio_date=AUDIT_START_DATE + relativedelta(days=27), ) # Failing patients @@ -594,8 +574,7 @@ def test_kpi_calculation_29(AUDIT_START_DATE): **eligible_criteria, # ACR before audit period visit__albumin_creatinine_ratio=3, - visit__albumin_creatinine_ratio_date=AUDIT_START_DATE - - relativedelta(days=2), + visit__albumin_creatinine_ratio_date=AUDIT_START_DATE - relativedelta(days=2), ) # Create Patients and Visits that should be ineligble @@ -613,8 +592,7 @@ def test_kpi_calculation_29(AUDIT_START_DATE): visit__visit_date=AUDIT_START_DATE + relativedelta(days=2), date_of_birth=AUDIT_START_DATE - relativedelta(days=365 * 10), # Date of leaving service within the audit period - transfer__date_leaving_service=AUDIT_START_DATE - + relativedelta(days=2), + transfer__date_leaving_service=AUDIT_START_DATE + relativedelta(days=2), ) ineligible_patient_death_within_audit_period = PatientFactory( postcode="ineligible_patient_death_within_audit_period", @@ -626,9 +604,7 @@ def test_kpi_calculation_29(AUDIT_START_DATE): ) # The default pz_code is "PZ130" for PaediatricsDiabetesUnitFactory - calc_kpis = CalculateKPIS( - pz_code="PZ130", calculation_date=AUDIT_START_DATE - ) + calc_kpis = CalculateKPIS(pz_code="PZ130", calculation_date=AUDIT_START_DATE) EXPECTED_TOTAL_ELIGIBLE = 4 EXPECTED_TOTAL_INELIGIBLE = 3 @@ -746,8 +722,7 @@ def test_kpi_calculation_30(AUDIT_START_DATE): visit__visit_date=AUDIT_START_DATE + relativedelta(days=2), date_of_birth=AUDIT_START_DATE - relativedelta(days=365 * 10), # Date of leaving service within the audit period - transfer__date_leaving_service=AUDIT_START_DATE - + relativedelta(days=2), + transfer__date_leaving_service=AUDIT_START_DATE + relativedelta(days=2), ) ineligible_patient_death_within_audit_period = PatientFactory( postcode="ineligible_patient_death_within_audit_period", @@ -759,9 +734,7 @@ def test_kpi_calculation_30(AUDIT_START_DATE): ) # The default pz_code is "PZ130" for PaediatricsDiabetesUnitFactory - calc_kpis = CalculateKPIS( - pz_code="PZ130", calculation_date=AUDIT_START_DATE - ) + calc_kpis = CalculateKPIS(pz_code="PZ130", calculation_date=AUDIT_START_DATE) EXPECTED_TOTAL_ELIGIBLE = 5 EXPECTED_TOTAL_INELIGIBLE = 3 @@ -866,8 +839,7 @@ def test_kpi_calculation_31(AUDIT_START_DATE): visit__visit_date=AUDIT_START_DATE + relativedelta(days=2), date_of_birth=AUDIT_START_DATE - relativedelta(days=365 * 10), # Date of leaving service within the audit period - transfer__date_leaving_service=AUDIT_START_DATE - + relativedelta(days=2), + transfer__date_leaving_service=AUDIT_START_DATE + relativedelta(days=2), ) ineligible_patient_death_within_audit_period = PatientFactory( postcode="ineligible_patient_death_within_audit_period", @@ -879,9 +851,7 @@ def test_kpi_calculation_31(AUDIT_START_DATE): ) # The default pz_code is "PZ130" for PaediatricsDiabetesUnitFactory - calc_kpis = CalculateKPIS( - pz_code="PZ130", calculation_date=AUDIT_START_DATE - ) + calc_kpis = CalculateKPIS(pz_code="PZ130", calculation_date=AUDIT_START_DATE) EXPECTED_TOTAL_ELIGIBLE = 4 EXPECTED_TOTAL_INELIGIBLE = 3 @@ -952,8 +922,7 @@ def test_kpi_calculation_32_1(AUDIT_START_DATE): visit_date=AUDIT_START_DATE + relativedelta(months=3), height=160.0, weight=50.0, - height_weight_observation_date=AUDIT_START_DATE - + relativedelta(months=3), + height_weight_observation_date=AUDIT_START_DATE + relativedelta(months=3), ) pt_lt_12yo_3_health_checks = PatientFactory( @@ -970,8 +939,7 @@ def test_kpi_calculation_32_1(AUDIT_START_DATE): visit_date=AUDIT_START_DATE + relativedelta(months=3), height=160.0, weight=50.0, - height_weight_observation_date=AUDIT_START_DATE - + relativedelta(months=3), + height_weight_observation_date=AUDIT_START_DATE + relativedelta(months=3), ) # Separate Visit has HC3 VisitFactory( @@ -996,12 +964,10 @@ def test_kpi_calculation_32_1(AUDIT_START_DATE): # HC 2 height=160.0, weight=50.0, - height_weight_observation_date=AUDIT_START_DATE - + relativedelta(months=3), + height_weight_observation_date=AUDIT_START_DATE + relativedelta(months=3), # HC 3 systolic_blood_pressure=120, - blood_pressure_observation_date=AUDIT_START_DATE - + relativedelta(months=3), + blood_pressure_observation_date=AUDIT_START_DATE + relativedelta(months=3), ) pt_gte_12yo_6_health_checks = PatientFactory( @@ -1019,8 +985,7 @@ def test_kpi_calculation_32_1(AUDIT_START_DATE): # HC 2 height=160.0, weight=50.0, - height_weight_observation_date=AUDIT_START_DATE - + relativedelta(months=3), + height_weight_observation_date=AUDIT_START_DATE + relativedelta(months=3), # HC 3 thyroid_function_date=AUDIT_START_DATE + relativedelta(months=3), ) @@ -1030,15 +995,12 @@ def test_kpi_calculation_32_1(AUDIT_START_DATE): visit_date=AUDIT_START_DATE + relativedelta(months=6), # HC 4 systolic_blood_pressure=120, - blood_pressure_observation_date=AUDIT_START_DATE - + relativedelta(months=3), + blood_pressure_observation_date=AUDIT_START_DATE + relativedelta(months=3), # HC 5 albumin_creatinine_ratio=2, - albumin_creatinine_ratio_date=AUDIT_START_DATE - + relativedelta(months=6), + albumin_creatinine_ratio_date=AUDIT_START_DATE + relativedelta(months=6), # HC 6 - foot_examination_observation_date=AUDIT_START_DATE - + relativedelta(months=6), + foot_examination_observation_date=AUDIT_START_DATE + relativedelta(months=6), ) # Create Patients and Visits that should be ineligble @@ -1070,8 +1032,7 @@ def test_kpi_calculation_32_1(AUDIT_START_DATE): visit__visit_date=AUDIT_START_DATE + relativedelta(days=2), date_of_birth=AUDIT_START_DATE - relativedelta(days=365 * 10), # Date of leaving service within the audit period - transfer__date_leaving_service=AUDIT_START_DATE - + relativedelta(days=2), + transfer__date_leaving_service=AUDIT_START_DATE + relativedelta(days=2), ) ineligible_patient_death_within_audit_period = PatientFactory( postcode="ineligible_patient_death_within_audit_period", @@ -1083,9 +1044,7 @@ def test_kpi_calculation_32_1(AUDIT_START_DATE): ) # The default pz_code is "PZ130" for PaediatricsDiabetesUnitFactory - calc_kpis = CalculateKPIS( - pz_code="PZ130", calculation_date=AUDIT_START_DATE - ) + calc_kpis = CalculateKPIS(pz_code="PZ130", calculation_date=AUDIT_START_DATE) EXPECTED_TOTAL_ELIGIBLE = 18 # (2*3) + (2*6) EXPECTED_TOTAL_INELIGIBLE = 5 @@ -1149,8 +1108,7 @@ def test_kpi_calculation_32_2(AUDIT_START_DATE): height=160.0, weight=50.0, # HC 3 - height_weight_observation_date=AUDIT_START_DATE - + relativedelta(months=3), + height_weight_observation_date=AUDIT_START_DATE + relativedelta(months=3), thyroid_function_date=AUDIT_START_DATE + relativedelta(months=3), ) @@ -1169,8 +1127,7 @@ def test_kpi_calculation_32_2(AUDIT_START_DATE): # HC 2 height=160.0, weight=50.0, - height_weight_observation_date=AUDIT_START_DATE - + relativedelta(months=3), + height_weight_observation_date=AUDIT_START_DATE + relativedelta(months=3), ) # Separate Visit has HC3 VisitFactory( @@ -1193,8 +1150,7 @@ def test_kpi_calculation_32_2(AUDIT_START_DATE): patient=failing_pt_only_2_HCs, visit_date=AUDIT_START_DATE + relativedelta(months=3), # HC 3 - height_weight_observation_date=AUDIT_START_DATE - + relativedelta(months=3), + height_weight_observation_date=AUDIT_START_DATE + relativedelta(months=3), thyroid_function_date=AUDIT_START_DATE + relativedelta(months=3), ) @@ -1208,8 +1164,7 @@ def test_kpi_calculation_32_2(AUDIT_START_DATE): patient=failing_pt_only_1_HCs, visit_date=AUDIT_START_DATE + relativedelta(months=3), # HC 1 - height_weight_observation_date=AUDIT_START_DATE - + relativedelta(months=3), + height_weight_observation_date=AUDIT_START_DATE + relativedelta(months=3), thyroid_function_date=AUDIT_START_DATE + relativedelta(months=3), ) @@ -1241,8 +1196,7 @@ def test_kpi_calculation_32_2(AUDIT_START_DATE): visit__visit_date=AUDIT_START_DATE + relativedelta(days=2), date_of_birth=AUDIT_START_DATE - relativedelta(days=365 * 10), # Date of leaving service within the audit period - transfer__date_leaving_service=AUDIT_START_DATE - + relativedelta(days=2), + transfer__date_leaving_service=AUDIT_START_DATE + relativedelta(days=2), ) ineligible_patient_death_within_audit_period = PatientFactory( postcode="ineligible_patient_death_within_audit_period", @@ -1254,9 +1208,7 @@ def test_kpi_calculation_32_2(AUDIT_START_DATE): ) # The default pz_code is "PZ130" for PaediatricsDiabetesUnitFactory - calc_kpis = CalculateKPIS( - pz_code="PZ130", calculation_date=AUDIT_START_DATE - ) + calc_kpis = CalculateKPIS(pz_code="PZ130", calculation_date=AUDIT_START_DATE) EXPECTED_TOTAL_ELIGIBLE = 4 EXPECTED_TOTAL_INELIGIBLE = 5 @@ -1320,8 +1272,7 @@ def test_kpi_calculation_32_3(AUDIT_START_DATE): # HC 2 height=160.0, weight=50.0, - height_weight_observation_date=AUDIT_START_DATE - + relativedelta(months=3), + height_weight_observation_date=AUDIT_START_DATE + relativedelta(months=3), # HC 3 thyroid_function_date=AUDIT_START_DATE + relativedelta(months=3), ) @@ -1331,15 +1282,12 @@ def test_kpi_calculation_32_3(AUDIT_START_DATE): visit_date=AUDIT_START_DATE + relativedelta(months=6), # HC 4 systolic_blood_pressure=120, - blood_pressure_observation_date=AUDIT_START_DATE - + relativedelta(months=3), + blood_pressure_observation_date=AUDIT_START_DATE + relativedelta(months=3), # HC 5 albumin_creatinine_ratio=2, - albumin_creatinine_ratio_date=AUDIT_START_DATE - + relativedelta(months=6), + albumin_creatinine_ratio_date=AUDIT_START_DATE + relativedelta(months=6), # HC 6 - foot_examination_observation_date=AUDIT_START_DATE - + relativedelta(months=6), + foot_examination_observation_date=AUDIT_START_DATE + relativedelta(months=6), ) passing_pt_2 = PatientFactory( @@ -1363,8 +1311,7 @@ def test_kpi_calculation_32_3(AUDIT_START_DATE): thyroid_function_date=AUDIT_START_DATE + relativedelta(months=3), # HC 4 systolic_blood_pressure=120, - blood_pressure_observation_date=AUDIT_START_DATE - + relativedelta(months=3), + blood_pressure_observation_date=AUDIT_START_DATE + relativedelta(months=3), ) # Separate Visit has HC5+6 VisitFactory( @@ -1372,11 +1319,9 @@ def test_kpi_calculation_32_3(AUDIT_START_DATE): visit_date=AUDIT_START_DATE + relativedelta(months=6), # HC 5 albumin_creatinine_ratio=2, - albumin_creatinine_ratio_date=AUDIT_START_DATE - + relativedelta(months=6), + albumin_creatinine_ratio_date=AUDIT_START_DATE + relativedelta(months=6), # HC 6 - foot_examination_observation_date=AUDIT_START_DATE - + relativedelta(months=6), + foot_examination_observation_date=AUDIT_START_DATE + relativedelta(months=6), ) # Failing patients @@ -1393,8 +1338,7 @@ def test_kpi_calculation_32_3(AUDIT_START_DATE): patient=failing_pt_only_2_HCs, visit_date=AUDIT_START_DATE + relativedelta(months=3), # HC 3 - height_weight_observation_date=AUDIT_START_DATE - + relativedelta(months=3), + height_weight_observation_date=AUDIT_START_DATE + relativedelta(months=3), thyroid_function_date=AUDIT_START_DATE + relativedelta(months=3), ) @@ -1415,16 +1359,13 @@ def test_kpi_calculation_32_3(AUDIT_START_DATE): # HC 3 height=160.0, weight=50.0, - height_weight_observation_date=AUDIT_START_DATE - + relativedelta(months=3), + height_weight_observation_date=AUDIT_START_DATE + relativedelta(months=3), # HC 4 systolic_blood_pressure=120, - blood_pressure_observation_date=AUDIT_START_DATE - + relativedelta(months=3), + blood_pressure_observation_date=AUDIT_START_DATE + relativedelta(months=3), # HC 5 albumin_creatinine_ratio=2, - albumin_creatinine_ratio_date=AUDIT_START_DATE - + relativedelta(months=3), + albumin_creatinine_ratio_date=AUDIT_START_DATE + relativedelta(months=3), ) # Create Patients and Visits that should be ineligble @@ -1455,8 +1396,7 @@ def test_kpi_calculation_32_3(AUDIT_START_DATE): visit__visit_date=AUDIT_START_DATE + relativedelta(days=2), date_of_birth=AUDIT_START_DATE - relativedelta(days=365 * 10), # Date of leaving service within the audit period - transfer__date_leaving_service=AUDIT_START_DATE - + relativedelta(days=2), + transfer__date_leaving_service=AUDIT_START_DATE + relativedelta(days=2), ) ineligible_patient_death_within_audit_period = PatientFactory( postcode="ineligible_patient_death_within_audit_period", @@ -1468,9 +1408,7 @@ def test_kpi_calculation_32_3(AUDIT_START_DATE): ) # The default pz_code is "PZ130" for PaediatricsDiabetesUnitFactory - calc_kpis = CalculateKPIS( - pz_code="PZ130", calculation_date=AUDIT_START_DATE - ) + calc_kpis = CalculateKPIS(pz_code="PZ130", calculation_date=AUDIT_START_DATE) EXPECTED_TOTAL_ELIGIBLE = 4 EXPECTED_TOTAL_INELIGIBLE = 5 diff --git a/project/npda/tests/kpi_calculations/test_kpis_33_40.py b/project/npda/tests/kpi_calculations/test_kpis_33_40.py index 6d8ddbfa..2f3dc109 100644 --- a/project/npda/tests/kpi_calculations/test_kpis_33_40.py +++ b/project/npda/tests/kpi_calculations/test_kpis_33_40.py @@ -7,12 +7,13 @@ from project.constants.diabetes_types import DIABETES_TYPES from project.constants.smoking_status import SMOKING_STATUS -from project.npda.general_functions.kpis import CalculateKPIS, KPIResult +from project.npda.kpi_class.kpis import CalculateKPIS, KPIResult from project.npda.models import Patient from project.npda.tests.factories.patient_factory import PatientFactory from project.npda.tests.factories.visit_factory import VisitFactory -from project.npda.tests.kpi_calculations.test_kpi_calculations import \ - assert_kpi_result_equal +from project.npda.tests.kpi_calculations.test_kpi_calculations import ( + assert_kpi_result_equal, +) @pytest.mark.django_db @@ -139,8 +140,7 @@ def test_kpi_calculation_33(AUDIT_START_DATE): visit__visit_date=AUDIT_START_DATE + relativedelta(days=2), date_of_birth=AUDIT_START_DATE - relativedelta(days=365 * 10), # Date of leaving service within the audit period - transfer__date_leaving_service=AUDIT_START_DATE - + relativedelta(days=2), + transfer__date_leaving_service=AUDIT_START_DATE + relativedelta(days=2), ) ineligible_patient_death_within_audit_period = PatientFactory( postcode="ineligible_patient_death_within_audit_period", @@ -273,8 +273,7 @@ def test_kpi_calculation_34(AUDIT_START_DATE): visit__visit_date=AUDIT_START_DATE + relativedelta(days=2), date_of_birth=AUDIT_START_DATE - relativedelta(days=365 * 10), # Date of leaving service within the audit period - transfer__date_leaving_service=AUDIT_START_DATE - + relativedelta(days=2), + transfer__date_leaving_service=AUDIT_START_DATE + relativedelta(days=2), ) ineligible_patient_death_within_audit_period = PatientFactory( postcode="ineligible_patient_death_within_audit_period", @@ -362,8 +361,7 @@ def test_kpi_calculation_35(AUDIT_START_DATE): VisitFactory( patient=passing_patient_2, # KPI 6 specific = an observation within the audit period - height_weight_observation_date=AUDIT_START_DATE - + relativedelta(days=5), + height_weight_observation_date=AUDIT_START_DATE + relativedelta(days=5), visit_date=AUDIT_START_DATE + relativedelta(days=5), smoking_status=SMOKING_STATUS[1][0], ) @@ -403,8 +401,7 @@ def test_kpi_calculation_35(AUDIT_START_DATE): visit__visit_date=AUDIT_START_DATE + relativedelta(days=2), date_of_birth=AUDIT_START_DATE - relativedelta(days=365 * 10), # Date of leaving service within the audit period - transfer__date_leaving_service=AUDIT_START_DATE - + relativedelta(days=2), + transfer__date_leaving_service=AUDIT_START_DATE + relativedelta(days=2), ) ineligible_patient_death_within_audit_period = PatientFactory( postcode="ineligible_patient_death_within_audit_period", @@ -477,8 +474,7 @@ def test_kpi_calculation_36(AUDIT_START_DATE): # KPI5 eligible **eligible_criteria, # KPI 35 specific - visit__smoking_cessation_referral_date=AUDIT_START_DATE - + relativedelta(days=2), + visit__smoking_cessation_referral_date=AUDIT_START_DATE + relativedelta(days=2), ) # only second visit has a valid smoking cessation referral passing_patient_2 = PatientFactory( @@ -492,8 +488,7 @@ def test_kpi_calculation_36(AUDIT_START_DATE): VisitFactory( patient=passing_patient_2, visit_date=AUDIT_START_DATE + relativedelta(days=5), - smoking_cessation_referral_date=AUDIT_START_DATE - + relativedelta(days=32), + smoking_cessation_referral_date=AUDIT_START_DATE + relativedelta(days=32), ) # Failing patients @@ -521,8 +516,7 @@ def test_kpi_calculation_36(AUDIT_START_DATE): visit__visit_date=AUDIT_START_DATE + relativedelta(days=2), date_of_birth=AUDIT_START_DATE - relativedelta(days=365 * 10), # Date of leaving service within the audit period - transfer__date_leaving_service=AUDIT_START_DATE - + relativedelta(days=2), + transfer__date_leaving_service=AUDIT_START_DATE + relativedelta(days=2), ) ineligible_patient_death_within_audit_period = PatientFactory( postcode="ineligible_patient_death_within_audit_period", @@ -644,8 +638,7 @@ def test_kpi_calculation_37(AUDIT_START_DATE): visit__visit_date=AUDIT_START_DATE + relativedelta(days=2), date_of_birth=AUDIT_START_DATE - relativedelta(days=365 * 10), # Date of leaving service within the audit period - transfer__date_leaving_service=AUDIT_START_DATE - + relativedelta(days=2), + transfer__date_leaving_service=AUDIT_START_DATE + relativedelta(days=2), ) ineligible_patient_death_within_audit_period = PatientFactory( postcode="ineligible_patient_death_within_audit_period", @@ -727,8 +720,7 @@ def test_kpi_calculation_38(AUDIT_START_DATE): VisitFactory( patient=passing_patient_2, visit_date=AUDIT_START_DATE + relativedelta(days=5), - dietician_additional_appointment_date=AUDIT_START_DATE - + relativedelta(days=4), + dietician_additional_appointment_date=AUDIT_START_DATE + relativedelta(days=4), ) # Failing patients @@ -769,8 +761,7 @@ def test_kpi_calculation_38(AUDIT_START_DATE): visit__visit_date=AUDIT_START_DATE + relativedelta(days=2), date_of_birth=AUDIT_START_DATE - relativedelta(days=365 * 10), # Date of leaving service within the audit period - transfer__date_leaving_service=AUDIT_START_DATE - + relativedelta(days=2), + transfer__date_leaving_service=AUDIT_START_DATE + relativedelta(days=2), ) ineligible_patient_death_within_audit_period = PatientFactory( postcode="ineligible_patient_death_within_audit_period", @@ -852,8 +843,7 @@ def test_kpi_calculation_39(AUDIT_START_DATE): VisitFactory( patient=passing_patient_2, visit_date=AUDIT_START_DATE + relativedelta(days=5), - flu_immunisation_recommended_date=AUDIT_START_DATE - + relativedelta(days=4), + flu_immunisation_recommended_date=AUDIT_START_DATE + relativedelta(days=4), ) # Failing patients @@ -894,8 +884,7 @@ def test_kpi_calculation_39(AUDIT_START_DATE): visit__visit_date=AUDIT_START_DATE + relativedelta(days=2), date_of_birth=AUDIT_START_DATE - relativedelta(days=365 * 10), # Date of leaving service within the audit period - transfer__date_leaving_service=AUDIT_START_DATE - + relativedelta(days=2), + transfer__date_leaving_service=AUDIT_START_DATE + relativedelta(days=2), ) ineligible_patient_death_within_audit_period = PatientFactory( postcode="ineligible_patient_death_within_audit_period", @@ -954,8 +943,7 @@ def test_kpi_calculation_40(AUDIT_START_DATE): # KPI1 eligible **eligible_criteria, # KPI 40 specific - visit__sick_day_rules_training_date=AUDIT_START_DATE - + relativedelta(days=30), + visit__sick_day_rules_training_date=AUDIT_START_DATE + relativedelta(days=30), ) # only second visit has a valid sick day rule passing_patient_2 = PatientFactory( diff --git a/project/npda/tests/kpi_calculations/test_kpis_41_43.py b/project/npda/tests/kpi_calculations/test_kpis_41_43.py index d4146811..b798e8d7 100644 --- a/project/npda/tests/kpi_calculations/test_kpis_41_43.py +++ b/project/npda/tests/kpi_calculations/test_kpis_41_43.py @@ -8,12 +8,13 @@ from project.constants.diabetes_types import DIABETES_TYPES from project.constants.smoking_status import SMOKING_STATUS -from project.npda.general_functions.kpis import CalculateKPIS, KPIResult +from project.npda.kpi_class.kpis import CalculateKPIS, KPIResult from project.npda.models import Patient from project.npda.tests.factories.patient_factory import PatientFactory from project.npda.tests.factories.visit_factory import VisitFactory -from project.npda.tests.kpi_calculations.test_kpi_calculations import \ - assert_kpi_result_equal +from project.npda.tests.kpi_calculations.test_kpi_calculations import ( + assert_kpi_result_equal, +) # Logging logger = logging.getLogger(__name__) @@ -37,8 +38,7 @@ def test_kpi_calculation_41(AUDIT_START_DATE, AUDIT_END_DATE): # Create Patients and Visits that should be eligible (KPI7) DIAB_DIAGNOSIS_91D_BEFORE_END = AUDIT_END_DATE - relativedelta(days=91) eligible_criteria = { - "visit__visit_date": DIAB_DIAGNOSIS_91D_BEFORE_END - - relativedelta(months=2), + "visit__visit_date": DIAB_DIAGNOSIS_91D_BEFORE_END - relativedelta(months=2), "date_of_birth": AUDIT_START_DATE - relativedelta(years=10), "diabetes_type": DIABETES_TYPES[0][0], # any other observation date @@ -68,8 +68,7 @@ def test_kpi_calculation_41(AUDIT_START_DATE, AUDIT_END_DATE): # create 2nd visit with coeliac screen < 90 days after T1DM diagnosis VisitFactory( patient=passing_patient_2, - coeliac_screen_date=DIAB_DIAGNOSIS_91D_BEFORE_END - + relativedelta(days=90), + coeliac_screen_date=DIAB_DIAGNOSIS_91D_BEFORE_END + relativedelta(days=90), ) # Failing patients @@ -167,8 +166,7 @@ def test_kpi_calculation_42(AUDIT_START_DATE, AUDIT_END_DATE): # Create Patients and Visits that should be eligible (KPI7) DIAB_DIAGNOSIS_91D_BEFORE_END = AUDIT_END_DATE - relativedelta(days=91) eligible_criteria = { - "visit__visit_date": DIAB_DIAGNOSIS_91D_BEFORE_END - - relativedelta(months=2), + "visit__visit_date": DIAB_DIAGNOSIS_91D_BEFORE_END - relativedelta(months=2), "date_of_birth": AUDIT_START_DATE - relativedelta(years=10), "diabetes_type": DIABETES_TYPES[0][0], # any other observation date @@ -198,8 +196,7 @@ def test_kpi_calculation_42(AUDIT_START_DATE, AUDIT_END_DATE): # create 2nd visit with thyroid_function_date < 90 days after T1DM diagnosis VisitFactory( patient=passing_patient_2, - thyroid_function_date=DIAB_DIAGNOSIS_91D_BEFORE_END - + relativedelta(days=90), + thyroid_function_date=DIAB_DIAGNOSIS_91D_BEFORE_END + relativedelta(days=90), ) # Failing patients @@ -279,6 +276,7 @@ def test_kpi_calculation_42(AUDIT_START_DATE, AUDIT_END_DATE): actual=calc_kpis.calculate_kpi_42_thyroid_disease_screening(), ) + @pytest.mark.django_db def test_kpi_calculation_43(AUDIT_START_DATE, AUDIT_END_DATE): """Tests that KPI43 is calculated correctly. @@ -299,8 +297,7 @@ def test_kpi_calculation_43(AUDIT_START_DATE, AUDIT_END_DATE): # Create Patients and Visits that should be eligible (KPI7) DIAB_DIAGNOSIS_15D_BEFORE_END = AUDIT_END_DATE - relativedelta(days=15) eligible_criteria = { - "visit__visit_date": DIAB_DIAGNOSIS_15D_BEFORE_END - - relativedelta(months=2), + "visit__visit_date": DIAB_DIAGNOSIS_15D_BEFORE_END - relativedelta(months=2), "date_of_birth": AUDIT_START_DATE - relativedelta(years=10), "diabetes_type": DIABETES_TYPES[0][0], # any other observation date @@ -389,7 +386,8 @@ def test_kpi_calculation_43(AUDIT_START_DATE, AUDIT_END_DATE): diabetes_type=DIABETES_TYPES[0][0], # Date of diag 2 days before end of audit period diagnosis_date=AUDIT_END_DATE - relativedelta(days=14), - visit__carbohydrate_counting_level_three_education_date=AUDIT_END_DATE - relativedelta(days=14), + visit__carbohydrate_counting_level_three_education_date=AUDIT_END_DATE + - relativedelta(days=14), ) calc_kpis = CalculateKPIS( @@ -412,4 +410,4 @@ def test_kpi_calculation_43(AUDIT_START_DATE, AUDIT_END_DATE): assert_kpi_result_equal( expected=EXPECTED_KPIRESULT, actual=calc_kpis.calculate_kpi_43_carbohydrate_counting_education(), - ) \ No newline at end of file + ) diff --git a/project/npda/tests/kpi_calculations/test_kpis_44_49.py b/project/npda/tests/kpi_calculations/test_kpis_44_49.py index 5a223092..bd50d754 100644 --- a/project/npda/tests/kpi_calculations/test_kpis_44_49.py +++ b/project/npda/tests/kpi_calculations/test_kpis_44_49.py @@ -8,16 +8,16 @@ from project.constants.albuminuria_stage import ALBUMINURIA_STAGES from project.constants.diabetes_types import DIABETES_TYPES -from project.constants.hospital_admission_reasons import \ - HOSPITAL_ADMISSION_REASONS +from project.constants.hospital_admission_reasons import HOSPITAL_ADMISSION_REASONS from project.constants.smoking_status import SMOKING_STATUS from project.constants.yes_no_unknown import YES_NO_UNKNOWN -from project.npda.general_functions.kpis import CalculateKPIS, KPIResult +from project.npda.kpi_class.kpis import CalculateKPIS, KPIResult from project.npda.models import Patient from project.npda.tests.factories.patient_factory import PatientFactory from project.npda.tests.factories.visit_factory import VisitFactory -from project.npda.tests.kpi_calculations.test_kpi_calculations import \ - assert_kpi_result_equal +from project.npda.tests.kpi_calculations.test_kpi_calculations import ( + assert_kpi_result_equal, +) # Logging logger = logging.getLogger(__name__) @@ -119,17 +119,15 @@ def test_kpi_calculation_44(AUDIT_START_DATE): ) # The default pz_code is "PZ130" for PaediatricsDiabetesUnitFactory - calc_kpis = CalculateKPIS( - pz_code="PZ130", calculation_date=AUDIT_START_DATE - ) + calc_kpis = CalculateKPIS(pz_code="PZ130", calculation_date=AUDIT_START_DATE) medians = list(map(calculate_median, [pt_1_hba1cs, pt_2_hba1cs])) EXPECTED_MEAN = sum(medians) / len(medians) EXPECTED_TOTAL_ELIGIBLE = 2 EXPECTED_TOTAL_INELIGIBLE = 2 - EXPECTED_TOTAL_PASSED = EXPECTED_MEAN # Stores the mean - EXPECTED_TOTAL_FAILED = -1 # Not used + EXPECTED_TOTAL_PASSED = EXPECTED_MEAN # Stores the mean + EXPECTED_TOTAL_FAILED = -1 # Not used EXPECTED_KPIRESULT = KPIResult( total_eligible=EXPECTED_TOTAL_ELIGIBLE, @@ -143,6 +141,7 @@ def test_kpi_calculation_44(AUDIT_START_DATE): actual=calc_kpis.calculate_kpi_44_mean_hba1c(), ) + @pytest.mark.django_db def test_kpi_calculation_45(AUDIT_START_DATE): """Tests that KPI45 is calculated correctly. @@ -240,17 +239,15 @@ def test_kpi_calculation_45(AUDIT_START_DATE): ) # The default pz_code is "PZ130" for PaediatricsDiabetesUnitFactory - calc_kpis = CalculateKPIS( - pz_code="PZ130", calculation_date=AUDIT_START_DATE - ) + calc_kpis = CalculateKPIS(pz_code="PZ130", calculation_date=AUDIT_START_DATE) medians = list(map(calculate_median, [pt_1_hba1cs, pt_2_hba1cs])) EXPECTED_MEDIAN = calculate_median(medians) EXPECTED_TOTAL_ELIGIBLE = 2 EXPECTED_TOTAL_INELIGIBLE = 2 - EXPECTED_TOTAL_PASSED = EXPECTED_MEDIAN # Stores the mean - EXPECTED_TOTAL_FAILED = -1 # Not used + EXPECTED_TOTAL_PASSED = EXPECTED_MEDIAN # Stores the mean + EXPECTED_TOTAL_FAILED = -1 # Not used EXPECTED_KPIRESULT = KPIResult( total_eligible=EXPECTED_TOTAL_ELIGIBLE, @@ -294,8 +291,7 @@ def test_kpi_calculation_46(AUDIT_START_DATE): # valid admission reason visit__hospital_admission_reason=HOSPITAL_ADMISSION_REASONS[0][0], # admission date within audit range - visit__hospital_admission_date=AUDIT_START_DATE - + relativedelta(days=2), + visit__hospital_admission_date=AUDIT_START_DATE + relativedelta(days=2), ) passing_valid_admission_reason_and_discharge_within_audit_range = PatientFactory( # KPI1 eligible @@ -303,8 +299,7 @@ def test_kpi_calculation_46(AUDIT_START_DATE): # valid admission reason visit__hospital_admission_reason=HOSPITAL_ADMISSION_REASONS[-1][0], # discharge date within audit range - visit__hospital_discharge_date=AUDIT_START_DATE - + relativedelta(days=2), + visit__hospital_discharge_date=AUDIT_START_DATE + relativedelta(days=2), ) # Create failing pts @@ -314,8 +309,7 @@ def test_kpi_calculation_46(AUDIT_START_DATE): # invalid admission reason visit__hospital_admission_reason="42", # admission date within audit range - visit__hospital_admission_date=AUDIT_START_DATE - + relativedelta(days=2), + visit__hospital_admission_date=AUDIT_START_DATE + relativedelta(days=2), ) failing_both_admission_outside_audit_date = PatientFactory( # KPI1 eligible @@ -323,8 +317,7 @@ def test_kpi_calculation_46(AUDIT_START_DATE): # valid admission reason visit__hospital_admission_reason=HOSPITAL_ADMISSION_REASONS[-1][0], # admission date outside audit range - visit__hospital_admission_date=AUDIT_START_DATE - - relativedelta(days=2), + visit__hospital_admission_date=AUDIT_START_DATE - relativedelta(days=2), ) # Create Patients and Visits that should be excluded @@ -342,9 +335,7 @@ def test_kpi_calculation_46(AUDIT_START_DATE): ) # The default pz_code is "PZ130" for PaediatricsDiabetesUnitFactory - calc_kpis = CalculateKPIS( - pz_code="PZ130", calculation_date=AUDIT_START_DATE - ) + calc_kpis = CalculateKPIS(pz_code="PZ130", calculation_date=AUDIT_START_DATE) EXPECTED_TOTAL_ELIGIBLE = 4 EXPECTED_TOTAL_INELIGIBLE = 2 @@ -393,8 +384,7 @@ def test_kpi_calculation_47(AUDIT_START_DATE): # valid dka admission reason visit__hospital_admission_reason=HOSPITAL_ADMISSION_REASONS[1][0], # admission date within audit range - visit__hospital_admission_date=AUDIT_START_DATE - + relativedelta(days=2), + visit__hospital_admission_date=AUDIT_START_DATE + relativedelta(days=2), ) passing_valid_dka_admission_reason_and_discharge_within_audit_range = PatientFactory( # KPI1 eligible @@ -402,8 +392,7 @@ def test_kpi_calculation_47(AUDIT_START_DATE): # valid dka admission reason visit__hospital_admission_reason=HOSPITAL_ADMISSION_REASONS[1][0], # discharge date within audit range - visit__hospital_discharge_date=AUDIT_START_DATE - + relativedelta(days=2), + visit__hospital_discharge_date=AUDIT_START_DATE + relativedelta(days=2), ) # Create failing pts @@ -413,8 +402,7 @@ def test_kpi_calculation_47(AUDIT_START_DATE): # invalid admission reason visit__hospital_admission_reason=HOSPITAL_ADMISSION_REASONS[0][0], # admission date within audit range - visit__hospital_admission_date=AUDIT_START_DATE - + relativedelta(days=2), + visit__hospital_admission_date=AUDIT_START_DATE + relativedelta(days=2), ) failing_both_admission_outside_audit_date = PatientFactory( # KPI1 eligible @@ -422,8 +410,7 @@ def test_kpi_calculation_47(AUDIT_START_DATE): # valid admission reason visit__hospital_admission_reason=HOSPITAL_ADMISSION_REASONS[0][0], # admission date outside audit range - visit__hospital_admission_date=AUDIT_START_DATE - - relativedelta(days=2), + visit__hospital_admission_date=AUDIT_START_DATE - relativedelta(days=2), ) # Create Patients and Visits that should be excluded @@ -441,9 +428,7 @@ def test_kpi_calculation_47(AUDIT_START_DATE): ) # The default pz_code is "PZ130" for PaediatricsDiabetesUnitFactory - calc_kpis = CalculateKPIS( - pz_code="PZ130", calculation_date=AUDIT_START_DATE - ) + calc_kpis = CalculateKPIS(pz_code="PZ130", calculation_date=AUDIT_START_DATE) EXPECTED_TOTAL_ELIGIBLE = 4 EXPECTED_TOTAL_INELIGIBLE = 2 @@ -541,9 +526,7 @@ def test_kpi_calculation_48(AUDIT_START_DATE): ) # The default pz_code is "PZ130" for PaediatricsDiabetesUnitFactory - calc_kpis = CalculateKPIS( - pz_code="PZ130", calculation_date=AUDIT_START_DATE - ) + calc_kpis = CalculateKPIS(pz_code="PZ130", calculation_date=AUDIT_START_DATE) EXPECTED_TOTAL_ELIGIBLE = 5 EXPECTED_TOTAL_INELIGIBLE = 2 @@ -641,9 +624,7 @@ def test_kpi_calculation_49(AUDIT_START_DATE): ) # The default pz_code is "PZ130" for PaediatricsDiabetesUnitFactory - calc_kpis = CalculateKPIS( - pz_code="PZ130", calculation_date=AUDIT_START_DATE - ) + calc_kpis = CalculateKPIS(pz_code="PZ130", calculation_date=AUDIT_START_DATE) EXPECTED_TOTAL_ELIGIBLE = 5 EXPECTED_TOTAL_INELIGIBLE = 2 @@ -678,4 +659,4 @@ def calculate_median(values): # If the length of the list is even, return the average of the two middle elements middle1 = sorted_values[n // 2 - 1] middle2 = sorted_values[n // 2] - return (middle1 + middle2) / 2 \ No newline at end of file + return (middle1 + middle2) / 2 diff --git a/project/npda/urls.py b/project/npda/urls.py index 57cbbe00..edb36f0d 100644 --- a/project/npda/urls.py +++ b/project/npda/urls.py @@ -1,7 +1,7 @@ from django.urls import path, include from django.contrib.auth.views import PasswordResetConfirmView from django.contrib.auth import urls as auth_urls -from project.npda.general_functions.kpis import KPIAggregationForPDU +from project.npda.kpi_class.kpis import KPIAggregationForPDU from project.npda.views import ( VisitCreateView, VisitDeleteView, @@ -101,6 +101,6 @@ path( "kpis/aggregation/pdu/", view=KPIAggregationForPDU.as_view(), - name='aggregation-pdu' - ) + name="aggregation-pdu", + ), ] diff --git a/project/npda/views/visit.py b/project/npda/views/visit.py index ebfd8aca..c6cf7be1 100644 --- a/project/npda/views/visit.py +++ b/project/npda/views/visit.py @@ -1,21 +1,24 @@ +# python imports +import datetime + # Django imports -from django.contrib.messages.views import SuccessMessageMixin -from django.contrib.auth.mixins import PermissionRequiredMixin from django.contrib import messages +from django.contrib.auth.mixins import PermissionRequiredMixin +from django.contrib.messages.views import SuccessMessageMixin from django.forms import BaseModelForm from django.http import HttpResponse, HttpResponseRedirect -from django.shortcuts import render -from django.views.generic.edit import CreateView, UpdateView, DeleteView +from django.urls import reverse, reverse_lazy from django.views.generic import ListView -from django.urls import reverse_lazy, reverse +from django.views.generic.edit import CreateView, UpdateView, DeleteView # Third party imports # RCPCH imports -from ..models import Visit, Patient from ..forms.visit_form import VisitForm from ..general_functions import get_visit_categories -from .mixins import CheckPDUInstanceMixin, CheckPDUListMixin, LoginAndOTPRequiredMixin +from ..kpi_class.kpis import CalculateKPIS +from .mixins import CheckPDUListMixin, LoginAndOTPRequiredMixin, CheckPDUInstanceMixin +from ..models import Visit, Patient, Transfer class PatientVisitsListView( @@ -39,6 +42,25 @@ def get_context_data(self, **kwargs): context["visits"] = calculated_visits context["patient"] = patient context["submission"] = submission + + # calculate the KPIs for this patient + pdu = ( + Transfer.objects.filter(patient=patient, date_leaving_service__isnull=True) + .first() + .paediatric_diabetes_unit + ) + # get the PDU for this patient - this is the PDU that the patient is currently under. + # If the patient has left the PDU, the date_leaving_service will be set and it will be possible to view KPIs for the PDU up until transfer, + # if this happened during the audit period. This is TODO + kpi_results = CalculateKPIS( + pz_code=pdu.pz_code, + calculation_date=datetime.date.today(), + patients=Patient.objects.filter( + pk=patient_id + ), # this is a queryset of one patient + ).calculate_kpis_for_patients() + context["kpi_results"] = kpi_results + return context