-
Notifications
You must be signed in to change notification settings - Fork 55
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
person to FHIR patient #4842
Merged
Merged
person to FHIR patient #4842
Changes from all commits
Commits
Show all changes
56 commits
Select commit
Hold shift + click to select a range
a4e5c3f
add fhir structure dependency
BobanL 85a378e
WIP patient
BobanL 0814e4a
write lock for fhir
BobanL 411520e
add to fhir method
BobanL bcc32a8
add to fhir method with mobile support
BobanL 521bd73
add landline support
BobanL d1f528a
add invalid test and reformat
BobanL 344a4ba
rename assert function and make it static
BobanL 6148456
add toFhir method
BobanL c164331
add a null test to street toFhir
BobanL e03eaad
use assertThat instead of assertEquals
BobanL f237181
add basic test for person toFhir
BobanL ced62f9
add null check to toFhir method
BobanL b506371
add additional gender checks
BobanL 6a1833e
remove comments
BobanL a70a6ba
delete unused method
BobanL eefd82c
refactor to chain methods
BobanL e8f1c86
rename method toFhir method to match other places
BobanL b3458dc
refactor test to check identifier alone.
BobanL 943b447
refactor toFhir to own methods.
BobanL 8bb602b
add race extension
BobanL 1cf43d2
add additional race extension tests
BobanL e0b5134
refactor tests
BobanL 4b7103f
fix word
BobanL 35e3856
fix
BobanL 0fbce0c
refactor to replace filter with get extension by url
BobanL 16c1acb
add ethnicity extension
BobanL cf23833
add tribal affiliation extension
BobanL 00fa282
do a full comparison of a patient object
BobanL d699105
add json ignore on all person methods
BobanL 29574d5
refactor static util methods and constants into own class
BobanL 7af6e3c
make unknowns use the null code system
BobanL 9a29ff5
Merge branch 'main' into boban/eventToFHIR
BobanL 5fec43d
set nullCodeSystem as transiet
BobanL fd5e3f0
fix human name for null values
BobanL 781ab6d
update build wiremock to make individual runs of TestResultUploadServ…
BobanL fecd87b
fix birthdate check on person test
BobanL f5c88c9
fix code smells
BobanL 6650403
add additional test coverage
BobanL 78c0d7d
move patient into resource folder
BobanL a17df02
rename fhir utils to mapping constants
BobanL 5be4528
refactor name & identifier to fhir
BobanL 17cf882
refactor PersonName to HumanName
BobanL 559df68
refactor number to fhir
BobanL b3eea27
refactor contact point
BobanL 8732055
refactor administrative gender
BobanL e613d68
refactor date
BobanL 64c4fb2
add convertToAddress
BobanL d80be10
move race extension to fhir converter
BobanL 2de7e8c
refactor to remove all remaining toFhir methods
BobanL db51a9c
move ethnicity to fhir converter
BobanL b847bb4
refactor gender tests
BobanL 3df53d3
convert tribal affiliation to convert
BobanL 517903a
move constants to fhir constants
BobanL 756a963
add a list for phone number and email converters
BobanL 8619417
fix code smells
BobanL File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
12 changes: 12 additions & 0 deletions
12
backend/src/main/java/gov/cdc/usds/simplereport/api/MappingConstants.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
package gov.cdc.usds.simplereport.api; | ||
|
||
public class MappingConstants { | ||
private MappingConstants() { | ||
throw new IllegalStateException("Utility class"); | ||
} | ||
|
||
public static final String UNK_CODE = "UNK"; | ||
public static final String UNKNOWN_STRING = "unknown"; | ||
public static final String U_CODE = "U"; | ||
public static final String ASKED_BUT_UNKNOWN_CODE = "ASKU"; | ||
} |
21 changes: 21 additions & 0 deletions
21
backend/src/main/java/gov/cdc/usds/simplereport/api/converter/FhirConstants.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
package gov.cdc.usds.simplereport.api.converter; | ||
|
||
public class FhirConstants { | ||
private FhirConstants() { | ||
throw new IllegalStateException("Utility class"); | ||
} | ||
|
||
public static final String NULL_CODE_SYSTEM = | ||
"http://terminology.hl7.org/CodeSystem/v3-NullFlavor"; | ||
public static final String RACE_EXTENSION_URL = | ||
"http://ibm.com/fhir/cdm/StructureDefinition/local-race-cd"; | ||
public static final String RACE_CODING_SYSTEM = "http://terminology.hl7.org/CodeSystem/v3-Race"; | ||
public static final String ETHNICITY_EXTENSION_URL = | ||
"http://hl7.org/fhir/us/core/StructureDefinition/us-core-ethnicity"; | ||
public static final String ETHNICITY_CODE_SYSTEM = "urn:oid:2.16.840.1.113883.6.238"; | ||
public static final String TRIBAL_AFFILIATION_EXTENSION_URL = | ||
"http://hl7.org/fhir/us/core/StructureDefinition/us-core-tribal-affiliation"; | ||
public static final String TRIBAL_AFFILIATION_STRING = "tribalAffiliation"; | ||
public static final String TRIBAL_AFFILIATION_CODE_SYSTEM = | ||
"http://terminology.hl7.org/CodeSystem/v3-TribalEntityUS"; | ||
} |
276 changes: 276 additions & 0 deletions
276
backend/src/main/java/gov/cdc/usds/simplereport/api/converter/FhirConverter.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,276 @@ | ||
package gov.cdc.usds.simplereport.api.converter; | ||
|
||
import static gov.cdc.usds.simplereport.api.converter.FhirConstants.ETHNICITY_CODE_SYSTEM; | ||
import static gov.cdc.usds.simplereport.api.converter.FhirConstants.ETHNICITY_EXTENSION_URL; | ||
import static gov.cdc.usds.simplereport.api.converter.FhirConstants.NULL_CODE_SYSTEM; | ||
import static gov.cdc.usds.simplereport.api.converter.FhirConstants.RACE_CODING_SYSTEM; | ||
import static gov.cdc.usds.simplereport.api.converter.FhirConstants.RACE_EXTENSION_URL; | ||
import static gov.cdc.usds.simplereport.api.converter.FhirConstants.TRIBAL_AFFILIATION_CODE_SYSTEM; | ||
import static gov.cdc.usds.simplereport.api.converter.FhirConstants.TRIBAL_AFFILIATION_EXTENSION_URL; | ||
import static gov.cdc.usds.simplereport.api.converter.FhirConstants.TRIBAL_AFFILIATION_STRING; | ||
|
||
import com.google.i18n.phonenumbers.NumberParseException; | ||
import com.google.i18n.phonenumbers.PhoneNumberUtil; | ||
import com.google.i18n.phonenumbers.PhoneNumberUtil.PhoneNumberFormat; | ||
import gov.cdc.usds.simplereport.api.MappingConstants; | ||
import gov.cdc.usds.simplereport.db.model.PersonUtils; | ||
import gov.cdc.usds.simplereport.db.model.PhoneNumber; | ||
import gov.cdc.usds.simplereport.db.model.auxiliary.PersonName; | ||
import gov.cdc.usds.simplereport.db.model.auxiliary.PhoneType; | ||
import gov.cdc.usds.simplereport.db.model.auxiliary.StreetAddress; | ||
import java.time.LocalDate; | ||
import java.time.ZoneId; | ||
import java.util.Collections; | ||
import java.util.Date; | ||
import java.util.List; | ||
import java.util.UUID; | ||
import java.util.stream.Collectors; | ||
import lombok.extern.slf4j.Slf4j; | ||
import org.apache.commons.lang3.StringUtils; | ||
import org.hl7.fhir.r4.model.Address; | ||
import org.hl7.fhir.r4.model.CodeableConcept; | ||
import org.hl7.fhir.r4.model.Coding; | ||
import org.hl7.fhir.r4.model.ContactPoint; | ||
import org.hl7.fhir.r4.model.ContactPoint.ContactPointSystem; | ||
import org.hl7.fhir.r4.model.ContactPoint.ContactPointUse; | ||
import org.hl7.fhir.r4.model.Enumerations.AdministrativeGender; | ||
import org.hl7.fhir.r4.model.Extension; | ||
import org.hl7.fhir.r4.model.HumanName; | ||
import org.hl7.fhir.r4.model.Identifier; | ||
import org.hl7.fhir.r4.model.Identifier.IdentifierUse; | ||
import org.hl7.fhir.r4.model.StringType; | ||
|
||
@Slf4j | ||
public class FhirConverter { | ||
private FhirConverter() { | ||
throw new IllegalStateException("Utility class"); | ||
} | ||
|
||
public static Identifier convertToIdentifier(UUID id) { | ||
if (id != null) { | ||
return convertToIdentifier(id.toString()); | ||
} | ||
return null; | ||
} | ||
|
||
public static Identifier convertToIdentifier(String id) { | ||
if (id != null) { | ||
return new Identifier().setValue(id).setUse(IdentifierUse.USUAL); | ||
} | ||
return null; | ||
} | ||
|
||
public static HumanName convertToHumanName(PersonName personName) { | ||
if (personName != null) { | ||
return convertToHumanName( | ||
personName.getFirstName(), | ||
personName.getMiddleName(), | ||
personName.getLastName(), | ||
personName.getSuffix()); | ||
} | ||
return null; | ||
} | ||
|
||
public static HumanName convertToHumanName( | ||
String first, String middle, String last, String suffix) { | ||
var humanName = new HumanName(); | ||
if (StringUtils.isNotBlank(first)) { | ||
humanName.addGiven(first); | ||
} | ||
if (StringUtils.isNotBlank(middle)) { | ||
humanName.addGiven(middle); | ||
} | ||
if (StringUtils.isNotBlank(last)) { | ||
humanName.setFamily(last); | ||
} | ||
if (StringUtils.isNotBlank(suffix)) { | ||
humanName.addSuffix(suffix); | ||
} | ||
return humanName; | ||
} | ||
|
||
public static List<ContactPoint> phoneNumberToContactPoint(List<PhoneNumber> phoneNumber) { | ||
if (phoneNumber != null && !phoneNumber.isEmpty()) { | ||
return phoneNumber.stream() | ||
.map(FhirConverter::phoneNumberToContactPoint) | ||
.collect(Collectors.toList()); | ||
} | ||
return Collections.emptyList(); | ||
} | ||
|
||
public static ContactPoint phoneNumberToContactPoint(PhoneNumber phoneNumber) { | ||
if (phoneNumber != null) { | ||
var contactPointUse = ContactPointUse.HOME; | ||
if (PhoneType.MOBILE.equals(phoneNumber.getType())) { | ||
contactPointUse = ContactPointUse.MOBILE; | ||
} | ||
|
||
return phoneNumberToContactPoint(contactPointUse, phoneNumber.getNumber()); | ||
} | ||
return null; | ||
} | ||
|
||
public static ContactPoint phoneNumberToContactPoint( | ||
ContactPointUse contactPointUse, String number) { | ||
// converting string to phone format as recommended by the fhir format. | ||
// https://www.hl7.org/fhir/datatypes.html#ContactPoint | ||
try { | ||
var phoneUtil = PhoneNumberUtil.getInstance(); | ||
var parsedNumber = phoneUtil.parse(number, "US"); | ||
var formattedWithDash = phoneUtil.format(parsedNumber, PhoneNumberFormat.NATIONAL); | ||
|
||
number = formattedWithDash.replace("-", " "); | ||
} catch (NumberParseException exception) { | ||
log.debug("Error parsing number: " + exception); | ||
} | ||
|
||
return convertToContactPoint(contactPointUse, ContactPointSystem.PHONE, number); | ||
} | ||
|
||
public static List<ContactPoint> emailToContactPoint(List<String> emails) { | ||
if (emails != null) { | ||
return emails.stream().map(FhirConverter::emailToContactPoint).collect(Collectors.toList()); | ||
} | ||
return Collections.emptyList(); | ||
} | ||
|
||
public static ContactPoint emailToContactPoint(String email) { | ||
if (email != null) { | ||
return convertToContactPoint(null, ContactPointSystem.EMAIL, email); | ||
} | ||
return null; | ||
} | ||
|
||
public static ContactPoint convertToContactPoint( | ||
ContactPointUse use, ContactPointSystem system, String value) { | ||
if (value != null) { | ||
return new ContactPoint().setUse(use).setSystem(system).setValue(value); | ||
} | ||
return null; | ||
} | ||
|
||
public static AdministrativeGender convertToAdministrativeGender(String gender) { | ||
if ("male".equalsIgnoreCase(gender) || "m".equalsIgnoreCase(gender)) { | ||
return AdministrativeGender.MALE; | ||
} else if ("female".equalsIgnoreCase(gender) || "f".equalsIgnoreCase(gender)) { | ||
return AdministrativeGender.FEMALE; | ||
} else { | ||
return AdministrativeGender.UNKNOWN; | ||
} | ||
} | ||
|
||
public static Date convertToDate(LocalDate date) { | ||
if (date != null) { | ||
return Date.from(date.atStartOfDay(ZoneId.systemDefault()).toInstant()); | ||
} | ||
return null; | ||
} | ||
|
||
public static Address convertToAddress(StreetAddress address) { | ||
if (address != null) { | ||
return convertToAddress( | ||
address.getStreet(), | ||
address.getCity(), | ||
address.getCounty(), | ||
address.getState(), | ||
address.getPostalCode()); | ||
} | ||
return null; | ||
} | ||
|
||
public static Address convertToAddress( | ||
List<String> street, String city, String county, String state, String postalCode) { | ||
var address = | ||
new Address().setCity(city).setDistrict(county).setState(state).setPostalCode(postalCode); | ||
if (street != null) { | ||
street.forEach(address::addLine); | ||
} | ||
return address; | ||
} | ||
|
||
public static Extension convertToRaceExtension(String race) { | ||
if (StringUtils.isNotBlank(race)) { | ||
var ext = new Extension(); | ||
ext.setUrl(RACE_EXTENSION_URL); | ||
var codeable = new CodeableConcept(); | ||
var coding = codeable.addCoding(); | ||
if (PersonUtils.raceMap.containsKey(race)) { | ||
if (MappingConstants.UNKNOWN_STRING.equalsIgnoreCase(race) | ||
|| "refused".equalsIgnoreCase(race)) { | ||
coding.setSystem(NULL_CODE_SYSTEM); | ||
} else { | ||
coding.setSystem(RACE_CODING_SYSTEM); | ||
} | ||
coding.setCode(PersonUtils.raceMap.get(race)); | ||
codeable.setText(race); | ||
} else { | ||
coding.setSystem(NULL_CODE_SYSTEM); | ||
coding.setCode(MappingConstants.UNK_CODE); | ||
codeable.setText(MappingConstants.UNKNOWN_STRING); | ||
} | ||
ext.setValue(codeable); | ||
return ext; | ||
} | ||
return null; | ||
} | ||
|
||
public static Extension convertToEthnicityExtension(String ethnicity) { | ||
if (StringUtils.isNotBlank(ethnicity)) { | ||
var ext = new Extension(); | ||
ext.setUrl(ETHNICITY_EXTENSION_URL); | ||
var ombExtension = ext.addExtension(); | ||
ombExtension.setUrl("ombCategory"); | ||
var ombCoding = new Coding(); | ||
if (PersonUtils.ETHNICITY_MAP.containsKey(ethnicity)) { | ||
if ("refused".equalsIgnoreCase(ethnicity)) { | ||
ombCoding.setSystem(NULL_CODE_SYSTEM); | ||
} else { | ||
ombCoding.setSystem(ETHNICITY_CODE_SYSTEM); | ||
} | ||
ombCoding.setCode(PersonUtils.ETHNICITY_MAP.get(ethnicity).get(0)); | ||
ombCoding.setDisplay(PersonUtils.ETHNICITY_MAP.get(ethnicity).get(1)); | ||
|
||
var text = ext.addExtension(); | ||
text.setUrl("text"); | ||
text.setValue(new StringType(PersonUtils.ETHNICITY_MAP.get(ethnicity).get(1))); | ||
} else { | ||
ombCoding.setSystem(NULL_CODE_SYSTEM); | ||
ombCoding.setCode(MappingConstants.UNK_CODE); | ||
ombCoding.setDisplay(MappingConstants.UNKNOWN_STRING); | ||
|
||
var text = ext.addExtension(); | ||
text.setUrl("text"); | ||
text.setValue(new StringType(MappingConstants.UNKNOWN_STRING)); | ||
} | ||
ombExtension.setValue(ombCoding); | ||
return ext; | ||
} | ||
return null; | ||
} | ||
|
||
public static Extension convertToTribalAffiliationExtension(List<String> tribalAffiliations) { | ||
if (tribalAffiliations != null && !tribalAffiliations.isEmpty()) { | ||
return convertToTribalAffiliationExtension(tribalAffiliations.get(0)); | ||
} | ||
return null; | ||
} | ||
|
||
public static Extension convertToTribalAffiliationExtension(String tribalAffiliation) { | ||
if (StringUtils.isNotBlank(tribalAffiliation) | ||
&& PersonUtils.tribalMap().containsKey(tribalAffiliation)) { | ||
var ext = new Extension(); | ||
ext.setUrl(TRIBAL_AFFILIATION_EXTENSION_URL); | ||
var tribeExtension = ext.addExtension(); | ||
tribeExtension.setUrl(TRIBAL_AFFILIATION_STRING); | ||
var tribeCodeableConcept = new CodeableConcept(); | ||
var tribeCoding = tribeCodeableConcept.addCoding(); | ||
tribeCoding.setSystem(TRIBAL_AFFILIATION_CODE_SYSTEM); | ||
tribeCoding.setCode(tribalAffiliation); | ||
tribeCoding.setDisplay(PersonUtils.tribalMap().get(tribalAffiliation)); | ||
tribeCodeableConcept.setText(PersonUtils.tribalMap().get(tribalAffiliation)); | ||
tribeExtension.setValue(tribeCodeableConcept); | ||
return ext; | ||
} | ||
return null; | ||
} | ||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I recommend adding
_URL
to the name to keep it consistent with the othersThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So the reason why these don't have
_URL
at the end is because the are used as the system and the_URL
are added to the URL, but if that's confusing I can totally change it to describe what the contents of that variable is like recommended.