diff --git a/backend/src/main/java/gov/cdc/usds/simplereport/api/model/filerow/TestResultRow.java b/backend/src/main/java/gov/cdc/usds/simplereport/api/model/filerow/TestResultRow.java index dfc0617e1d..d3fb918fb6 100644 --- a/backend/src/main/java/gov/cdc/usds/simplereport/api/model/filerow/TestResultRow.java +++ b/backend/src/main/java/gov/cdc/usds/simplereport/api/model/filerow/TestResultRow.java @@ -14,6 +14,7 @@ import static gov.cdc.usds.simplereport.validators.CsvValidatorUtils.validateEmail; import static gov.cdc.usds.simplereport.validators.CsvValidatorUtils.validateEthnicity; import static gov.cdc.usds.simplereport.validators.CsvValidatorUtils.validateGendersOfSexualPartners; +import static gov.cdc.usds.simplereport.validators.CsvValidatorUtils.validatePatientGenderIdentity; import static gov.cdc.usds.simplereport.validators.CsvValidatorUtils.validatePhoneNumber; import static gov.cdc.usds.simplereport.validators.CsvValidatorUtils.validateRace; import static gov.cdc.usds.simplereport.validators.CsvValidatorUtils.validateRequiredFieldsForPositiveResult; @@ -54,7 +55,17 @@ public class TestResultRow implements FileRow { final ValueOrError patientCounty; final ValueOrError patientPhoneNumber; final ValueOrError patientDob; + + /** + * This field "patient_gender" refers to the patient's sex assigned at birth.
+ * + *

When "patient_gender_identity" was added as a field in 2024, the team decided to keep this + * current column name of "patient_gender" instead of changing the header to something like + * "patient_sex_assigned_at_birth". This decision was made to maintain compatibility for existing + * bulk upload users. + */ final ValueOrError patientGender; + final ValueOrError patientRace; final ValueOrError patientEthnicity; final ValueOrError patientPreferredLanguage; @@ -107,6 +118,7 @@ public class TestResultRow implements FileRow { final ValueOrError testOrderedCode; final ValueOrError gendersOfSexualPartners; final ValueOrError syphilisHistory; + final ValueOrError patientGenderIdentity; static final String PATIENT_LAST_NAME = "patient_last_name"; static final String PATIENT_FIRST_NAME = "patient_first_name"; @@ -152,6 +164,7 @@ public class TestResultRow implements FileRow { public static final String ORDERING_FACILITY_PHONE_NUMBER = "ordering_facility_phone_number"; public static final String GENDERS_OF_SEXUAL_PARTNERS = "genders_of_sexual_partners"; public static final String SYPHILIS_HISTORY = "syphilis_history"; + public static final String PATIENT_GENDER_IDENTITY = "patient_gender_identity"; public static final ImmutableMap diseaseSpecificLoincMap = new ImmutableMap.Builder() @@ -456,6 +469,8 @@ public TestResultRow(Map rawRow) { gendersOfSexualPartners = getValue(rawRow, GENDERS_OF_SEXUAL_PARTNERS, isRequired(GENDERS_OF_SEXUAL_PARTNERS)); syphilisHistory = getValue(rawRow, SYPHILIS_HISTORY, isRequired(SYPHILIS_HISTORY)); + patientGenderIdentity = + getValue(rawRow, PATIENT_GENDER_IDENTITY, isRequired(PATIENT_GENDER_IDENTITY)); } private List validateDeviceModelAndTestPerformedCode( @@ -612,6 +627,8 @@ public List validateIndividualValues() { errors.addAll(validateGendersOfSexualPartners(gendersOfSexualPartners)); + errors.addAll(validatePatientGenderIdentity(patientGenderIdentity)); + if (isHivResult()) { errors.addAll( validateRequiredFieldsForPositiveResult( diff --git a/backend/src/main/java/gov/cdc/usds/simplereport/db/model/PersonUtils.java b/backend/src/main/java/gov/cdc/usds/simplereport/db/model/PersonUtils.java index 9dfac6e823..d5d8f549e8 100644 --- a/backend/src/main/java/gov/cdc/usds/simplereport/db/model/PersonUtils.java +++ b/backend/src/main/java/gov/cdc/usds/simplereport/db/model/PersonUtils.java @@ -843,6 +843,7 @@ public static Map getGenderIdentityAbbreviationMap() { genderMap.put("M", MALE); genderMap.put("MALE", MALE); genderMap.put("NB", NON_BINARY); + genderMap.put("NON BINARY", NON_BINARY); genderMap.put("NONBINARY", NON_BINARY); genderMap.put("TM", TRANS_MAN); genderMap.put("TRANS MAN", TRANS_MAN); diff --git a/backend/src/main/java/gov/cdc/usds/simplereport/utils/BulkUploadResultsToFhir.java b/backend/src/main/java/gov/cdc/usds/simplereport/utils/BulkUploadResultsToFhir.java index 0a8997e076..7df1d232d9 100644 --- a/backend/src/main/java/gov/cdc/usds/simplereport/utils/BulkUploadResultsToFhir.java +++ b/backend/src/main/java/gov/cdc/usds/simplereport/utils/BulkUploadResultsToFhir.java @@ -528,6 +528,16 @@ private Bundle convertRowToFhirBundle(TestResultRow row, UUID orgId) { fhirConverter.convertToAOEGenderOfSexualPartnersObservation(abbrConvertedGenders)); } + String patientGenderIdentity = row.getPatientGenderIdentity().getValue(); + if (StringUtils.isNotBlank(patientGenderIdentity)) { + Map genderAbbreviationMap = getGenderIdentityAbbreviationMap(); + String abbrConvertedGenderIdentity = + genderAbbreviationMap.get(patientGenderIdentity.toUpperCase()); + aoeObservations.addAll( + fhirConverter.convertToAOEGenderIdentityObservation( + testEventId, abbrConvertedGenderIdentity)); + } + var serviceRequest = fhirConverter.convertToServiceRequest( ServiceRequest.ServiceRequestStatus.COMPLETED, diff --git a/backend/src/main/java/gov/cdc/usds/simplereport/validators/CsvValidatorUtils.java b/backend/src/main/java/gov/cdc/usds/simplereport/validators/CsvValidatorUtils.java index 3556caf950..902c793ffe 100644 --- a/backend/src/main/java/gov/cdc/usds/simplereport/validators/CsvValidatorUtils.java +++ b/backend/src/main/java/gov/cdc/usds/simplereport/validators/CsvValidatorUtils.java @@ -660,6 +660,26 @@ public static List validateGendersOfSexualPartners(ValueOrError return errors; } + public static List validatePatientGenderIdentity(ValueOrError input) { + List errors = new ArrayList<>(); + String value = parseString(input.getValue()); + if (value == null) { + return errors; + } + if (!getGenderIdentityAbbreviationMap().containsKey(value.toUpperCase())) { + errors.add( + FeedbackMessage.builder() + .scope(ITEM_SCOPE) + .fieldHeader(input.getHeader()) + .source(ResultUploadErrorSource.SIMPLE_REPORT) + .message(getInvalidValueErrorMessage(input.getValue(), input.getHeader())) + .errorType(ResultUploadErrorType.INVALID_DATA) + .fieldRequired(input.isRequired()) + .build()); + } + return errors; + } + public static List validateRequiredFieldsForPositiveResult( ValueOrError testResult, String diseaseName, List fields) { List errors = new ArrayList<>(); diff --git a/backend/src/test/java/gov/cdc/usds/simplereport/utils/BulkUploadResultsToFhirTest.java b/backend/src/test/java/gov/cdc/usds/simplereport/utils/BulkUploadResultsToFhirTest.java index 6cfd895011..e70cbb8c6a 100644 --- a/backend/src/test/java/gov/cdc/usds/simplereport/utils/BulkUploadResultsToFhirTest.java +++ b/backend/src/test/java/gov/cdc/usds/simplereport/utils/BulkUploadResultsToFhirTest.java @@ -214,7 +214,7 @@ void convertExistingCsv_aoeQuestionsMapped() { .filter( observation -> observation.getResource().getNamedProperty("identifier").hasValues()) .toList(); - assertThat(asymptomaticNotCongregateAOE).hasSize(11); + assertThat(asymptomaticNotCongregateAOE).hasSize(12); var symptomaticCongregateSettingEntry = serializedBundles.get(1); var deserializedSymptomaticCongregateEntry = @@ -227,7 +227,7 @@ void convertExistingCsv_aoeQuestionsMapped() { symptomaticCongregateObservations.stream() .filter(obs -> obs.getResource().getNamedProperty("identifier").hasValues()) .toList(); - assertThat(symptomaticCongregateAOE).hasSize(10); + assertThat(symptomaticCongregateAOE).hasSize(11); } private InputStream loadCsv(String csvFile) { diff --git a/backend/src/test/java/gov/cdc/usds/simplereport/validators/CsvValidatorUtilsTest.java b/backend/src/test/java/gov/cdc/usds/simplereport/validators/CsvValidatorUtilsTest.java index af37d86296..c5cdcb703d 100644 --- a/backend/src/test/java/gov/cdc/usds/simplereport/validators/CsvValidatorUtilsTest.java +++ b/backend/src/test/java/gov/cdc/usds/simplereport/validators/CsvValidatorUtilsTest.java @@ -10,6 +10,7 @@ import static gov.cdc.usds.simplereport.validators.CsvValidatorUtils.validateFlexibleDate; import static gov.cdc.usds.simplereport.validators.CsvValidatorUtils.validateGendersOfSexualPartners; import static gov.cdc.usds.simplereport.validators.CsvValidatorUtils.validatePartialUnkAddress; +import static gov.cdc.usds.simplereport.validators.CsvValidatorUtils.validatePatientGenderIdentity; import static gov.cdc.usds.simplereport.validators.CsvValidatorUtils.validatePhoneNumber; import static gov.cdc.usds.simplereport.validators.CsvValidatorUtils.validateRequiredFieldsForPositiveResult; import static gov.cdc.usds.simplereport.validators.CsvValidatorUtils.validateSpecimenType; @@ -290,6 +291,61 @@ void invalidGendersOfSexualPartners() { assertThat(validateGendersOfSexualPartners(genders)).hasSize(1); } + @Test + void validPatientGenderIdentity() { + assertThat(validatePatientGenderIdentity(new ValueOrError("f", "patient_gender_identity"))) + .isEmpty(); + assertThat(validatePatientGenderIdentity(new ValueOrError("m", "patient_gender_identity"))) + .isEmpty(); + assertThat(validatePatientGenderIdentity(new ValueOrError("nb", "patient_gender_identity"))) + .isEmpty(); + assertThat(validatePatientGenderIdentity(new ValueOrError("tm", "patient_gender_identity"))) + .isEmpty(); + assertThat(validatePatientGenderIdentity(new ValueOrError("tw", "patient_gender_identity"))) + .isEmpty(); + assertThat(validatePatientGenderIdentity(new ValueOrError("o", "patient_gender_identity"))) + .isEmpty(); + assertThat(validatePatientGenderIdentity(new ValueOrError("r", "patient_gender_identity"))) + .isEmpty(); + assertThat(validatePatientGenderIdentity(new ValueOrError("female", "patient_gender_identity"))) + .isEmpty(); + assertThat(validatePatientGenderIdentity(new ValueOrError("male", "patient_gender_identity"))) + .isEmpty(); + assertThat( + validatePatientGenderIdentity( + new ValueOrError("non binary", "patient_gender_identity"))) + .isEmpty(); + assertThat( + validatePatientGenderIdentity(new ValueOrError("nonbinary", "patient_gender_identity"))) + .isEmpty(); + assertThat( + validatePatientGenderIdentity(new ValueOrError("trans man", "patient_gender_identity"))) + .isEmpty(); + assertThat( + validatePatientGenderIdentity( + new ValueOrError("trans woman", "patient_gender_identity"))) + .isEmpty(); + assertThat(validatePatientGenderIdentity(new ValueOrError("other", "patient_gender_identity"))) + .isEmpty(); + assertThat( + validatePatientGenderIdentity(new ValueOrError("refused", "patient_gender_identity"))) + .isEmpty(); + } + + @Test + void invalidPatientGenderIdentity() { + assertThat(validatePatientGenderIdentity(new ValueOrError("t", "patient_gender_identity"))) + .hasSize(1); + assertThat(validatePatientGenderIdentity(new ValueOrError("n", "patient_gender_identity"))) + .hasSize(1); + assertThat(validatePatientGenderIdentity(new ValueOrError("ma", "patient_gender_identity"))) + .hasSize(1); + assertThat(validatePatientGenderIdentity(new ValueOrError("fe", "patient_gender_identity"))) + .hasSize(1); + assertThat(validatePatientGenderIdentity(new ValueOrError(" ", "patient_gender_identity"))) + .hasSize(1); + } + @Test void validNegativeHIVNoRequiredAOEFields() { ValueOrError testResult = new ValueOrError("negative", "test_result"); diff --git a/backend/src/test/java/gov/cdc/usds/simplereport/validators/TestResultRowTest.java b/backend/src/test/java/gov/cdc/usds/simplereport/validators/TestResultRowTest.java index 50dd28244a..236b851891 100644 --- a/backend/src/test/java/gov/cdc/usds/simplereport/validators/TestResultRowTest.java +++ b/backend/src/test/java/gov/cdc/usds/simplereport/validators/TestResultRowTest.java @@ -96,7 +96,8 @@ class TestResultRowTest { "test_result_status", "specimen_type", "testing_lab_clia", - "genders_of_sexual_partners"); + "genders_of_sexual_partners", + "patient_gender_identity"); @BeforeEach public void init() { @@ -164,6 +165,7 @@ public void init() { validRowMap.put("comment", "Test Comment"); validRowMap.put("test_result_status", ""); validRowMap.put("genders_of_sexual_partners", "M, F, TM, TW"); + validRowMap.put("patient_gender_identity", "F"); } @Test @@ -358,7 +360,10 @@ void processRowSetsAllValues() { from(TestResultRow::getTestResultStatus).andThen(ValueOrError::getValue)) .returns( validRowMap.get("genders_of_sexual_partners"), - from(TestResultRow::getGendersOfSexualPartners).andThen(ValueOrError::getValue)); + from(TestResultRow::getGendersOfSexualPartners).andThen(ValueOrError::getValue)) + .returns( + validRowMap.get("patient_gender_identity"), + from(TestResultRow::getPatientGenderIdentity).andThen(ValueOrError::getValue)); } @Test @@ -412,6 +417,7 @@ void validateIndividualFields() { invalidIndividualFields.put("specimen_type", "100"); invalidIndividualFields.put("testing_lab_clia", "à"); invalidIndividualFields.put("genders_of_sexual_partners", "ma, f"); + invalidIndividualFields.put("patient_gender_identity", "fe"); var testResultRow = new TestResultRow( invalidIndividualFields, diff --git a/backend/src/test/resources/testResultUpload/test-results-upload-all-fields.csv b/backend/src/test/resources/testResultUpload/test-results-upload-all-fields.csv index 21c4ac4419..d3db9052bf 100644 --- a/backend/src/test/resources/testResultUpload/test-results-upload-all-fields.csv +++ b/backend/src/test/resources/testResultUpload/test-results-upload-all-fields.csv @@ -1,3 +1,3 @@ -patient_id,patient_last_name,patient_first_name,patient_middle_name,patient_street,patient_street2,patient_city,patient_state,patient_zip_code,patient_county,patient_phone_number,patient_dob,patient_gender,patient_race,patient_ethnicity,patient_preferred_language,patient_email,accession_number,equipment_model_name,test_performed_code,test_result,order_test_date,specimen_collection_date,testing_lab_specimen_received_date,test_result_date,date_result_released,specimen_type,ordering_provider_id,ordering_provider_last_name,ordering_provider_first_name,ordering_provider_middle_name,ordering_provider_street,ordering_provider_street2,ordering_provider_city,ordering_provider_state,ordering_provider_zip_code,ordering_provider_phone_number,testing_lab_clia,testing_lab_name,testing_lab_street,testing_lab_street2,testing_lab_city,testing_lab_state,testing_lab_zip_code,testing_lab_phone_number,pregnant,employed_in_healthcare,symptomatic_for_disease,illness_onset_date,resident_congregate_setting,residence_type,hospitalized,icu,ordering_facility_name,ordering_facility_street,ordering_facility_street2,ordering_facility_city,ordering_facility_state,ordering_facility_zip_code,ordering_facility_phone_number,comment,test_result_status,test_ordered_code,genders_of_sexual_partners,syphilis_history -1234,Doe,Jane,Jingleheimer,123 Main St,Suite 401,Birmingham,AL,35226,Jefferson,205-999-2800,1/20/1962,F,White,Not Hispanic or Latino,Eng,test@test.com,123,ID NOW,94534-5,Detected,12/20/2021 14:00,12/20/2021 14:00,12/20/2021 14:00,12/20/2021 14:00,12/20/2021 14:00,Nasal Swab,1013012657,Smith MD,John,Smitty,400 Main Street,Suite 401,Birmingham,AL,35228,205-888-2000,01D1058442,My Urgent Care,400 Main Street,Suite 401,Birmingham,AL,35228,205-888-2000,N,N,N,01/01/1901,N,22232009,N,N,My Urgent Care,400 Main Street,Suite 100,Birmingham,AL,35228,205-888-2000,Test Comment,C,94534-1,"M, F, trans man, TW, nonbinary","" -5678,Doe,John,Jingleheimer,333 Elm St,Suite 401,Hoover,AL,35244,Shelby,205-222-2000,11/1/2001,MALE,BLACK OR AFRICAN AMERICAN,NOT HISPANIC OR LATINO,Spa,myemail@email.com,05be96ef-949f-4f46-9c07-2675e8abf412,BD Veritor System for Rapid Detection of SARS-CoV-2*,94558-4,NOT DETECTED,12/21/2021 17:00,12/21/2021 17:00,12/21/2021 17:00,12/21/2021 17:00,12/21/2021 17:00,ANTERIOR NARES SWAB,1013012657,Smith MD,John,Smitty,400 Main Street,Suite 401,Birmingham,AL,35228,205-888-2000,01D1058442,My Urgent Care,400 Main Street,Suite 401,Birmingham,AL,35228,205-888-2000,NO,YES,Y,01/01/2020,Y,HOSTEL,NO,NO,My Urgent Care,400 Main Street,Suite 100,Birmingham,AL,35228,205-888-2000,testy mctesterson,F,94534-2,"Male, NB","" +patient_id,patient_last_name,patient_first_name,patient_middle_name,patient_street,patient_street2,patient_city,patient_state,patient_zip_code,patient_county,patient_phone_number,patient_dob,patient_gender,patient_race,patient_ethnicity,patient_preferred_language,patient_email,accession_number,equipment_model_name,test_performed_code,test_result,order_test_date,specimen_collection_date,testing_lab_specimen_received_date,test_result_date,date_result_released,specimen_type,ordering_provider_id,ordering_provider_last_name,ordering_provider_first_name,ordering_provider_middle_name,ordering_provider_street,ordering_provider_street2,ordering_provider_city,ordering_provider_state,ordering_provider_zip_code,ordering_provider_phone_number,testing_lab_clia,testing_lab_name,testing_lab_street,testing_lab_street2,testing_lab_city,testing_lab_state,testing_lab_zip_code,testing_lab_phone_number,pregnant,employed_in_healthcare,symptomatic_for_disease,illness_onset_date,resident_congregate_setting,residence_type,hospitalized,icu,ordering_facility_name,ordering_facility_street,ordering_facility_street2,ordering_facility_city,ordering_facility_state,ordering_facility_zip_code,ordering_facility_phone_number,comment,test_result_status,test_ordered_code,genders_of_sexual_partners,syphilis_history,patient_gender_identity +1234,Doe,Jane,Jingleheimer,123 Main St,Suite 401,Birmingham,AL,35226,Jefferson,205-999-2800,1/20/1962,F,White,Not Hispanic or Latino,Eng,test@test.com,123,ID NOW,94534-5,Detected,12/20/2021 14:00,12/20/2021 14:00,12/20/2021 14:00,12/20/2021 14:00,12/20/2021 14:00,Nasal Swab,1013012657,Smith MD,John,Smitty,400 Main Street,Suite 401,Birmingham,AL,35228,205-888-2000,01D1058442,My Urgent Care,400 Main Street,Suite 401,Birmingham,AL,35228,205-888-2000,N,N,N,01/01/1901,N,22232009,N,N,My Urgent Care,400 Main Street,Suite 100,Birmingham,AL,35228,205-888-2000,Test Comment,C,94534-1,"M, F, trans man, TW, nonbinary","",m +5678,Doe,John,Jingleheimer,333 Elm St,Suite 401,Hoover,AL,35244,Shelby,205-222-2000,11/1/2001,MALE,BLACK OR AFRICAN AMERICAN,NOT HISPANIC OR LATINO,Spa,myemail@email.com,05be96ef-949f-4f46-9c07-2675e8abf412,BD Veritor System for Rapid Detection of SARS-CoV-2*,94558-4,NOT DETECTED,12/21/2021 17:00,12/21/2021 17:00,12/21/2021 17:00,12/21/2021 17:00,12/21/2021 17:00,ANTERIOR NARES SWAB,1013012657,Smith MD,John,Smitty,400 Main Street,Suite 401,Birmingham,AL,35228,205-888-2000,01D1058442,My Urgent Care,400 Main Street,Suite 401,Birmingham,AL,35228,205-888-2000,NO,YES,Y,01/01/2020,Y,HOSTEL,NO,NO,My Urgent Care,400 Main Street,Suite 100,Birmingham,AL,35228,205-888-2000,testy mctesterson,F,94534-2,"Male, NB","",f