Skip to content

Commit 3ec22a4

Browse files
authored
Merge pull request #54 from rcpch/eatyourpeas/issue41
who
2 parents ddefc14 + 61bc25b commit 3ec22a4

File tree

14 files changed

+5655
-33
lines changed

14 files changed

+5655
-33
lines changed

.bumpversion.cfg

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
[bumpversion]
2-
current_version = 4.2.8
2+
current_version = 4.3.0
33
tag = False
44
commit = True
55

Dockerfile

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,20 @@
1-
FROM python:3.12-bookworm
1+
FROM python:3.12
22

3+
# Set the working directory in the container
4+
WORKDIR /app
5+
6+
# Copy the requirements file into the container
37
COPY requirements.txt .
48

5-
RUN pip install -r requirements.txt
9+
# Create a virtual environment
10+
RUN python -m venv /app/venv
11+
12+
# Upgrade pip and install the dependencies in the virtual environment
13+
RUN /app/venv/bin/pip install --upgrade pip
14+
RUN /app/venv/bin/pip install -r requirements.txt
615

16+
# Copy the rest of the application code
717
COPY . .
18+
19+
# Set the entrypoint to use the virtual environment's Python interpreter
20+
CMD ["python3"]

docker-compose.yml

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
# NOTE The below is the Docker Compose specification version NOT the Python version:
2-
version: "3.8"
1+
version: "3.12"
32

43
services:
54
rcpchgrowth-python:
@@ -9,5 +8,4 @@ services:
98
working_dir: /app
109
tty: true # Allocate a pseudo-TTY
1110
stdin_open: true # Keep stdin open
12-
command: |
13-
pytest
11+
container_name: rcpchgrowth-python

rcpchgrowth/age_advice_strings.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from rcpchgrowth.constants import CDC
1+
from rcpchgrowth.constants import CDC, WHO
22

33
def comment_prematurity_correction(
44
chronological_decimal_age: float,
@@ -17,7 +17,7 @@ def comment_prematurity_correction(
1717
lay_chronological_decimal_age_comment = "Your child was born on their due date."
1818
clinician_chronological_decimal_age_comment = "Born Term. No correction has been made for gestation."
1919
# These fields should only apply to CDC reference, since UK-WHO corrects for all gestations (and therefore corrected_decimal_age will never be equal to chronological_decimal_age if gestation_weeks is not 40)
20-
if gestation_weeks < 42 and reference == CDC:
20+
if gestation_weeks < 42 and (reference == CDC or reference == WHO):
2121
if gestation_weeks < 37:
2222
lay_chronological_decimal_age_comment = f"Your child was born at {gestation_weeks}+{gestation_days} weeks gestation. No correction is made for this beyond 2 years of age."
2323
clinician_chronological_decimal_age_comment =f"Born preterm at {gestation_weeks}+{gestation_days} weeks gestation. No correction is made for this beyond 2 years of age."

rcpchgrowth/chart_functions.py

Lines changed: 193 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
from .trisomy_21_aap import select_reference_data_for_trisomy_21_aap
66
from .turner import select_reference_data_for_turner
77
from .uk_who import select_reference_data_for_uk_who_chart
8+
from .who import who_reference, select_reference_data_for_who_chart
89
from .constants.reference_constants import (
910
CDC_REFERENCES,
1011
CDC,
@@ -25,6 +26,8 @@
2526
TURNERS,
2627
UK_WHO,
2728
UK_WHO_REFERENCES,
29+
WHO,
30+
WHO_REFERENCES
2831
)
2932

3033
"""
@@ -70,6 +73,12 @@ def create_chart(
7073
sex=sex,
7174
centile_format=centile_format,
7275
is_sds=is_sds)
76+
elif reference == WHO:
77+
return create_who_chart(
78+
measurement_method=measurement_method,
79+
sex=sex,
80+
centile_format=centile_format,
81+
is_sds=is_sds)
7382
else:
7483
print("No reference data returned. Is there a spelling mistake in your reference?")
7584

@@ -109,7 +118,7 @@ def generate_custom_sds_line(
109118
centile=custom_centile
110119
)
111120
except:
112-
print("Could not generate centile data.")
121+
print("Could not generate centile data for UK-WHO.")
113122
centile_data=[]
114123
# all data can now be tagged by reference_name and added to reference_data
115124
reference_data.append({reference: centile_data})
@@ -131,7 +140,30 @@ def generate_custom_sds_line(
131140
centile=custom_centile
132141
)
133142
except:
134-
print(f"Could not generate SDS centile data.")
143+
print(f"Could not generate SDS centile data for {CDC}.")
144+
centile_data=[]
145+
# all data can now be tagged by reference_name and added to reference_data
146+
reference_data.append({reference: centile_data})
147+
elif reference == WHO:
148+
for reference_index, reference in enumerate(WHO_REFERENCES):
149+
# the centile reference data
150+
lms_array_for_measurement=select_reference_data_for_who_chart(
151+
who_reference_name=reference,
152+
measurement_method=measurement_method,
153+
sex=sex
154+
)
155+
centile_data=[]
156+
try:
157+
centile_data= build_centile_object(
158+
reference=WHO,
159+
measurement_method=measurement_method,
160+
sex=sex,
161+
lms_array_for_measurement=lms_array_for_measurement,
162+
z=custom_sds,
163+
centile=custom_centile
164+
)
165+
except:
166+
print(f"Could not generate SDS centile data for WHO.")
135167
centile_data=[]
136168
# all data can now be tagged by reference_name and added to reference_data
137169
reference_data.append({reference: centile_data})
@@ -219,6 +251,8 @@ def select_reference_lms_data(reference: str, measurement_method: str, sex: str)
219251
lms_array_for_measurement=select_reference_data_for_trisomy_21(measurement_method=measurement_method, sex=sex)
220252
elif reference == CDC:
221253
lms_array_for_measurement=select_reference_data_for_cdc_chart(measurement_method=measurement_method, sex=sex)
254+
elif reference == WHO:
255+
lms_array_for_measurement=select_reference_data_for_who_chart(measurement_method=measurement_method, sex=sex)
222256
else:
223257
raise Exception("No data has been selected!")
224258

@@ -907,7 +941,163 @@ def create_trisomy_21_aap_chart(measurement_method: str, sex: str, centile_forma
907941
]
908942
"""
909943

910-
# Private functions
944+
def create_who_chart(
945+
measurement_method: str,
946+
sex: str,
947+
centile_format: Union[str, list] = COLE_TWO_THIRDS_SDS_NINE_CENTILES,
948+
is_sds = False
949+
):
950+
951+
# user selects which centile collection they want, for sex and measurement_method
952+
# If the Cole method is selected, conversion between centile and SDS
953+
# is different as SDS is rounded to the nearest 2/3
954+
# Cole method selection is stored in the cole_method flag.
955+
# If no parameter is passed, default is the Cole method
956+
# Alternatively it is possible to pass a custom list of values - if the is_sds flag is False (default) these are centiles
957+
958+
centile_sds_collection = []
959+
cole_method = False
960+
961+
if (type(centile_format) is list):
962+
# a custom list of centiles was provided
963+
centile_sds_collection = centile_format
964+
else:
965+
# a standard centile collection was selected
966+
centile_sds_collection = select_centile_format(centile_format)
967+
is_sds=False
968+
969+
##
970+
# iterate through the 3 references that make up CDC (Fenton, WHO, CDC itself)
971+
# There will be a list for each one
972+
##
973+
974+
# all data for a given reference are stored here: this is returned to the user
975+
reference_data = []
976+
977+
for reference_index, reference_name in enumerate(WHO_REFERENCES):
978+
sex_list: dict = {} # all the data for a given sex are stored here
979+
# For each reference we have 2 sexes
980+
# for sex_index, sex in enumerate(SEXES):
981+
# For each sex we have 4 measurement_methods
982+
983+
measurements: dict = {} # all the data for a given measurement_method are stored here
984+
985+
# for measurement_index, measurement_method in enumerate(MEASUREMENT_METHODS):
986+
# for every measurement method we have as many centiles
987+
# as have been requested
988+
989+
centiles = [] # all generated centiles for a selected centile collection are stored here
990+
991+
default_youngest_reference = False
992+
if reference_index == 1: # WHO children
993+
default_youngest_reference = True
994+
995+
# the centile reference data
996+
try:
997+
lms_array_for_measurement=select_reference_data_for_who_chart(
998+
who_reference_name=reference_name,
999+
measurement_method=measurement_method,
1000+
sex=sex,
1001+
default_youngest_reference=default_youngest_reference)
1002+
except:
1003+
lms_array_for_measurement = []
1004+
1005+
for centile_index, centile_sds in enumerate(centile_sds_collection):
1006+
# we must create a z for each requested centile
1007+
# if the Cole 9 centiles were selected, these are rounded,
1008+
# so conversion to SDS is different
1009+
# Otherwise standard conversation of centile to z is used
1010+
1011+
z=0.0 #initialise
1012+
centile_value=0.0 #initialise
1013+
1014+
if cole_method:
1015+
z = rounded_sds_for_centile(centile_sds) # a centile was provided, so convert to z
1016+
centile_value=centile_sds # store the original centile value
1017+
else:
1018+
if (is_sds):
1019+
z=centile_sds # an sds was supplied
1020+
centile_value=centile(centile_sds) # convert the z to a centile and store
1021+
else:
1022+
z = sds_for_centile(centile_sds) # a centile was provided, so convert to z
1023+
centile_value=centile_sds # store the original centile value
1024+
centile_data = []
1025+
1026+
try:
1027+
# Generate a centile. there will be nine of these if Cole method selected.
1028+
# Some data does not exist at all ages, so any error reflects missing data.
1029+
# If this happens, an empty list is returned.
1030+
centile_data = generate_centile(
1031+
z=z,
1032+
centile=centile_value,
1033+
measurement_method=measurement_method,
1034+
sex=sex,
1035+
lms_array_for_measurement=lms_array_for_measurement,
1036+
reference=WHO,
1037+
is_sds=is_sds,
1038+
default_youngest_reference=default_youngest_reference
1039+
)
1040+
except LookupError as e:
1041+
print(f"Not possible to generate centile data for WHO {measurement_method} in {sex}s. {e}")
1042+
centile_data=None
1043+
# Store this centile for a given measurement
1044+
1045+
centiles.append({"sds": round(z * 100) / 100,
1046+
"centile": centile_value, "data": centile_data})
1047+
1048+
# this is the end of the centile_collection for loop
1049+
# All the centiles for this measurement, sex and reference are added to the measurements list
1050+
measurements.update({measurement_method: centiles})
1051+
1052+
# this is the end of the measurement_methods loop
1053+
# All data for all measurement_methods for this sex are added to the sex_list list
1054+
1055+
sex_list.update({sex: measurements})
1056+
1057+
# all data can now be tagged by reference_name and added to reference_data
1058+
reference_data.append({reference_name: sex_list})
1059+
1060+
# returns a list of 4 references, each containing 2 lists for each sex,
1061+
# each sex in turn containing 4 datasets for each measurement_method
1062+
return reference_data
1063+
1064+
"""
1065+
# return object structure
1066+
[ cdc_infant: {
1067+
male: {
1068+
height: [
1069+
{
1070+
sds: -2.667,
1071+
centile: 0.4
1072+
data: [{l: , x: , y: }, ....]
1073+
}
1074+
],
1075+
weight: [...],
1076+
bmi: [...],
1077+
ofc: [...]
1078+
},
1079+
female {...}
1080+
},
1081+
cdc_child: {
1082+
male: {
1083+
height: [
1084+
{
1085+
sds: -2.667,
1086+
centile: 0.4
1087+
data: [{l: , x: , y: }, ....]
1088+
}
1089+
],
1090+
weight: [...],
1091+
bmi: [...],
1092+
ofc: [...]
1093+
},
1094+
female {...}
1095+
}
1096+
]
1097+
"""
1098+
1099+
1100+
9111101
def select_centile_format(centile_format: str):
9121102
"""
9131103
Select the centile format

rcpchgrowth/constants/age_constants.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,14 @@
77
FORTY_TWO_WEEKS_GESTATION = ((42 * 7) - (40 * 7)) / 365.25 # 42 weeks as decimal age
88
TWENTY_THREE_WEEKS_GESTATION = ((23 * 7) - (40 * 7)) / 365.25 # 23 weeks as decimal age
99
FIFTY_WEEKS_GESTATION = ((50 * 7) - (40 * 7)) / 365.25 # 50 weeks as decimal age
10+
ZERO_YEARS = 0.0
1011
TWO_YEARS = 2.0
1112
THREE_YEARS = 3.0
13+
FIVE_YEARS = 5.0
14+
TEN_YEARS = 10.0
1215
SEVENTEEN_YEARS = 17.0
1316
EIGHTEEN_YEARS = 18.0
17+
NINETEEN_YEARS = 19.0
1418
TWENTY_YEARS = 20.0
1519
TERM_WEEKS = 40
1620
LOWER_THRESHOLD_PRETERM_REFERENCE_WEEKS = 23

rcpchgrowth/constants/reference_constants.py

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,18 @@
2727
FENTON = "fenton" # Fenton is the reference name for preterm infants
2828
CDC_REFERENCES = [FENTON, CDC_INFANT, CDC_CHILD] # CDC references
2929

30+
# WHO constants
31+
WHO = "who" # WHO is the overarching reference
32+
WHO_2006_INFANT = "who_2006_infant" # WHO 2006 infant is the reference name for children 0-2 years
33+
WHO_2006_CHILD = "who_2006_child" # WHO 2006 child is the reference name for children 2-5 years
34+
WHO_2007_CHILD = "who_2007_child" # WHO 2007 child is the reference name for children 5-19 years
35+
WHO_REFERENCES = [WHO_2006_INFANT, WHO_2006_CHILD, WHO_2007_CHILD] # WHO references
36+
37+
WHO_2006_REFERENCE_LOWER_THRESHOLD = ((42 * 7) - (40 * 7)) / 365.25 # 42 weeks as decimal age # 2 weeks as decimal age
38+
WHO_2006_REFERENCE_UPPER_THRESHOLD = 5.0 # 5 years as decimal age
39+
WHO_2007_REFERENCE_LOWER_THRESHOLD = 5.0 # 5 years as decimal age
40+
WHO_2007_REFERENCE_UPPER_THRESHOLD = 19.0 # 19 years as decimal age
41+
3042
# 23 weeks is the lowest decimal age available on the UK90 charts
3143
UK90_REFERENCE_LOWER_THRESHOLD = (
3244
(23 * 7) - (40 * 7)
@@ -64,7 +76,7 @@
6476
WEIGHT = "weight"
6577
HEAD_CIRCUMFERENCE = "ofc"
6678
BMI = "bmi"
67-
REFERENCES = [UK_WHO, TRISOMY_21, TURNERS]
79+
REFERENCES = [UK_WHO, TRISOMY_21, TRISOMY_21_AAP, TURNERS, CDC, WHO]
6880
SEXES = [MALE, FEMALE]
6981
MEASUREMENT_METHODS = [HEIGHT, WEIGHT, HEAD_CIRCUMFERENCE, BMI]
7082

0 commit comments

Comments
 (0)