Skip to main content

result

Result for the EHR query step (v3).

Classes

EHRQueryResult

class EHRQueryResult(**data: Any):

Result of a best-effort EHR criteria query task.

records_stored reports how many EHR rows this run persisted to the cache; cache exposes the CacheAccessor over the partition for downstream DAG steps. The accessor is present even when this run stored nothing — with it unwired, criteria_matching has no EHR columns to tag and reports EHR criteria as scan evidence with provenance unknown.

The remaining fields exist because a run that could not reach the EHR is otherwise indistinguishable from a quiet success: the write guard withholds a failure record rather than overwriting a stored row, so the attempt leaves no trace in ehr_data at all.

lister_truncated is deliberately absent: the truncation it reports belongs to ehr_patient_lister, a separate step, and this step's template wires no lister at all.

Attributes

  • run_outcome: How well the run primed the serving layer. See EHRRunOutcome. Required, with no default: a result that did not say how the run went must not be readable as a healthy prime.
  • skipped_total_failure: Patients whose lookup produced nothing usable this run and whose failure record the write guard withheld in favour of a stored row. Those patients are served from a row as old as the last run that reached the EHR for them, and nothing in the table records this run's attempt. A failure the guard did write is not counted here — it is readable as that row's error.
  • skipped_unidentified: Rows carrying nothing to look a patient up by. No EHR query was made for them, and no row can record that: the reason fires before a bitfount_patient_id — the ehr_data primary key — exists to key one on.
  • skipped_incomplete_name: Rows whose name did not yield both a given and a family name. Reported here for the same reason.
  • skipped_unparseable_dob: Rows whose date of birth could not be parsed. Reported here for the same reason.
  • groups_unsupported: The fetch groups this backend reported as structurally lacking, unioned over the patients queried. Sourced from the in-memory fetch outcomes, since ehr_data.error holds one patient-scope reason and cannot represent per-group state. Sticky across runs — a configuration finding, not an outage.

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 model_config
  • static skipped_incomplete_name : int
  • static skipped_total_failure : int
  • static skipped_unidentified : int
  • static skipped_unparseable_dob : int
  • cache : CacheAccessor | None - Return the CacheAccessor for reading this result in a DAG pipeline.
  • records : pandas.core.frame.DataFrame - Retrieve this result's rows as a DataFrame.

    The DataFrame is fetched from the cache on each access — it is not stored in the result object, allowing lazy access to large datasets without materialising them in memory until needed.

    Returns: A pandas.DataFrame with one row per cached record.

    Raises: RuntimeError: If no accessor is attached (e.g. the result was serialised across a Prefect task boundary). In a DAG pipeline use <step_name>.cache instead.

Static methods


from_accessor

def from_accessor(    accessor: CacheAccessor,    *,    records_stored: int | None = None,    run_outcome: EHRRunOutcome = not_configured,    skipped_total_failure: int = 0,    skipped_unidentified: int = 0,    skipped_incomplete_name: int = 0,    skipped_unparseable_dob: int = 0,    groups_unsupported: tuple[EHRUnavailableReason, ...] = (),)> Self:

Build a result carrying accessor and this run's status.

Widens the base classmethod with the run-status fields, so the task still builds its result in one call rather than constructing it and reaching in to attach the accessor.

Arguments

  • accessor: The CacheAccessor backing <step>.cache, scoped to this run's partition. Passed even when the run stored no rows.
  • records_stored: Rows this run persisted. Defaults to the whole partition's count, per the base class.
  • run_outcome: How well the run primed the serving layer. Defaults to NOT_CONFIGURED rather than PRIMED, so the generic cross-phase reconstruction in flows/dag/context.py — which cannot know how the background run went — cannot claim a healthy prime on its behalf.
  • skipped_total_failure: Failure records the write guard withheld.
  • skipped_unidentified: Rows with no usable query key.
  • skipped_incomplete_name: Rows with an unsplittable name.
  • skipped_unparseable_dob: Rows with an unparseable date of birth.
  • groups_unsupported: Fetch groups the backend structurally lacks.

Returns The result, with the accessor attached.

Methods


model_post_init

def model_post_init(self: BaseModel, context: Any, /)> None:

Inherited from:

CacheBackedResult.model_post_init :

This function is meant to behave like a BaseModel method to initialise private attributes.

It takes context as an argument since that's what pydantic-core passes when calling it.

Arguments

  • self: The BaseModel instance.
  • context: The context.