config
Config for the patient enrichment step (v1).
Classes
EnrichmentSource
class EnrichmentSource(**data: Any):One externally-supplied file and how to read it.
Every optional field's unused state is None, so a blanked template
variable needs no _BLANK_TO_DEFAULT_FIELDS handling. The null-to-concrete
fallbacks (patient_id_column, value_column) resolve at the use site
rather than as field defaults, keeping the null-default convention intact.
Identity. A source states who each row is about one of two ways. Either
it carries a ready-made BitfountPatientID (patient_id_column, or the
canonically-named column when neither mode is configured), or it carries
the name and date of birth the ID is derived from (given_name_column +
family_name_column + dob_column). The second is the realistic one for a
site export: the ID is an md5 over a Bitfount-private key, so a site cannot
compute it. Derivation runs through bitfount.steps.patient_identity, the
same code the imaging and EHR routes use, so a supplied row joins the
patient it belongs to rather than minting a second one. _check_identity_shape
rejects the two shapes that would silently empty the source: both modes at
once, and a half-configured name/DOB trio.
Arguments
name: The logical source name. Used to code the facts this source produces, so it is part of the criteria surface.file: The file to read, resolved relative tosettings.paths.enrichment_dir. A path escaping that root is rejected — config names a file, never a location.patient_id_column: The column holding a ready-made BitfountPatientID. Mutually exclusive with the name/DOB columns;Nonewhen the source states name and date of birth instead.given_name_column: The column holding the patient's given name.family_name_column: The column holding the patient's family name.dob_column: The column holding the patient's date of birth, in any spellingpd.to_datetimeaccepts.mrn_column: The column holding the site's own medical record number, if the source states one;Nonestores no MRN. Carried for operator diagnostics only — identity is the derivedBitfountPatientID, never the MRN.value_column: The column holding the measurement, for a source with a single one;Noneuses name. Mutually exclusive with value_columns.value_columns: The columns holding measurements, for a source with several — each minting"<name>_<key>". Mutually exclusive with value_column.laterality_column: The column holding the eye, if the source states one;Noneleaves every fact unlateralised.measured_at_column: The column holding the measurement date, if the source states one;Noneleaves every fact undated.code_system: The code system to stamp on this source's facts;Nonemints the Bitfount enrichment system.
Create a new model by parsing and validating input data from keyword arguments.
Raises [ValidationError][pydantic_core.ValidationError] if the input data cannot be validated to form a valid model.
self is explicitly positional-only to allow self as a field name.
Variables
- static
code_system : str | None
- static
dob_column : str | None
- static
family_name_column : str | None
- static
file : str
- static
given_name_column : str | None
- static
laterality_column : str | None
- static
measured_at_column : str | None
- static
model_config
- static
mrn_column : str | None
- static
name : str
- static
patient_id_column : str | None
- static
value_column : str | None
- static
value_columns : list[EnrichmentValueColumn] | None
-
derives_patient_id : bool- Whether rows carry name/DOB parts rather than a ready-made ID.Set by configuring the name/DOB trio. A source configuring neither identity mode keeps the original default — a
BitfountPatientIDcolumn read straight off the file — so the null-default convention holds and an existing source needs no change.
-
resolved_value_columns : list[EnrichmentValueColumn]- The value columns to read, whichever spelling declared them.A source declaring neither reads one column named for the source itself, which is the pre-existing default.
Methods
code_root_for
def code_root_for( self, value_column: EnrichmentValueColumn,) ‑> str:The code root value_column's facts are addressed by.
A single-value source codes on its name alone, so an existing source's codes do not change when this field gains a list form. A multi-value source qualifies each column by its own key, since one code per source could not tell two measurements apart.
Arguments
value_column: The column to build a code root for.
Returns The code root.
EnrichmentValueColumn
class EnrichmentValueColumn(**data: Any):One column of a source holding a measurement, and how to code it.
Arguments
column: The column to read the measurement from.key: The suffix distinguishing this measurement from the source's others, minting the code"<source>_<key>";Noneuses column. Set it when the site's own column name is not a name a criterion should have to spell — a criterion addressesbcva_cc_logmar, notVA_D_OD_cc_logMar.unit: The unit every value in this column is expressed in, stamped onto facts whose cell does not carry a unit of its own;Noneleaves a plain cell unitless. A criterion bounding a value byUnitBoundmatches nothing without this, since a CSV cell of0.18states no unit anywhere.
Create a new model by parsing and validating input data from keyword arguments.
Raises [ValidationError][pydantic_core.ValidationError] if the input data cannot be validated to form a valid model.
self is explicitly positional-only to allow self as a field name.
Variables
- static
column : str
- static
key : str | None
- static
model_config
- static
unit : str | None
code_suffix : str- The suffix this column contributes to its facts' code.
PatientEnrichmentConfig
class PatientEnrichmentConfig(**data: Any):Config for the patient enrichment step.
sources being None is the whole of the step's optionality: no sources
means nothing is read, nothing is stored, and no criterion can be decided
on supplied data. There is no separate enable flag and no empty-list
sentinel.
Configuring a source is not a purely local change, and this is the paragraph to read before doing it on a live pod. Two effects reach reported eligibility, and neither is a defect:
A criterion's reported provenance follows the row. An observation
criterion reads both Observations and Supplied Observations, so each row's
evidence is stamped ehr or supplied according to which column decided that
row (supplied when both did — see _merged_provenance). A report that
previously said ehr for every observation criterion will say supplied for
the rows a source actually supplied. That is the point of the feature, but it
does mean stored evidence changes shape for a pod that starts supplying data.
A lateralised supplied criterion gates the scan verdict. A criterion with
laterality.side != "either" is scan-grain, because the study eye is
resolved per scan, so it is partitioned into scan_eligibility and can make
scans ineligible. An unlateralised one is patient-grain and gates the patient
verdict via patient_level_eligibility. See build_scan_evidence for why
supplied criteria are partitioned by grain rather than provenance.
So: a source's first real ingest can change a pod's served verdicts, in the direction the criteria ask for. Configure it deliberately, not as a trial.
Arguments
sources: The files to ingest, orNoneto ingest nothing.
Create a new model by parsing and validating input data from keyword arguments.
Raises [ValidationError][pydantic_core.ValidationError] if the input data cannot be validated to form a valid model.
self is explicitly positional-only to allow self as a field name.