types
EHR types.
Module
Functions
dedupe_dataclass_list
def dedupe_dataclass_list(items: list[_T]) ‑> list[~_T]:Remove duplicate dataclass instances from a list, preserving order.
Uses _type_preserving_key() to create hashable representations for
comparison. The first occurrence of each unique item is kept.
Arguments
items: A list of dataclass instances.
Returns A new list with duplicates removed, maintaining original order.
Classes
Allergy
class Allergy( date: datetime | None, code_text: str | None, clinical_status: str | None, reactions: list[AllergyReaction], codings: list[Coding] = [],):Dataclass to describe a patient allergy/intolerance.
code is 0..1 on the source FHIR resource — a resource may carry no
top-level diagnosis code at all and rely entirely on
reactions[].substance_code instead (both are None in that case, not
merely absent). date resolves onsetDateTime → onsetPeriod.start →
recordedDate, the same fallback order Condition.onset_datetime uses.
code can itself carry more than one coding — the same diagnosis
named in more than one system. codings holds every one of them;
code_system/code_code/code_display are computed properties
reading the first entry (None if codings is empty — a resource
already read back from a row an older SDK build wrote, before this
field existed). One Allergy per FHIR resource either way.
Variables
- static
clinical_status : str | None
- static
code_text : str | None
- static
codings : list[Coding]
- static
date : datetime.datetime | None
- static
reactions : list[AllergyReaction]
code_code : str | None- The first coding's code, orNoneifcodingsis empty.
code_display : str | None- The first coding's display, orNoneifcodingsis empty.
code_system : str | None- The first coding's system, orNoneifcodingsis empty.
AllergyReaction
class AllergyReaction(substance_text: str | None, substance_codings: list[Coding] = []):One AllergyIntolerance.reaction entry, flattened to its substance code.
reaction is 0..* on the owning AllergyIntolerance — a single
allergy/intolerance can list more than one reaction, each potentially
naming a different substance (e.g. a drug class entry alongside a
specific drug within it). manifestation/severity/exposureRoute
are not captured here; only substance is needed to match
AllergyCriterion.code against, per
https://hl7.org/fhir/R4/allergyintolerance-definitions.html#AllergyIntolerance.reaction.substance.
substance (like AllergyIntolerance.code) can itself carry more than
one coding. substance_codings holds every one of them.
substance_system/substance_code/substance_display are computed
properties reading the first entry (None if substance_codings is
empty — a reaction already read back from a row an older SDK build
wrote, before this field existed).
Variables
- static
substance_codings : list[Coding]
- static
substance_text : str | None
substance_code : str | None- The first substance coding's code, orNoneif empty.
substance_display : str | None- The first substance coding's display, orNoneif empty.
substance_system : str | None- The first substance coding's system, orNoneif empty.
BodySite
class BodySite( text: str | None = None, bodysite_coding_system: str | None = None, bodysite_coding_code: str | None = None, bodysite_coding_display: str | None = None,):Where on the body a clinical entry applies.
Condition, Procedure, Observation and each DosageInstruction
carry one. On a dosage instruction it is where the medication is
administered, not the site of the problem it treats.
text states the site in words. The three bodysite_coding_* fields
state the same site as a code. A source may give either, both, or
neither.
This does not store laterality. A reader derives the side from text
or from bodysite_coding_code each time, through
bitfount.steps.filters._code_laterality. A site whose wording is
outside that vocabulary reads as indeterminate.
Variables
- static
bodysite_coding_code : str | None
- static
bodysite_coding_display : str | None
- static
bodysite_coding_system : str | None
- static
text : str | None
CodeableConcept
class CodeableConcept( system: str | None, code: str | None, display: str | None, text: str | None,):FHIR CodeableConcept datatype, flattened to its first coding.
Used as Observation.value[x] when the shape is valueCodeableConcept.
Variables
- static
code : str | None
- static
display : str | None
- static
system : str | None
- static
text : str | None
Coding
class Coding(system: str | None, code: str | None, display: str | None):One coding entry within a FHIR CodeableConcept.
A CodeableConcept can carry more than one coding — the same concept
named in more than one system (e.g. a SNOMED coding alongside an
ICD-10 one for the same diagnosis).
Condition
Dataclass to describe patient Condition.
codings holds every coding on the resource's own code — see
Coding's docstring.
Variables
- static
bodysite : BodySite | None
- static
clinical_status : str | None
- static
code_text : str | None
- static
codings : list[Coding]
- static
onset_datetime : datetime.datetime | None
code_code : str | None- The first coding's code, orNoneifcodingsis empty.
code_display : str | None- The first coding's display, orNoneifcodingsis empty.
code_system : str | None- The first coding's system, orNoneifcodingsis empty.
Device
class Device(code_text: str | None, status: str | None, codings: list[Coding] = []):Dataclass to describe a patient device record.
type is 0..1 on the source FHIR resource — a resource may carry no
kind/type code at all (codings empty, not merely absent, in that
case). type can itself carry more than one coding — the same kind
of device named in more than one system. codings holds every one of
them; code_system/code_code/code_display are computed properties
reading the first entry (None if codings is empty). One Device
per FHIR resource.
status is stored for tabulation only — not read by DeviceCriterion,
which matches on codings alone.
Variables
- static
code_text : str | None
- static
codings : list[Coding]
- static
status : str | None
code_code : str | None- The first coding's code, orNoneifcodingsis empty.
code_display : str | None- The first coding's display, orNoneifcodingsis empty.
code_system : str | None- The first coding's system, orNoneifcodingsis empty.
DosageInstruction
class DosageInstruction( text: str | None, route: str | None, dose_and_rate: list[DoseAndRate], frequency: int | None, period: float | None, period_unit: str | None, site: BodySite | None = None, frequency_max: int | None = None, period_max: float | None = None,):One dosage/administration instruction for a Medication.
Normalised from FHIR's Dosage datatype (MedicationRequest .dosageInstruction, MedicationStatement.dosage,
MedicationDispense.dosageInstruction — all 0..*, e.g. for a taper) or
from MedicationAdministration.dosage (a distinct, simpler 0..1
backbone element with no timing, since an administration is a
point-in-time event rather than a schedule).
dose_and_rate holds every dose reading FHIR gave us for this
instruction — see DoseAndRate. It is [] when the instruction has
no structured dose at all; text may still carry a free-text
instruction in that case.
site is where this instruction was administered. FHIR has no
Medication-level bodySite element, unlike Condition/Procedure/
Observation. The body site a medication is given to is a property
of how it is dosed, not of the medication entry as a whole.
Dosage.site/MedicationAdministrationDosage.site are both 0..1,
singular, unlike bodySite's 0..*. A bilateral prescription
carries this as two separate instructions, one per site, rather than
one instruction with two sites.
frequency_max/period_max are the upper end of a repeat stated as a
range — FHIR's Timing.repeat.frequencyMax/periodMax, and NextGen's
frequencyHigh/intervalHigh. "Every 4 to 6 hours" is period 4 and
period_max 6. None means the repeat states a single value, not that
it is unbounded; an instruction with period_max set names a span the
actual interval falls somewhere within, the same way a Range-typed
DoseAndRate does for the dose.
Variables
- static
dose_and_rate : list[DoseAndRate]
- static
frequency : int | None
- static
frequency_max : int | None
- static
period : float | None
- static
period_max : float | None
- static
period_unit : str | None
- static
route : str | None
- static
site : BodySite | None
- static
text : str | None
DoseAndRate
class DoseAndRate( type: "Literal['ordered', 'calculated'] | None", dose_quantity: float | None, dose_low: float | None, dose_high: float | None, dose_unit: str | None,):One dose reading from FHIR's Dosage.doseAndRate list.
Dosage.doseAndRate (MedicationRequest/Statement/Dispense) is a
list because one instruction can carry more than one dose reading for
the same administration. The two readings FHIR's own dose-rate-type
CodeSystem names are ordered (the prescriber's own order, sometimes
in per-weight units such as "mg/kg") and calculated (a separately
computed absolute amount for this specific patient, for example after
a weight- or renal-function-based calculation, or after rounding to
an available tablet strength). We keep every entry instead of
choosing one, since a caller may need to read either value — the
calculated amount is the one in patient-specific absolute units, but
the ordered amount is the prescriber's actual instruction.
type is None for an entry with no type element — common, since
the dose-rate-type binding is optional in FHIR.
MedicationAdministration.dosage.dose has no type element at all
(and is never a list — its single reading is represented the same
way, as a one-entry list on the owning DosageInstruction), but is
stamped "calculated" rather than left None: an administration
records an exact amount actually given, not a prescriber's own
instruction, the same distinction "calculated" already draws on a
Dosage.doseAndRate entry.
The dose amount is itself a FHIR choice type: either a single
quantity (dose_quantity) or a range (dose_low/dose_high, e.g.
"give 1 to 2 tablets"). dose_unit applies to whichever of the two
is set. Exactly one of dose_quantity or the dose_low/dose_high
pair is set for a given entry; the other stays None.
Variables
- static
dose_high : float | None
- static
dose_low : float | None
- static
dose_quantity : float | None
- static
dose_unit : str | None
- static
type : Optional[Literal['ordered', 'calculated']]
DownloadedEHRDocumentInfo
class DownloadedEHRDocumentInfo( *, document_id: str, document_date: str = '', document_description: str = '', extension: str | None = '', source_url: str | None = None, content_type: str | None = None, inline_data: str | None = None, local_path: Path,):Document Info for successfully downloaded documents from EHR.
Ancestors
Subclasses
Variables
- static
local_path : pathlib.Path
Static methods
from_instance
def from_instance(instance: EHRDocumentInfo, **kwargs: Any) ‑> Self:Inherited from:
EHRDocumentInfo.from_instance :
Method to instantiate children class from parent class.
EHRAppointmentEncounter
class EHRAppointmentEncounter( appointment_date: date | None, location_name: str | None, event_name: str | None,):Class for Patient Appointment.
Variables
- static
appointment_date : datetime.date | None
- static
event_name : str | None
- static
location_name : str | None
Methods
format_for_csv
def format_for_csv(self) ‑> dict[str, str]:Format into a readable dictionary for csv.
EHRDocumentInfo
class EHRDocumentInfo( *, document_id: str, document_date: str = '', document_description: str = '', extension: str | None = '', source_url: str | None = None, content_type: str | None = None, inline_data: str | None = None,):Document Information to facilitate download of EHR documents.
Subclasses
Variables
- static
content_type : str | None
- static
document_date : str
- static
document_description : str
- static
document_id : str
- static
extension : str | None
- static
inline_data : str | None
- static
source_url : str | None
Static methods
from_instance
def from_instance(instance: EHRDocumentInfo, **kwargs: Any) ‑> Self:Method to instantiate children class from parent class.
FailedEHRDocumentInfo
class FailedEHRDocumentInfo( *, document_id: str, document_date: str = '', document_description: str = '', extension: str | None = '', source_url: str | None = None, content_type: str | None = None, inline_data: str | None = None, local_path: Path | None = None, failed_reason: str,):Document Info for documents that failed an attempt to upload.
Ancestors
Static methods
from_instance
def from_instance(instance: EHRDocumentInfo, **kwargs: Any) ‑> Self:Inherited from:
EHRDocumentInfo.from_instance :
Method to instantiate children class from parent class.
Medication
class Medication( source_resource: "Literal['MedicationRequest', 'MedicationStatement', 'MedicationDispense', 'MedicationAdministration', 'NextGenMedication']", date: datetime | None, code_text: str | None, status: str | None, dosage: list[DosageInstruction] | None = None, codings: list[Coding] = [], end_date: datetime | None = None, status_reason: str | None = None, reason_codings: list[Coding] = [],):Dataclass to describe a patient medication order, statement, dispense, or admin.
source_resource records which FHIR resource (or, for NextGen, which
single non-FHIR source) this entry came from — a MedicationDispense or
MedicationAdministration entry is materially stronger evidence of
actual use than a MedicationRequest (an order, not proof the patient
took it). date is normalised across each resource's own date field
(MedicationRequest.authoredOn, MedicationStatement .effectiveDateTime/effectivePeriod, MedicationDispense .whenHandedOver/whenPrepared, MedicationAdministration .effectiveDateTime/effectivePeriod) rather than being named after any
one of them, since one field here has to represent all four.
medicationCodeableConcept can carry more than one coding — the same
drug named in more than one system, typically RxNorm alongside NDC.
codings holds every one of them and code_system/code_code/
code_display are computed properties reading the first entry (None
if codings is empty — a row an older SDK build wrote, before this
field existed). One Medication per resource either way, matching
Condition, Procedure and Allergy: emitting one per coding would
make a single prescription count as several when a CountSelector
counts entries.
status uses FHIR's medication status vocabulary, but which codes are
valid depends on source_resource: each of the four FHIR resources
binds its own set, and they differ — intended is in
MedicationStatement's and not in MedicationRequest's, whose
nearest code, draft, means something else. A NextGenMedication
entry is bound to none of them, so it carries whichever code its own
status maps onto. A consumer reading status must not assume any one
resource's value set.
end_date is when the medication stops being in effect
(MedicationStatement/MedicationAdministration.effectivePeriod.end,
or NextGen's stopDate). None means open-ended — still in effect as
far as the source says — and not that it ended at fetch time; a
caller asking "was this in effect during a window" resolves that
against its own reference date, so a cached entry does not go stale.
Indistinguishable from that, on this dataclass alone, is a row an
older SDK build wrote before this field existed — same as
codings above, None is this field's default too. A dict-shaped
entry read back from the cache still tells the two apart, by whether
the key is present at all (see _entry_declares_field in
bitfount.steps.criteria_matching.functions); the distinction is
lost once an entry becomes a Medication instance.
Only a source that describes a period can be open-ended. One that
describes an event sets end_date equal to date, so the entry
occupies the one day it happened. Leaving those None would let
something recorded years ago satisfy a current window for ever.
A MedicationRequest is one of those events. It records that a
prescriber ordered the drug on a date, not that the patient took it or
for how long — a statement, dispense or administration is what says
that. So an entry sourced from a request always ends on its own
date, and an EHR exposing nothing but requests supports "was this
prescribed in the window", not "was the patient taking it then".
status_reason is why the entry reached its status
(MedicationRequest/MedicationStatement.statusReason, or NextGen's
inactiveReason, e.g. "Stopped" vs "Marked Ineffective" — a
distinction status alone loses).
reason_codings is what the medication was ordered for — FHIR's
reasonCode (0..*), or NextGen's up-to-three linked diagnoses. It is
an indication, not an administration site: a drug ordered for a
left-eye condition is usually given to the left eye, but the order has
not said so. It is kept separate from dosage[].site for exactly that
reason, so a consumer can weigh a stated site differently from an
inferred one.
Variables
- static
code_text : str | None
- static
codings : list[Coding]
- static
date : datetime.datetime | None
- static
dosage : list[DosageInstruction] | None
- static
end_date : datetime.datetime | None
- static
reason_codings : list[Coding]
- static
source_resource : Literal['MedicationRequest', 'MedicationStatement', 'MedicationDispense', 'MedicationAdministration', 'NextGenMedication']
- static
status : str | None
- static
status_reason : str | None
code_code : str | None- The first coding's code, orNoneifcodingsis empty.
code_display : str | None- The first coding's display, orNoneifcodingsis empty.
code_system : str | None- The first coding's system, orNoneifcodingsis empty.
Observation
Observation object from FHIR.
value holds whichever ObservationValue variant value[x] populated,
or None if value[x] was absent or an unhandled shape. bodysite
comes from the parent resource's own bodySite (single-valued, unlike
Condition/Procedure's list-valued one) — a component of a
component-bearing Observation has no bodySite of its own, so it
inherits the parent's, the same way it inherits the parent's date.
codings holds every coding on the resource's own code — see
Coding's docstring.
Variables
- static
bodysite : BodySite | None
- static
code_text : str | None
- static
codings : list[Coding]
- static
date : datetime.datetime | None
- static
value : Quantity | CodeableConcept | ValueString | ValueBoolean | ValueInteger | Range | Ratio | Period | ValueTime | ValueDateTime | SampledData | None
code_code : str | None- The first coding's code, orNoneifcodingsis empty.
code_display : str | None- The first coding's display, orNoneifcodingsis empty.
code_system : str | None- The first coding's system, orNoneifcodingsis empty.
Period
class Period(start: datetime | None, end: datetime | None):FHIR Period datatype: a start/end datetime range.
Used as Observation.value[x] when the shape is valuePeriod.
Procedure
Dataclass to describe patient Procedure.
codings holds every coding on the resource's own code — see
Coding's docstring.
Variables
- static
bodysite : BodySite | None
- static
code_text : str | None
- static
codings : list[Coding]
- static
performed_datetime : datetime.datetime | None
code_code : str | None- The first coding's code, orNoneifcodingsis empty.
code_display : str | None- The first coding's display, orNoneifcodingsis empty.
code_system : str | None- The first coding's system, orNoneifcodingsis empty.
Quantity
class Quantity( value: float | None, unit: str | None, comparator: "Literal['<', '<=', '>', '>='] | None" = None,):FHIR Quantity datatype: a measured amount with its unit.
Used both as Observation.value[x] when the shape is valueQuantity,
and nested inside Range/Ratio/SampledData.
Attributes
value: The measured amount. With acomparator, this is the bound of the interval the true value lies in, not the true value.unit: The unit the amount was measured in, if one was stated.comparator: How to readvalue, when the true value could not be measured exactly. FHIR R4 is explicit that this "modifies the meaning of other elements": acomparatorof"<"with avalueof0.02means the true value is below0.02, not that it is0.02.None— the common case — meansvalueis the measurement itself. Below-detection results (troponin, viral load, drug levels, serology) are the usual source.
Variables
- static
comparator : Optional[Literal['<', '<=', '>', '>=']]
- static
unit : str | None
- static
value : float | None
Range
FHIR Range datatype: a low/high Quantity bound.
Used as Observation.value[x] when the shape is valueRange.
Ratio
FHIR Ratio datatype: a numerator/denominator Quantity pair.
Used as Observation.value[x] when the shape is valueRatio.
S3UploadedEHRDocumentInfo
class S3UploadedEHRDocumentInfo( *, document_id: str, document_date: str = '', document_description: str = '', extension: str | None = '', source_url: str | None = None, content_type: str | None = None, inline_data: str | None = None, local_path: Path, s3_key: str, upload_date: str,):Document Info for documents successfully uploaded to S3.
Ancestors
Static methods
from_instance
def from_instance(instance: EHRDocumentInfo, **kwargs: Any) ‑> Self:Inherited from:
DownloadedEHRDocumentInfo.from_instance :
Method to instantiate children class from parent class.
SampledData
class SampledData( origin: Quantity | None, period: float | None, factor: float | None, lower_limit: float | None, upper_limit: float | None, dimensions: int | None, data: str | None,):FHIR SampledData datatype: a series of regularly-sampled values.
Used as Observation.value[x] when the shape is valueSampledData.
data is the raw space-separated encoded sample string, not decoded
into individual values.
Variables
- static
data : str | None
- static
dimensions : int | None
- static
factor : float | None
- static
lower_limit : float | None
- static
origin : Quantity | None
- static
period : float | None
- static
upper_limit : float | None
ValueBoolean
class ValueBoolean(value: bool):Wraps a plain valueBoolean.
Variables
- static
value : bool
ValueDateTime
class ValueDateTime(value: datetime):Wraps a plain valueDateTime.
Variables
- static
value : datetime.datetime
ValueInteger
class ValueInteger(value: int):Wraps a plain valueInteger.
Variables
- static
value : int
ValueString
class ValueString(value: str):Wraps a plain valueString, distinguishing it from valueTime.
FHIR represents both valueString and valueTime as a bare string;
without a wrapper the two shapes would be indistinguishable at runtime.
Variables
- static
value : str
ValueTime
class ValueTime(value: str):Wraps a plain valueTime (a time-of-day, e.g. "13:04:23").
Kept as a string rather than datetime.time, since FHIR time values
carry no date/timezone context. See ValueString for why this needs a
dedicated wrapper.
Variables
- static
value : str