Formal Definition of a Patient Record#

Pydantic schema to define a single patient record.

This schema is useful for casting dtypes, as done in validator.cast_dtypes(), validation via is_valid(), and for exporting a JSON schema that may be used for all kinds of purposes, e.g. to automatically generate HTML forms using a JSON-Editor.

lydata.schema.convert_nat(value: Any) Any[source]#

Convert pandas NaT to None.

pydantic throws an unspecific TypeError when pd.NaT is passed to a field. See [this issue on Github](pydantic/pydantic#8039).

class lydata.schema.PatientCore(*, id: str, institution: str, sex: Literal['male', 'female'], age: Annotated[int, Ge(ge=0), Le(le=120)], diagnose_date: Annotated[PastDate, BeforeValidator(func=convert_nat, json_schema_input_type=PydanticUndefined)], alcohol_abuse: bool | None, nicotine_abuse: bool | None, pack_years: Annotated[float | None, Ge(ge=0)] = None, hpv_status: bool | None = None, neck_dissection: bool | None, tnm_edition: Annotated[int, Ge(ge=6), Le(le=8)] = 8, n_stage_prefix: Literal['c', 'p'] | None = None, n_stage: Annotated[int, Ge(ge=-1), Le(le=3)], n_stage_suffix: Literal['a', 'b', 'c'] | None = None, m_stage: Annotated[int | None, Ge(ge=-1), Le(le=1)] = None, weight: Annotated[float | None, Ge(ge=0)] = None)[source]#

Basic required patient information.

This includes demographic information, such as age and sex, as well as some risk factors for head and neck cancer, including HPV status, alcohol and nicotine abuse, etc.

classmethod nan_to_none(value: Any) Any[source]#

Convert NaN values to None to avoid pydantic errors.

classmethod to_lower(value: Any) Any[source]#

Convert some string fields to lower case before validation.

model_config: ClassVar[ConfigDict] = {}#

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

class lydata.schema.PatientRecord(*, core: ~lydata.schema.PatientCore = <factory>)[source]#

A patient’s record.

Because the final dataset has a three-level header, this record holds only the key core under which we store the actual patient information defined in the PatientCore model.

Alongside core, this may at some point hold additional or optional information about the patient.

model_config: ClassVar[ConfigDict] = {}#

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

class lydata.schema.TumorCore(*, location: str, subsite: Annotated[str, _PydanticGeneralMetadata(pattern='C[0-9]{2}(\\.[0-9X])?')], central: bool | None = False, extension: bool | None = False, dist_to_midline: Annotated[float | None, Ge(ge=0)] = None, volume: Annotated[float | None, Ge(ge=0)] = None, t_stage_prefix: Literal['c', 'p'] = 'c', t_stage: Annotated[int, Ge(ge=-1), Le(le=4)], t_stage_suffix: Literal['is', 'a', 'b'] | None = None, side: Literal['left', 'right', 'central'] | None = None)[source]#

Information about the tumor of a patient.

This information characterizes the primary tumor via its location, ICD-O-3 subsite, T-category and so on.

classmethod nan_to_none(value: Any) Any[source]#

Convert NaN values to None.

classmethod to_lower(value: Any) Any[source]#

Convert string values to lower case.

check_tumor_side() TumorCore[source]#

Ensure tumor side information is consistent with central.

check_t_stage() TumorCore[source]#

Ensure T-category is valid.

model_config: ClassVar[ConfigDict] = {}#

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

class lydata.schema.TumorRecord(*, core: ~lydata.schema.TumorCore = <factory>)[source]#

A tumor record of a patient.

As with the PatientRecord, this holds only the key core under which we store the actual tumor information defined in the TumorCore model.

model_config: ClassVar[ConfigDict] = {}#

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

lydata.schema.create_lnl_field(lnl: str) tuple[type, Field][source]#

Create a field for a specific lymph node level.

class lydata.schema.ModalityCore(*, date: Annotated[PastDate | None, BeforeValidator(func=convert_nat, json_schema_input_type=PydanticUndefined)] = None)[source]#

Basic info about a diagnostic/pathological modality.

model_config: ClassVar[ConfigDict] = {}#

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

class lydata.schema.UnilateralInvolvementInfo(*, I: ~types.Annotated[bool | None, ~pydantic.functional_validators.BeforeValidator(func=~lydata.schema.create_lnl_field.<locals>.<lambda>, json_schema_input_type=PydanticUndefined)] = None, Ia: ~types.Annotated[bool | None, ~pydantic.functional_validators.BeforeValidator(func=~lydata.schema.create_lnl_field.<locals>.<lambda>, json_schema_input_type=PydanticUndefined)] = None, Ib: ~types.Annotated[bool | None, ~pydantic.functional_validators.BeforeValidator(func=~lydata.schema.create_lnl_field.<locals>.<lambda>, json_schema_input_type=PydanticUndefined)] = None, II: ~types.Annotated[bool | None, ~pydantic.functional_validators.BeforeValidator(func=~lydata.schema.create_lnl_field.<locals>.<lambda>, json_schema_input_type=PydanticUndefined)] = None, IIa: ~types.Annotated[bool | None, ~pydantic.functional_validators.BeforeValidator(func=~lydata.schema.create_lnl_field.<locals>.<lambda>, json_schema_input_type=PydanticUndefined)] = None, IIb: ~types.Annotated[bool | None, ~pydantic.functional_validators.BeforeValidator(func=~lydata.schema.create_lnl_field.<locals>.<lambda>, json_schema_input_type=PydanticUndefined)] = None, III: ~types.Annotated[bool | None, ~pydantic.functional_validators.BeforeValidator(func=~lydata.schema.create_lnl_field.<locals>.<lambda>, json_schema_input_type=PydanticUndefined)] = None, IV: ~types.Annotated[bool | None, ~pydantic.functional_validators.BeforeValidator(func=~lydata.schema.create_lnl_field.<locals>.<lambda>, json_schema_input_type=PydanticUndefined)] = None, V: ~types.Annotated[bool | None, ~pydantic.functional_validators.BeforeValidator(func=~lydata.schema.create_lnl_field.<locals>.<lambda>, json_schema_input_type=PydanticUndefined)] = None, Va: ~types.Annotated[bool | None, ~pydantic.functional_validators.BeforeValidator(func=~lydata.schema.create_lnl_field.<locals>.<lambda>, json_schema_input_type=PydanticUndefined)] = None, Vb: ~types.Annotated[bool | None, ~pydantic.functional_validators.BeforeValidator(func=~lydata.schema.create_lnl_field.<locals>.<lambda>, json_schema_input_type=PydanticUndefined)] = None, VI: ~types.Annotated[bool | None, ~pydantic.functional_validators.BeforeValidator(func=~lydata.schema.create_lnl_field.<locals>.<lambda>, json_schema_input_type=PydanticUndefined)] = None, VII: ~types.Annotated[bool | None, ~pydantic.functional_validators.BeforeValidator(func=~lydata.schema.create_lnl_field.<locals>.<lambda>, json_schema_input_type=PydanticUndefined)] = None, VIII: ~types.Annotated[bool | None, ~pydantic.functional_validators.BeforeValidator(func=~lydata.schema.create_lnl_field.<locals>.<lambda>, json_schema_input_type=PydanticUndefined)] = None, IX: ~types.Annotated[bool | None, ~pydantic.functional_validators.BeforeValidator(func=~lydata.schema.create_lnl_field.<locals>.<lambda>, json_schema_input_type=PydanticUndefined)] = None, X: ~types.Annotated[bool | None, ~pydantic.functional_validators.BeforeValidator(func=~lydata.schema.create_lnl_field.<locals>.<lambda>, json_schema_input_type=PydanticUndefined)] = None)#
model_config: ClassVar[ConfigDict] = {}#

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

class lydata.schema.ModalityRecord(*, core: ~lydata.schema.ModalityCore = <factory>, ipsi: ~lydata.schema.UnilateralInvolvementInfo = <factory>, contra: ~lydata.schema.UnilateralInvolvementInfo = <factory>)[source]#

Involvement patterns of a diagnostic or pathological modality.

This holds some basic information about the modality, which is currently limited to the date its information was collected (e.g. the date of the PET/CT scan).

Most importantly, this holds the ipsi- and contralateral lymph node level involvement patterns under the respective keys ipsi and contra.

model_config: ClassVar[ConfigDict] = {}#

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

lydata.schema.create_modality_field(modality: str) tuple[type, Field][source]#

Create a field for a specific modality.

class lydata.schema.BaseRecord(*, patient: ~lydata.schema.PatientRecord = <factory>, tumor: ~lydata.schema.TumorRecord = <factory>)[source]#

A basic record of a patient.

Contains at least the patient and tumor information in the same nested form as the data represents it.

model_config: ClassVar[ConfigDict] = {}#

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

lydata.schema.create_full_record_model(modalities: list[str], model_name: str = 'FullRecord', **kwargs: dict[str, Any]) type[source]#

Create a Pydantic model for a full record with all modalities.