Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions src/igh_data_transform/transformations/diseases.py
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,36 @@ def transform_diseases(
mask = has_dash & suffix_matches_secondary
df.loc[mask, "disease_filter"] = parent_candidate[mask]

# =========================================================
# Display label (single source of truth)
# =========================================================
#
# The portal prints ONE disease label in tables, slide-ins and the
# per-column table filter. The agreed rule:
# * default -> the secondary disease (COVID-19, Lassa fever,
# Gonorrhea, …);
# * no secondary -> the primary disease group (Tuberculosis,
# Buruli ulcer);
# * Malaria (the -> "<primary> – <secondary>", because the strains
# only outlier) (P. falciparum, P. vivax) never stand alone.
# Computing it here keeps the rule in one tested place; downstream
# (Gold, GraphQL, the React tables/slide-ins) just passes it through.
# Both inputs are already normalized above, so "no secondary" is
# exactly `secondary_disease_name IS NULL` here.
if "disease_filter" in df.columns and "secondary_disease_name" in df.columns:
primary = df["disease_filter"]
secondary = df["secondary_disease_name"]
has_secondary = secondary.notna()

# Default branch: the secondary when present, else the primary group.
label = secondary.where(has_secondary, primary)

# Malaria outlier: prefix the primary so the strain never stands alone.
malaria = has_secondary & primary.eq("Malaria")
label = label.mask(malaria, primary.str.cat(secondary, sep=" – "))

df["disease_label"] = label

cleaned_option_sets: dict[str, pd.DataFrame] = {}

if option_sets and "_optionset_new_globalhealtharea" in option_sets:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,10 @@
# any new joins to filter by either.
"disease_filter": "disease_filter",
"secondary_disease_name": "secondary_disease_name",
# Pre-computed canonical display label (primary/secondary/Malaria
# rule). Tables, slide-ins and the per-column table filter all read
# this so there is one definition of how a disease is named.
"disease_label": "disease_label",
},
"dim_phase": {
"_source_table": "vin_rdstages",
Expand Down
1 change: 1 addition & 0 deletions tests/e2e/test_silver_to_gold_e2e.py
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,7 @@ def test_dim_disease_has_expected_columns(self, gold_conn):
"disease_name",
"disease_group_name",
"global_health_area",
"disease_label",
}
missing = expected - cols
assert not missing, f"Missing columns: {missing}"
Expand Down
73 changes: 73 additions & 0 deletions tests/unit/test_diseases.py
Original file line number Diff line number Diff line change
Expand Up @@ -309,3 +309,76 @@ def test_normalizes_sti_primary_when_suffix_matches_secondary(self):
# Secondary unchanged.
assert result["secondary_disease_name"].iloc[0] == "Gonorrhea"
assert result["secondary_disease_name"].iloc[1] == "Different"

def test_disease_label_prefers_secondary(self):
# Default rule: when a secondary disease exists, it is the label
# (the primary group is implied by context).
df = self._make_input_df(
overrides={
"new_diseasefilter": ["Coronaviral diseases", "Filoviral diseases"],
"new_secondary_diseae_choice_text": ["COVID-19", "Ebola"],
}
)
result, _ = transform_diseases(df)
assert result["disease_label"].iloc[0] == "COVID-19"
assert result["disease_label"].iloc[1] == "Ebola"

def test_disease_label_falls_back_to_primary_without_secondary(self):
# No secondary -> show the primary disease group. The sentinel
# "No secondary disease" has already been collapsed to NULL.
df = self._make_input_df(
overrides={
"new_diseasefilter": ["Tuberculosis", "Buruli ulcer"],
"new_secondary_diseae_choice_text": [None, "No secondary disease"],
}
)
result, _ = transform_diseases(df)
assert result["disease_label"].iloc[0] == "Tuberculosis"
assert result["disease_label"].iloc[1] == "Buruli ulcer"

def test_disease_label_combines_for_malaria(self):
# Malaria is the only outlier: its strains never stand alone, so
# the label is "<primary> – <secondary>" with a spaced en dash.
df = self._make_input_df(
overrides={
"new_diseasefilter": ["Malaria", "Malaria"],
"new_secondary_diseae_choice_text": ["P. falciparum", "P. vivax"],
}
)
result, _ = transform_diseases(df)
assert result["disease_label"].iloc[0] == "Malaria – P. falciparum"
assert result["disease_label"].iloc[1] == "Malaria – P. vivax"

def test_disease_label_for_sti_uses_secondary(self):
# STIs arrive parent-collapsed with the specific infection in the
# secondary field, so the default branch already prints it.
df = self._make_input_df(
overrides={
"new_diseasefilter": [
"Sexually transmitted infections (STIs) - Gonorrhea",
"Sexually transmitted infections (STIs)",
],
"new_secondary_diseae_choice_text": [
"Gonorrhea",
"Trichomoniasis",
],
}
)
result, _ = transform_diseases(df)
assert result["disease_label"].iloc[0] == "Gonorrhea"
assert result["disease_label"].iloc[1] == "Trichomoniasis"

def test_disease_label_is_null_when_both_inputs_missing(self):
# Cross-cutting "R&D for all global health areas" rows carry
# neither a primary filter nor a secondary -> no label. The
# second row keeps a secondary populated so the column survives
# `drop_empty_columns` (in production it always has values).
df = self._make_input_df(
overrides={
"new_diseasefilter": [None, "Dengue"],
"new_secondary_diseae_choice_text": [None, "Severe dengue"],
}
)
result, _ = transform_diseases(df)
assert pd.isna(result["disease_label"].iloc[0])
assert result["disease_label"].iloc[1] == "Severe dengue"