Skip to main content

functions

Pure projection of CriteriaEvaluations into the flat csv_report_df.

Reproduces exactly what criteria_matching + csv_report's filter re-run produced before: the boolean FILTER_MATCHING_COLUMN, one boolean column per filter, a comma-joined failure-reason column (rendered against display names), and the context column. Reasons are regenerated structurally from each outcome so no filter is ever re-run. The filename column lets csv_report merge the result onto its freshly-read source data.

Module

Functions

evaluations_to_csv_report_df

def evaluations_to_csv_report_df(    evaluations: list[CriteriaEvaluation],    rename_columns: Mapping[str, str] | None = None,    evaluations_grain: CriterionGrain = scan,)> pandas.core.frame.DataFrame:

Project structured evaluations into the flat report df.

Arguments

  • evaluations: One CriteriaEvaluation per row, from criteria_matching.

  • rename_columns: Display-name map for the failure-reason strings.

  • evaluations_grain: The grain the evaluations were produced on, which selects the merge key csv_report uses to carry this projection back onto the patient/scan demographics. The two grains differ only in what identifies a row:

  • CriterionGrain.SCAN (file-system source): each identity carries a real scan_id, so rows key on ORIGINAL_FILENAME_METADATA_COLUMN (the scan's filename).

  • CriterionGrain.PATIENT (non-file-system / EHR source): every scan_id is None, so rows key on EHR_BITFOUNT_PATIENT_ID_COL (from each identity's bitfount_patient_id) instead — keying on the all-None filename column would drop every row in the merge.

Returns A DataFrame keyed by the grain's merge column (the filename column on the scan grain, EHR_BITFOUNT_PATIENT_ID_COL on the EHR grain) plus FILTER_MATCHING_COLUMN and the comma-joined FILTER_FAILED_REASON_COLUMN — both always present, mirroring legacy criteria_matching, which ran apply_inclusion_filters (seeding FILTER_FAILED_REASON_COLUMN) and always set FILTER_MATCHING_COLUMN (to True for every row when no filter was active). When at least one outcome exists, also: one boolean column per filter (keyed by column_name), a per-MethodFilter "Context for {column_name}" column (only when at least one row has context; mirrors MethodFilter.apply_filter dropping an all-null one), and FILTER_CONTEXT_COLUMN (only when a MethodFilter outcome whose required columns were present exists, mirroring MethodFilter.apply_filter — a ColumnFilter-only evaluation never writes it, and neither does a MethodFilter whose required columns were entirely absent, since legacy returns before ever creating FILTER_CONTEXT_COLUMN in that case).

A MethodFilter whose required columns are entirely absent from the row is out of scope for the per-filter boolean column only (see TestByteParityWithLegacyApplyFilter): legacy writes a descriptive string there instead of a bool, which this projection does not reproduce. FILTER_CONTEXT_COLUMN's presence is still gated correctly in that case (see above) — it is not conflated with a MethodFilter outcome whose method simply returned a non-bool (also state=unknown, context=None, but distinguished by failed_message not matching the absent-columns "unable to determine ... filter" string).