Transformers
Shared helpers for validating vertical-form workbook fields.
- acmad_uploader.transformers.forms.is_supported_version(version)
Return whether a source version denotes template version 3.
- Return type:
bool
- acmad_uploader.transformers.forms.single_field(form, key, issues, *, code_prefix, required=True)
Read one source field and report missing or duplicate keys.
Some workbook variants don’t declare every key at all (for example
site_idexists onphysical_exam.xlsxbut not onfunction_proms.xlsx/mocap.xlsx). Passrequired=Falseto treat an entirely absent key as silently optional rather than an error; a duplicate key is still always reported.- Return type:
SourceField|None
- acmad_uploader.transformers.forms.required_text(form, key, issues, *, code_prefix)
Read a non-blank text field without coercing identifiers.
- Return type:
str|None
- acmad_uploader.transformers.forms.coerce_whole_number(value)
Coerce a whole number from an int, a whole float, or a digit string.
Different sites export the same logically-numeric field with different Excel cell types (e.g.
gaitass_ageas text"3733"at one site, an int elsewhere) — this tolerates either, returningNonefor anything that doesn’t cleanly represent a whole number (booleans, fractional floats, non-numeric text).- Return type:
int|None
- acmad_uploader.transformers.forms.required_int(form, key, issues, *, code_prefix)
Read a whole-number field, rejecting text and fractional values.
- Return type:
int|None
- acmad_uploader.transformers.forms.optional_int(form, key, issues, *, code_prefix, required=True)
Read an optional whole-number field, distinguishing blank from invalid.
requiredcontrols whether the key itself must be declared by the workbook at all; seesingle_field().- Return type:
int|None
- acmad_uploader.transformers.forms.optional_text(form, key, issues, *, code_prefix, required=True)
Read optional text, distinguishing blank values from invalid types.
requiredcontrols whether the key itself must be declared by the workbook at all; seesingle_field().- Return type:
str|None
- acmad_uploader.transformers.forms.coerce_float(value)
Coerce a float from an int, a float, or a numeric string.
Mirrors
coerce_whole_number()but permits fractional values — for real-valued measurement fields (height, mass, …) rather than whole-number fields (age, year, …).- Return type:
float|None
- acmad_uploader.transformers.forms.required_float(form, key, issues, *, code_prefix)
Read a numeric field, rejecting non-numeric text.
- Return type:
float|None
- acmad_uploader.transformers.forms.optional_float(form, key, issues, *, code_prefix, required=True)
Read an optional numeric field, distinguishing blank from invalid.
requiredcontrols whether the key itself must be declared by the workbook at all; seesingle_field().- Return type:
float|None
- acmad_uploader.transformers.forms.choice_text(form, key, issues, *, code_prefix, required=True)
Read a choice-code field, tolerating digit codes stored as numbers.
Some sites enter digit choice codes (e.g.
"0".."5") as numbers rather than text (Excel auto-converts a numeric-looking cell), so this reads the raw value rather than going throughoptional_text()’s text-only requirement. Callers look the returned text up against their ownStrEnum.- Return type:
str|None
- acmad_uploader.transformers.forms.field_location(form, key)
Return the first source location for a field key.
- Return type:
SourceLocation|None
- acmad_uploader.transformers.forms.cross_check_site_id(workbook_value, override, issues, *, code_prefix, location)
Resolve the effective site abbreviation for one workbook.
The workbook’s own
site_idwins whenever it declares one - a supplied override must agree with it (case-insensitively) or this appends a{code_prefix}.site_override_conflicterror, but the workbook’s value is still returned so a caller’s own required-field check doesn’t also (redundantly) fire; the conflict issue alone already fails the enclosing form. The override only fills the gap when the workbook doesn’t declare a value at all, returningNonein that case only if no override was supplied either - callers already have their own required-field handling for that (seerequired_text/optional_text).- Return type:
str|None
Shared extraction of PatientEvent metadata across event-creating slices.
- acmad_uploader.transformers.patient_event.extract_patient_event_details(form, issues, *, code_prefix, year_field, region_field, state_field, country_field, irsd_field, irsad_field, ieo_field, ier_field)
Read the
PatientEventfields a slice’s anchor workbook may declare.Field names differ between workbooks (
gaitass_*vs.surg_res*), so each call site names its own eight source keys explicitly rather than assuming a shared prefix. Every field is read tolerantly (required=False): a workbook variant that doesn’t carry this group at all (e.g.function_proms.xlsx/mocap.xlsx) simply yields an emptyPatientEventDetails.- Return type:
- acmad_uploader.transformers.patient_event.merge_patient_event_details(details)
Coalesce PatientEvent fields from every contributing form.
Only one workbook type ever declares this field group in practice, so this is a simple first-non-
None-wins merge rather than a conflict-checked one (contrastGaitAssessmentTransformer’s stricter handling ofsite_id, which every contributing workbook can independently declare).- Return type:
Transformation of patient workbooks into canonical patient records.
- class acmad_uploader.transformers.patient.PatientTransformer(*, site_override=None)
Bases:
objectValidate version 3 patient forms and map them to domain records.
- transform(extracted)
Transform all forms as one all-or-nothing upload batch.
- Return type:
StageResult[tuple[PatientRecord,...]]
Transformation of patient record fields into canonical diagnosis records.
- class acmad_uploader.transformers.diagnosis.DiagnosisTransformer(*, site_override=None)
Bases:
objectValidate version 3 patient records and extract their diagnosis fields.
A blank diagnosis group is a valid state (a participant recruited to the normative comparison group has no primary diagnosis at all), so absent fields are treated as “no diagnosis of this type”, not an error.
- transform(extracted)
Extract every primary and secondary diagnosis, one batch at a time.
- Return type:
StageResult[tuple[DiagnosisRecord,...]]
Transformation of gait assessment workbooks into canonical event records.
- class acmad_uploader.transformers.gait_assessment.GaitAssessmentTransformer(*, site_override=None)
Bases:
objectValidate version 3 gait assessment forms and join them by natural key.
- transform(extracted)
Join physical exam, function/PROMs and mocap forms by natural key.
- Return type:
StageResult[tuple[GaitAssessmentRecord,...]]
Transformation of function/PROMs workbooks into canonical records.
- class acmad_uploader.transformers.function_assessment.FunctionAssessmentTransformer
Bases:
objectValidate version 3 function/PROMs forms and map them to domain records.
- transform(extracted)
Transform all forms as one all-or-nothing upload batch.
- Return type:
StageResult[tuple[FunctionAssessmentRecord,...]]
Transformation of mocap workbooks into canonical mocap session records.
- class acmad_uploader.transformers.mocap_data.MocapDataTransformer
Bases:
objectValidate version 3 mocap forms and map them to domain records.
- transform(extracted)
Transform all forms as one all-or-nothing upload batch.
- Return type:
StageResult[tuple[MocapDataRecord,...]]
Transformation of mocap session file inventories into artifact records.
- class acmad_uploader.transformers.biomechanics_artifact.BiomechanicsArtifactTransformer(mocap_transformer=None)
Bases:
objectMap every file in a mocap session folder to a biomechanics artifact record.
Every file is a plain upload with no ETL-side processing beyond metadata (role/checksum/content-type) — the destination’s own hooks parse the three “normalised” CSVs into their array-field models server-side (see
CLAUDE.md).- transform(extracted)
Transform every discovered mocap session folder’s files.
- Return type:
StageResult[tuple[BiomechanicsArtifactRecord,...]]
Transformation of physical exam workbooks into canonical records.
- class acmad_uploader.transformers.physical_examination.PhysicalExaminationTransformer
Bases:
objectValidate version 3 physical exam forms and map them to domain records.
- transform(extracted)
Transform all forms as one all-or-nothing upload batch.
- Return type:
StageResult[tuple[PhysicalExaminationRecord,...]]
Transformation of function/PROMs workbooks into canonical PROMs records.
- class acmad_uploader.transformers.proms.PromsTransformer
Bases:
objectValidate version 3 function/PROMs forms and map them to domain records.
- transform(extracted)
Transform all forms as one all-or-nothing upload batch.
- Return type:
StageResult[tuple[PromsRecord,...]]
Transformation of surgery workbooks into canonical intervention episodes.
- class acmad_uploader.transformers.surgery.SurgeryTransformer(*, site_override=None)
Bases:
objectValidate version 3 surgery forms and join them by natural key.
Every numbered procedure slot found across contributing
surgery.xlsxworkbooks for the same(site_patient_id, age_in_days)is aggregated into one episode, the same “multiple entries per day” behaviourGaitAssessmentTransformeralready applies to its own siblings.- transform(extracted)
Join every contributing workbook’s procedures by natural key.
- Return type:
StageResult[tuple[SurgeryRecord,...]]