Skip to content

HumanSubject species validator not working correctly with JSON/dictionary input #59

Description

@gouwens

Describe the bug
The HumanSubject validator that checks that the species field is human does not work properly when validating a model from dictionaries read in from a JSON file.

To Reproduce

import json
from aind_data_schema.components.subjects import HumanSubject, Sex
from aind_data_schema_models.species import Species, SpeciesModel
from aind_data_schema_models.organizations import Organization

# Make a dictionary directly from the schema
my_human_dict = json.loads(Species.HUMAN.model_dump_json())

# Make a HumanSubject using Species.HUMAN -- works
HumanSubject.model_validate({
	"species": Species.HUMAN,
	"sex": "Male",
	"year_of_birth": 1948,
	"source": Organization.AI
})
# HumanSubject(object_type='Human subject', species=_Homo_Sapiens(name='Homo sapiens', common_name='Human', registry=<Registry.NCBI: 'National Center for Biotechnology Information (NCBI)'>, registry_identifier='NCBI:txid9606'), sex='Male', year_of_birth=1948, source=_Allen_Institute(name='Allen Institute', abbreviation='AI', registry=<Registry.ROR: 'Research Organization Registry (ROR)'>, registry_identifier='03cpe7c52'))

# Make a HumanSubject using the dictionary -- fails
HumanSubject.model_validate({
	"species": my_human_dict,
	"sex": "Male",
	"year_of_birth": 1948,
	"source": Organization.AI
})
# pydantic_core._pydantic_core.ValidationError: 1 validation error for HumanSubject
# species
#   Value error, HumanSubject species must be HUMAN [type=value_error, input_value={'name': 'Homo sapiens', ...ifier': 'NCBI:txid9606'}, input_type=dict]
#     For further information visit https://errors.pydantic.dev/2.11/v/value_error

Expected behavior
Should return a valid HumanSubject.

I was able to get a version of HumanSubject to work like this if I changed the species field to be Species.ONE_OF (like the other species-specific subject models) and change the field_validator parameter to mode="after". But this does lose the default of Species.HUMAN --- not sure how to keep that. I did need to make both changes for it to work.

from pydantic import Field, field_validator, model_validator
from aind_data_schema.base import DataModel

class MyHumanSubject(DataModel):
    """Description of a human subject that validates with a dictionary for Species"""

    species: Species.ONE_OF = Field(..., title="species") # Changed from original SpeciesModel = Field(default=Species.HUMAN, title="Species")
    sex: Sex = Field(..., title="Sex")
    year_of_birth: int = Field(..., title="Year of birth")
    source: Organization.ONE_OF = Field(
        ...,
        description="Where the subject was acquired from.",
        title="Source",
    )

    @field_validator("species", mode="after") # Mode changed from "before" in original
    def validate_species_is_human(cls, v):
        """Ensure species is always human for HumanSubject"""
        if v != Species.HUMAN:
            raise ValueError("HumanSubject species must be HUMAN")
        return v


# Make a HumanSubject using Species.HUMAN -- still works
MyHumanSubject.model_validate({
	"species": Species.HUMAN,
	"sex": "Male",
	"year_of_birth": 1948,
	"source": Organization.AI
})
# MyHumanSubject(object_type='My human subject', species=_Homo_Sapiens(name='Homo sapiens', common_name='Human', registry=<Registry.NCBI: 'National Center for Biotechnology Information (NCBI)'>, registry_identifier='NCBI:txid9606'), sex='Male', year_of_birth=1948, source=_Allen_Institute(name='Allen Institute', abbreviation='AI', registry=<Registry.ROR: 'Research Organization Registry (ROR)'>, registry_identifier='03cpe7c52'))


# Make a HumanSubject using the dictionary -- now works
MyHumanSubject.model_validate({
	"species": my_human_dict,
	"sex": "Male",
	"year_of_birth": 1948,
	"source": Organization.AI
})
# MyHumanSubject(object_type='My human subject', species=_Homo_Sapiens(name='Homo sapiens', common_name='Human', registry=<Registry.NCBI: 'National Center for Biotechnology Information (NCBI)'>, registry_identifier='NCBI:txid9606'), sex='Male', year_of_birth=1948, source=_Allen_Institute(name='Allen Institute', abbreviation='AI', registry=<Registry.ROR: 'Research Organization Registry (ROR)'>, registry_identifier='03cpe7c52'))


# Make a HumanSubject using the wrong species -- fails as expected
MyHumanSubject.model_validate({
	"species": Species.RHESUS_MACAQUE,
	"sex": "Male",
	"year_of_birth": 1948,
	"source": Organization.AI
})
# pydantic_core._pydantic_core.ValidationError: 1 validation error for MyHumanSubject
# species
#   Value error, HumanSubject species must be HUMAN [type=value_error, input_value=_Macaca_Mulatta(name='Mac...ntifier='NCBI:txid9544'), input_type=_Macaca_Mulatta]
#     For further information visit https://errors.pydantic.dev/2.11/v/value_error

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions