Skip to content
Open
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),

### Added
* Added support for mapping different EhrSupplyType (e.g. NHS prescription, OTC sale) into the Medication Statement Prescribing Agency extension
* Added fallback for Condition.asserter to use the EHRComposition / author / agent field when EHRComposition / participant2 is absent.
*
* Added fallback for Condition.asserter to use the EHRComposition / author / agent field when EHRComposition / participant2 is absent.

### Fixed
* Fixed handling of multiple `<given>` name fields in practitioner XML to JSON mapping - now correctly captures all given names as an array in FHIR HumanName.
* Improved error handling in SkeletonProcessingService to throw a meaningful IllegalArgumentException
when a payload node cannot be matched to a skeleton document ID, replacing an uninformative NullPointerException.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -130,17 +130,23 @@
}

private static void setTextFromAvailableNameFields(PN name, HumanName humanName) {
var requiresSeparator = StringUtils.isNotBlank(name.getPrefix()) && StringUtils.isNotBlank(name.getGiven());
var text = StringUtils.join(name.getPrefix(), (requiresSeparator ? " " : ""), name.getGiven());
var givenNames = getGivenNamesAsString(name);
var requiresSeparator = StringUtils.isNotBlank(name.getPrefix()) && StringUtils.isNotBlank(givenNames);
var text = StringUtils.join(name.getPrefix(), (requiresSeparator ? " " : ""), givenNames);
humanName.setText(text);
}

private void setHumanNameValuesFromName(PN name, HumanName humanName) {
humanName.setFamily(name.getFamily());

var given = getPractitionerGiven(name.getGiven());
if (given != null) {
humanName.getGiven().add(given);
var givenList = name.getGiven();
if (givenList != null && !givenList.isEmpty()) {
for (String givenName : givenList) {
var given = getPractitionerGiven(givenName);
if (given != null) {
humanName.getGiven().add(given);
}
}
}

var prefix = getPractitionerPrefix(name.getPrefix());
Expand All @@ -149,10 +155,19 @@
}
}

private static String getGivenNamesAsString(PN name) {
var givenList = name.getGiven();
if (givenList != null && !givenList.isEmpty()) {
return StringUtils.join(givenList, " ");
}
return null;

Check warning on line 163 in gp2gp-translator/src/main/java/uk/nhs/adaptors/pss/translator/mapper/AgentDirectoryMapper.java

View workflow job for this annotation

GitHub Actions / pitest

A change can be made to line 163 without causing a test to fail

replaced return value with "" for getGivenNamesAsString (covered by 1 tests EmptyObjectReturnValsMutator)
}
Comment thread
Copilot marked this conversation as resolved.

private static boolean hasNoName(PN name) {
var hasGiven = name != null && name.getGiven() != null && !name.getGiven().isEmpty();

Check warning on line 167 in gp2gp-translator/src/main/java/uk/nhs/adaptors/pss/translator/mapper/AgentDirectoryMapper.java

View workflow job for this annotation

GitHub Actions / pitest

A change can be made to line 167 without causing a test to fail

removed conditional - replaced equality check with true (covered by 18 tests RemoveConditionalMutator_EQUAL_IF)
return name == null
|| (StringUtils.isBlank(name.getFamily())
&& StringUtils.isBlank(name.getGiven())
&& !hasGiven
&& StringUtils.isBlank(name.getPrefix()));
}
Comment on lines 170 to 176

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,19 +71,16 @@ public static Map<String, Optional<Reference>> fetchRecorderAndAsserter(RCMRMT03

public static Reference getParticipant2Reference(RCMRMT030101UKEhrComposition ehrComposition, String typeCode) {

var participant2Reference = ehrComposition.getParticipant2().stream()
return ehrComposition.getParticipant2().stream()
.filter(participant2 -> participant2.getNullFlavor() == null)
.filter(participant2 -> typeCode.equals(participant2.getTypeCode().getFirst()))
.map(RCMRMT030101UKParticipant2::getAgentRef)
.map(RCMRMT030101UKAgentRef::getId)
.filter(II::hasRoot)
.map(II::getRoot)
.findFirst();

if (participant2Reference.isPresent()) {
return new Reference(PRACTITIONER_REFERENCE_PREFIX.formatted(participant2Reference.get()));
}
return null;
.findFirst()
.map(ref -> new Reference(PRACTITIONER_REFERENCE_PREFIX.formatted(ref)))
.orElse(null);
}

private static Optional<String> getParticipant2Reference(RCMRMT030101UKEhrComposition ehrComposition) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,150 @@ public void When_MapAgentDirectoryOnlyAgentPersonWithNameElementWithOnlyGiven_Ex
);
}

