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
2 changes: 0 additions & 2 deletions isic_metadata/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
from isic_metadata.diagnosis_hierarchical import DiagnosisEnum
from isic_metadata.fields import (
Age,
AnatomSiteGeneralEnum,
AnatomSiteSpecialEnum,
ClinSizeLongDiamMm,
ColorTintEnum,
Expand All @@ -26,7 +25,6 @@
"FIELD_REGISTRY",
"Age",
"AnatomSiteEnum",
"AnatomSiteGeneralEnum",
"AnatomSiteSpecialEnum",
"ClinSizeLongDiamMm",
"ColorTintEnum",
Expand Down
11 changes: 0 additions & 11 deletions isic_metadata/fields.py
Original file line number Diff line number Diff line change
Expand Up @@ -137,17 +137,6 @@ class MelMitoticIndexEnum(StrEnum):
gt_4 = ">4/mm^2"


class AnatomSiteGeneralEnum(StrEnum):
head_neck = "head/neck"
upper_extremity = "upper extremity"
lower_extremity = "lower extremity"
anterior_torso = "anterior torso"
posterior_torso = "posterior torso"
palms_soles = "palms/soles"
lateral_torso = "lateral torso"
oral_genital = "oral/genital"


class AnatomSiteSpecialEnum(StrEnum):
acral_nos = "acral NOS"
nail_nos = "nail NOS"
Expand Down
57 changes: 0 additions & 57 deletions isic_metadata/metadata.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@
from isic_metadata.diagnosis_hierarchical import DiagnosisEnum
from isic_metadata.fields import (
Age,
AnatomSiteGeneralEnum,
AnatomSiteSpecialEnum,
ClinSizeLongDiamMm,
ColorTintEnum,
Expand Down Expand Up @@ -194,7 +193,6 @@ class MetadataRow(BaseModel):
| None
) = None
sex: Literal["male", "female"] | None = None
anatom_site_general: AnatomSiteGeneralEnum | None = None
anatom_site_special: AnatomSiteSpecialEnum | None = None
anatom_site: (
Annotated[
Expand Down Expand Up @@ -374,7 +372,6 @@ def strip[T](cls, v: T) -> T | None:
return None if v == "" else v

@field_validator(
"anatom_site_general",
"clin_size_long_diam_mm",
"diagnosis_confirm_type",
"mel_mitotic_index",
Expand Down Expand Up @@ -447,60 +444,6 @@ def validate_dermoscopic_fields(self) -> MetadataRow:

return self

@model_validator(mode="after")
def validate_anatom_site_special(self) -> MetadataRow:
if not self.anatom_site_special:
return self

if not self.anatom_site_general:
raise _error_missing_field("anatom_site_special", "anatom_site_general")

valid_combinations = {
AnatomSiteSpecialEnum.acral_nos: [
AnatomSiteGeneralEnum.upper_extremity,
AnatomSiteGeneralEnum.lower_extremity,
AnatomSiteGeneralEnum.palms_soles,
],
AnatomSiteSpecialEnum.nail_nos: [
AnatomSiteGeneralEnum.upper_extremity,
AnatomSiteGeneralEnum.lower_extremity,
AnatomSiteGeneralEnum.palms_soles,
],
AnatomSiteSpecialEnum.fingernail: [
AnatomSiteGeneralEnum.upper_extremity,
AnatomSiteGeneralEnum.palms_soles,
],
AnatomSiteSpecialEnum.toenail: [
AnatomSiteGeneralEnum.lower_extremity,
AnatomSiteGeneralEnum.palms_soles,
],
AnatomSiteSpecialEnum.acral_palms_soles: [
AnatomSiteGeneralEnum.upper_extremity,
AnatomSiteGeneralEnum.lower_extremity,
AnatomSiteGeneralEnum.palms_soles,
],
AnatomSiteSpecialEnum.oral_genital: [
AnatomSiteGeneralEnum.head_neck,
AnatomSiteGeneralEnum.oral_genital,
AnatomSiteGeneralEnum.lower_extremity,
AnatomSiteGeneralEnum.anterior_torso,
AnatomSiteGeneralEnum.posterior_torso,
],
}

if self.anatom_site_special not in valid_combinations:
return self

if self.anatom_site_general not in valid_combinations[self.anatom_site_special]:
raise _error_incompatible_fields(
"anatom_site_special",
"anatom_site_general",
self.anatom_site_special.value,
self.anatom_site_general.value,
)

return self

@model_validator(mode="after")
def validate_tbp_tile_fields(self) -> MetadataRow:
if not self.tbp_tile_type:
Expand Down
6 changes: 0 additions & 6 deletions isic_metadata/registry.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,6 @@ class Field:
"dermoscopic_type",
"tbp_tile_type",
"mel_mitotic_index",
"anatom_site_general",
"anatom_site_special",
"color_tint",
"patient_id",
Expand Down Expand Up @@ -201,8 +200,3 @@ class Field:
FIELD_REGISTRY[field].type = "acquisition"
else:
FIELD_REGISTRY[field].type = "clinical"

for field, label in [
("anatom_site_general", "Anatomic Site"),
]:
FIELD_REGISTRY[field].label = label
48 changes: 0 additions & 48 deletions tests/test_fields.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
import pytest

from isic_metadata.diagnosis_hierarchical import DiagnosisEnum
from isic_metadata.fields import AnatomSiteGeneralEnum
from isic_metadata.metadata import MetadataRow, convert_errors


Expand Down Expand Up @@ -118,50 +117,3 @@ def test_clin_size_long_diam_mm_invalid() -> None:
MetadataRow.model_validate({"clin_size_long_diam_mm": "foo"})
assert len(excinfo.value.errors()) == 1
assert "Unable to parse value as a number" in convert_errors(excinfo.value)[0]["msg"]


@pytest.mark.parametrize(
("anatom_site_special", "anatom_site_general_values"),
[
("acral NOS", ["upper extremity", "lower extremity", "palms/soles"]),
("nail NOS", ["upper extremity", "lower extremity", "palms/soles"]),
("fingernail", ["upper extremity", "palms/soles"]),
("toenail", ["lower extremity", "palms/soles"]),
("acral palms or soles", ["upper extremity", "lower extremity", "palms/soles"]),
(
"oral or genital",
["head/neck", "oral/genital", "lower extremity", "anterior torso", "posterior torso"],
),
],
)
def test_anatom_site_special(
anatom_site_special: str, anatom_site_general_values: list[str]
) -> None:
for anatom_site_general_value in anatom_site_general_values:
metadata = MetadataRow.model_validate(
{
"anatom_site_special": anatom_site_special,
"anatom_site_general": anatom_site_general_value,
}
)
assert metadata.anatom_site_special == anatom_site_special
assert metadata.anatom_site_general == anatom_site_general_value

for invalid_anatom_site_general in AnatomSiteGeneralEnum:
if invalid_anatom_site_general.value not in anatom_site_general_values:
with pytest.raises(ValidationError) as excinfo:
MetadataRow.model_validate(
{
"anatom_site_special": anatom_site_special,
"anatom_site_general": invalid_anatom_site_general.value,
}
)
assert len(excinfo.value.errors()) == 1
assert "is incompatible with anatom_site_general" in excinfo.value.errors()[0]["msg"]


def test_anatom_site_special_requires_anatom_site_general() -> None:
with pytest.raises(ValidationError) as excinfo:
MetadataRow.model_validate({"anatom_site_special": "acral NOS"})
assert len(excinfo.value.errors()) == 1
assert "requires setting anatom_site_general" in excinfo.value.errors()[0]["msg"]
23 changes: 0 additions & 23 deletions tests/test_hierarchical_anatom_site.py
Original file line number Diff line number Diff line change
Expand Up @@ -110,29 +110,6 @@ def test_anatom_site_validation_is_idempotent() -> None:
assert metadata == metadata_2


def test_anatom_site_general_and_special_unaffected() -> None:
metadata = MetadataRow.model_validate(
{
"anatom_site_general": "head/neck",
"anatom_site_special": "oral or genital",
}
)
assert metadata.anatom_site_general == "head/neck"
assert metadata.anatom_site_special == "oral or genital"


def test_anatom_site_general_coexists_with_hierarchical() -> None:
metadata = MetadataRow.model_validate(
{
"anatom_site_general": "head/neck",
"anatom_site": "Head and neck:Head:Scalp",
}
)
assert metadata.anatom_site_general == "head/neck"
assert metadata.anatom_site_1 == "Head and neck"
assert metadata.anatom_site_3 == "Scalp"


def test_invalid_anatom_site_raises_error() -> None:
with pytest.raises(ValidationError):
MetadataRow.model_validate({"anatom_site": "Not a real site"})