v1
Patient-eligibility reduce step v1.
Module
Submodules
- bitfount.steps.patient_eligibility.v1.config - Config for the patient_eligibility step (v1).
- bitfount.steps.patient_eligibility.v1.result - Result for the patient_eligibility step (v1).
- bitfount.steps.patient_eligibility.v1.task - Prefect task for the patient_eligibility reduce step.
Functions
task_fn
def task_fn( scan_eligibility: CacheAccessor | None, patient_level_eligibility: CacheAccessor | None, cache: CacheProtocol, task_hash: str, project_id: str, parent_task_hashes: dict[str, str] | None = None, config: PatientEligibilityConfig | None = None, run_id: str | None = None,) ‑> PatientEligibilityResult:Combine per-scan + patient-level verdicts into per-patient rows; flip pointer.
The rollup rows are always written. The pointer flip is withheld for an
empty run, and for one run when this run would publish zero eligible
patients on EHR coverage worse than the currently-published partition's —
see _withhold_publish, which also documents the case this does not
cover. A flip that happens records that coverage and the partition's EHR
staleness span in the pointer's tags.
Arguments
scan_eligibility: The upstreamscan_eligibilityedge. Its value is unused — this step reduces the table through direct store calls, which an accessor's grouped queries cannot express — but declaring it is this step's dependency contract, and the name is the key its partition appears under inparent_task_hashes.patient_level_eligibility: The upstreampatient_level_eligibilityedge, declared on the same basis.cache: The pod background cache to read verdicts from and write rollups to.task_hash: This step's own partition key — where the rollup rows are written, and what the published pointer names for this table.project_id: The trial (project) ID (a runtime param).parent_task_hashes: Partition key of each upstream step, keyed by this step's input parameter name (bitfount.flows.dag.hashing). Where the two source tables are read from — this step's owntask_hashwould find nothing in them.Nonebecause the executor injects it only for a stamped DAG; an unstamped one then reaches the explicit error below rather than aTypeErrorabout a missing argument.config: Optional step config (trial display name); nothing in it is persisted by this step.run_id: Optional provenance run ID.
Returns
PatientEligibilityResult with the number of patient_eligibility rows
written and a cache accessor scoped to this task_hash.
Raises
ValueError: Ifproject_idis not supplied, or parent_task_hashes is missing either upstream partition — without which this would silently roll up zero patients and leave the pointer on the previous run's data.
Classes
Config
class Config(**data: Any):Config for the patient_eligibility reduce step.
Arguments
trial_name: Optional display name for the trial, stored for the UI.
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.
Result
class Result(**data: Any):Result of a patient_eligibility reduce task.
records_stored reports how many patient_eligibility rows were persisted;
cache exposes the CacheAccessor over them for downstream DAG steps.
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.
Ancestors
Variables
- static
model_config
cache : CacheAccessor | None- Return theCacheAccessorfor 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.DataFramewith 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>.cacheinstead.
Static methods
from_accessor
def from_accessor( accessor: CacheAccessor, *, records_stored: int | None = None,) ‑> Self:Inherited from:
CacheBackedResult.from_accessor :
Build a result carrying accessor, in one call rather than two.
_accessor is a PrivateAttr, so it cannot be passed to the
constructor and every producer would otherwise construct the result and
then reach in to attach the accessor. That two-step is the shape this
replaces.
Arguments
accessor: TheCacheAccessorbacking<step>.cache, scoped to the partition this result stands for.records_stored: Rows this result stands for. Defaults toaccessor.count()— the whole partition — which is what a step that persisted nothing this run reports. A step that wrote rows passes what it wrote; see the field's docstring for why the two are not interchangeable.
Returns An instance of the calling subclass, 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.