@Test
public void When_MapAgentWithMultipleGivenAndFamilyName_Expect_AllGivenNamesInArray() {
var agentDirectoryXml = """
<agentDirectory xmlns="urn:hl7-org:v3" classCode="AGNT">
<part typeCode="PART">
Comment on lines +255 to +259
<Agent classCode="AGNT">
<id root="CD8E40B3-6A3C-11F1-AE7C-00155D75C807"/>
<code code="704951000000106" codeSystem="2.16.840.1.113883.2.1.3.2.4.15" displayName="Other person"/>
<agentPerson classCode="PSN" determinerCode="INSTANCE">
<name use="L">
<given>Minire</given>
<given>E</given>
<family>Clarkson</family>
</name>
</agentPerson>
</Agent>
</part>
</agentDirectory>""";
var agentDirectory = unmarshallAgentDirectoryFromXmlString(agentDirectoryXml);

var mappedAgents = agentDirectoryMapper.mapAgentDirectory(agentDirectory);

assertThat(mappedAgents).hasSize(1);

var practitioner = (Practitioner) mappedAgents.getFirst();

assertAll(
() -> assertThat(practitioner.getId()).isEqualTo("CD8E40B3-6A3C-11F1-AE7C-00155D75C807"),
() -> assertThat(practitioner.getNameFirstRep().getFamily()).isEqualTo("Clarkson"),
() -> assertThat(practitioner.getNameFirstRep().getGiven()).hasSize(2),
() -> assertThat(practitioner.getNameFirstRep().getGiven().getFirst().getValue()).isEqualTo("Minire"),
() -> assertThat(practitioner.getNameFirstRep().getGiven().get(1).getValue()).isEqualTo("E"),
() -> assertThat(practitioner.getNameFirstRep().getText()).isNull(),
() -> assertThat(practitioner.getMeta().getProfile().getFirst().getValue()).isEqualTo(PRACT_META_PROFILE)
);
}

@Test
public void When_MapAgentWithThreeGivenNames_Expect_AllThreeGivenNamesInArray() {
var agentDirectoryXml = """
<agentDirectory xmlns="urn:hl7-org:v3" classCode="AGNT">
<part typeCode="PART">
<Agent classCode="AGNT">
<id root="95D00D99-0601-4A8E-AD1D-1B564307B0A6" />
<agentPerson classCode="PSN" determinerCode="INSTANCE">
<name>
<given>John</given>
<given>Paul</given>
<given>George</given>
<family>Smith</family>
</name>
</agentPerson>
</Agent>
</part>
</agentDirectory>""";
var agentDirectory = unmarshallAgentDirectoryFromXmlString(agentDirectoryXml);

var mappedAgents = agentDirectoryMapper.mapAgentDirectory(agentDirectory);

assertThat(mappedAgents).hasSize(1);

var practitioner = (Practitioner) mappedAgents.getFirst();

assertAll(
() -> assertThat(practitioner.getNameFirstRep().getFamily()).isEqualTo("Smith"),
() -> assertThat(practitioner.getNameFirstRep().getGiven()).hasSize(3),
() -> assertThat(practitioner.getNameFirstRep().getGiven().getFirst().getValue()).isEqualTo("John"),
() -> assertThat(practitioner.getNameFirstRep().getGiven().get(1).getValue()).isEqualTo("Paul"),
() -> assertThat(practitioner.getNameFirstRep().getGiven().get(2).getValue()).isEqualTo("George"),
() -> assertThat(practitioner.getNameFirstRep().getText()).isNull()
);
}

@Test
public void When_MapAgentWithEmptyGivenNameInList_Expect_OnlyNonEmptyGivenNamesAdded() {
var agentDirectoryXml = """
<agentDirectory xmlns="urn:hl7-org:v3" classCode="AGNT">
<part typeCode="PART">
<Agent classCode="AGNT">
<id root="95D00D99-0601-4A8E-AD1D-1B564307B0A6" />
<agentPerson classCode="PSN" determinerCode="INSTANCE">
<name>
<given>John</given>
<given></given>
<given>Paul</given>
<family>Smith</family>
</name>
</agentPerson>
</Agent>
</part>
</agentDirectory>""";
var agentDirectory = unmarshallAgentDirectoryFromXmlString(agentDirectoryXml);

var mappedAgents = agentDirectoryMapper.mapAgentDirectory(agentDirectory);

assertThat(mappedAgents).hasSize(1);

var practitioner = (Practitioner) mappedAgents.getFirst();

// Should only have 2 given names since one is empty
assertAll(
() -> assertThat(practitioner.getNameFirstRep().getFamily()).isEqualTo("Smith"),
() -> assertThat(practitioner.getNameFirstRep().getGiven()).hasSize(2),
() -> assertThat(practitioner.getNameFirstRep().getGiven().getFirst().getValue()).isEqualTo("John"),
() -> assertThat(practitioner.getNameFirstRep().getGiven().get(1).getValue()).isEqualTo("Paul")
);
}

@Test
public void When_MapAgentWithMultipleGivenNamesAndPrefix_Expect_AllNamesAndPrefix() {
var agentDirectoryXml = """
<agentDirectory xmlns="urn:hl7-org:v3" classCode="AGNT">
<part typeCode="PART">
<Agent classCode="AGNT">
<id root="95D00D99-0601-4A8E-AD1D-1B564307B0A6" />
<agentPerson classCode="PSN" determinerCode="INSTANCE">
<name>
<prefix>Dr</prefix>
<given>John</given>
<given>Robert</given>
<family>Smith</family>
</name>
</agentPerson>
</Agent>
</part>
</agentDirectory>""";
var agentDirectory = unmarshallAgentDirectoryFromXmlString(agentDirectoryXml);

var mappedAgents = agentDirectoryMapper.mapAgentDirectory(agentDirectory);

assertThat(mappedAgents).hasSize(1);

var practitioner = (Practitioner) mappedAgents.getFirst();

assertAll(
() -> assertThat(practitioner.getNameFirstRep().getPrefix()).hasSize(1),
() -> assertThat(practitioner.getNameFirstRep().getPrefix().getFirst().getValue()).isEqualTo("Dr"),
() -> assertThat(practitioner.getNameFirstRep().getGiven()).hasSize(2),
() -> assertThat(practitioner.getNameFirstRep().getGiven().getFirst().getValue()).isEqualTo("John"),
() -> assertThat(practitioner.getNameFirstRep().getGiven().get(1).getValue()).isEqualTo("Robert"),
() -> assertThat(practitioner.getNameFirstRep().getFamily()).isEqualTo("Smith")
);
}

@Test
public void When_MapAgentDirectoryOnlyAgentPersonWithNameElementWithOnlyPrefix_Expect_TextSetToPrefix() {
var agentDirectoryXml = """
Expand Down
38 changes: 33 additions & 5 deletions schema/src/main/java/org/hl7/v3/EN.java
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ public class EN {
protected List<CsEntityNameUse> use;
protected String delimiter;
protected String family;
protected String given;
protected List<String> given;
protected String prefix;
protected String suffix;
protected IVLTS validTime;
Expand Down Expand Up @@ -120,12 +120,40 @@ public void setFamily(String family) {
this.family = family;
}

public String getGiven() {
return given;
/**
* Gets the value of the given property.
*
* <p>
* This accessor method returns a reference to the live list,
* not a snapshot. Therefore any modification you make to the
* returned list will be present inside the JAXB object.
* This is why there is not a <CODE>set</CODE> method for the given property.
*
* <p>
* For example, to add a new item, do as follows:
* <pre>
* getGiven().add(newItem);
* </pre>
*
* <p>
* Objects of the following type(s) are allowed in the list
* {@link String }
*/
public List<String> getGiven() {
if (given == null) {
given = new ArrayList<>();
}
return this.given;
}

public void setGiven(String given) {
this.given = given;
/**
* Convenience method for backward compatibility - returns the first given name as a string.
*/
public String getFirstGiven() {
if (given != null && !given.isEmpty()) {
return given.get(0);
}
return null;
}

public String getPrefix() {
Expand Down
3 changes: 2 additions & 1 deletion schema/src/test/java/JaxbTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,8 @@ void When_RCMRIN030000UK06MessageIsUnmarshalled_Expect_FieldsToBeParsable() thro

// then
assertThat(person.getName().getFamily()).isEqualTo("Whitcombe");
assertThat(person.getName().getGiven()).isEqualTo("Peter");
assertThat(person.getName().getGiven()).hasSize(1);
assertThat(person.getName().getGiven().getFirst()).isEqualTo("Peter");
assertThat(person.getName().getPrefix()).isEqualTo("Dr");
assertThat(person.getName().getValidTime().getCenter().getValue()).isEqualTo("20100114");
assertThat(place.getName()).isEqualTo("EMIS Test Practice Location");
Expand Down
Loading