Skip to content

Commit 47f0576

Browse files
Refactor CodeableConceptCdMapper
* Refactor `mapCodeableConceptForMedication` and `mapCodeableConceptToCd` to reduce code duplication. * Refactor `mapCodeableConceptToCdForAllergy` for readability
1 parent d60623c commit 47f0576

File tree

1 file changed

+104
-110
lines changed

1 file changed

+104
-110
lines changed

service/src/main/java/uk/nhs/adaptors/gp2gp/ehr/mapper/CodeableConceptCdMapper.java

Lines changed: 104 additions & 110 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import java.util.Collections;
44
import java.util.List;
55
import java.util.Optional;
6+
import java.util.function.BiFunction;
67

78
import org.apache.commons.lang3.StringUtils;
89
import org.hl7.fhir.dstu3.model.AllergyIntolerance;
@@ -51,10 +52,23 @@ public class CodeableConceptCdMapper {
5152
private static final String OTHER_CATEGORY_DESCRIPTION = "Other category";
5253

5354
public String mapCodeableConceptToCd(CodeableConcept codeableConcept) {
55+
return mapCodeableConcept(codeableConcept, this::getMainCode);
56+
}
57+
58+
public String mapCodeableConceptForMedication(CodeableConcept codeableConcept) {
59+
return mapCodeableConcept(
60+
codeableConcept,
61+
(descriptionExtensions, snomedCodeCoding) -> Optional.ofNullable(snomedCodeCoding.getCode()));
62+
}
63+
64+
private String mapCodeableConcept(
65+
CodeableConcept codeableConcept,
66+
BiFunction<Optional<List<Extension>>, Coding, Optional<String>> getMainCodeFunction
67+
) {
5468
Optional<Coding> snomedCodeCoding = getSnomedCodeCoding(codeableConcept);
5569

5670
if (snomedCodeCoding.isEmpty()) {
57-
return buildNullFlavourCodeableConceptCd(codeableConcept, snomedCodeCoding);
71+
return mapToNullFlavorCodeableConcept(codeableConcept);
5872
}
5973

6074
var builder = CodeableConceptCdTemplateParameters.builder();
@@ -64,87 +78,60 @@ public String mapCodeableConceptToCd(CodeableConcept codeableConcept) {
6478

6579
builder.mainCodeSystem(SNOMED_SYSTEM_CODE);
6680

67-
var mainCode = getMainCode(descriptionExtensions, snomedCodeCoding.get());
68-
mainCode.ifPresent(builder::mainCode);
69-
7081
var mainDisplayName = getMainDisplayName(descriptionExtensions, snomedCodeCoding.get());
7182
mainDisplayName.ifPresent(builder::mainDisplayName);
7283

7384
builder.mainOriginalText(codeableConcept.getText());
7485
builder.translations(getNonSnomedCodeCodings(codeableConcept));
7586

87+
var mainCode = getMainCodeFunction.apply(descriptionExtensions, snomedCodeCoding.get());
88+
mainCode.ifPresent(builder::mainCode);
89+
7690
return TemplateUtils.fillTemplate(CODEABLE_CONCEPT_CD_TEMPLATE, builder.build());
7791
}
7892

79-
// Medications are currently using D&T Codes rather than snomed codes but are being passed through as SNOMED codes which is
80-
// creating a degradation on the receiving side. Until the types are configured correctly and agreed to a specification
81-
// we have agreed to use the Concept ID rather than Description Id for medications which will avoided the degradation.
82-
public String mapCodeableConceptForMedication(CodeableConcept codeableConcept) {
93+
public String mapCodeableConceptToCdForAllergy(
94+
CodeableConcept codeableConcept,
95+
AllergyIntolerance.AllergyIntoleranceClinicalStatus allergyIntoleranceClinicalStatus
96+
) {
8397
var builder = CodeableConceptCdTemplateParameters.builder();
84-
var snomedCodeCoding = getSnomedCodeCoding(codeableConcept);
98+
Optional<Coding> snomedCodeCoding = getSnomedCodeCoding(codeableConcept);
8599

86100
if (snomedCodeCoding.isEmpty()) {
87-
return buildNullFlavourCodeableConceptCd(codeableConcept, snomedCodeCoding);
101+
builder.nullFlavor(true);
102+
return TemplateUtils.fillTemplate(CODEABLE_CONCEPT_CD_TEMPLATE, builder.build());
88103
}
89104

90-
var extension = retrieveDescriptionExtension(snomedCodeCoding.get())
91-
.map(Extension::getExtension)
92-
.orElse(Collections.emptyList());
93-
94-
builder.mainCodeSystem(SNOMED_SYSTEM_CODE);
95-
96-
Optional<String> code = Optional.ofNullable(snomedCodeCoding.get().getCode());
97-
code.ifPresent(builder::mainCode);
105+
if (ACTIVE_CLINICAL_STATUS.equals(allergyIntoleranceClinicalStatus.toCode())) {
106+
builder.mainCodeSystem(SNOMED_SYSTEM_CODE);
107+
} else {
108+
builder.nullFlavor(true);
109+
}
98110

99-
Optional<String> displayName = extension.stream()
100-
.filter(displayExtension -> DESCRIPTION_DISPLAY.equals(displayExtension.getUrl()))
101-
.map(description -> description.getValue().toString())
102-
.findFirst()
103-
.or(() -> Optional.ofNullable(snomedCodeCoding.get().getDisplay()));
104-
displayName.ifPresent(builder::mainDisplayName);
111+
getAllergyMainCode(snomedCodeCoding.get()).ifPresent(builder::mainCode);
112+
getCodingDisplayName(snomedCodeCoding.get()).ifPresent(builder::mainDisplayName);
105113

106-
builder.mainOriginalText(codeableConcept.getText());
114+
if (codeableConcept.hasText()) {
115+
builder.mainOriginalText(codeableConcept.getText());
116+
} else {
117+
var originalText = findOriginalTextForAllergy(codeableConcept, snomedCodeCoding, allergyIntoleranceClinicalStatus);
118+
originalText.ifPresent(builder::mainOriginalText);
119+
}
107120

108-
builder.translations(getNonSnomedCodeCodings(codeableConcept));
109121
return TemplateUtils.fillTemplate(CODEABLE_CONCEPT_CD_TEMPLATE, builder.build());
110122
}
111123

112-
public String mapCodeableConceptToCdForAllergy(CodeableConcept codeableConcept, AllergyIntolerance.AllergyIntoleranceClinicalStatus
113-
allergyIntoleranceClinicalStatus) {
114-
var builder = CodeableConceptCdTemplateParameters.builder();
115-
var mainCode = getSnomedCodeCoding(codeableConcept);
116-
117-
builder.nullFlavor(mainCode.isEmpty());
118-
119-
if (mainCode.isPresent()) {
120-
var extension = retrieveDescriptionExtension(mainCode.get())
121-
.map(Extension::getExtension)
122-
.orElse(Collections.emptyList());
123-
124-
if (ACTIVE_CLINICAL_STATUS.equals(allergyIntoleranceClinicalStatus.toCode())) {
125-
builder.mainCodeSystem(SNOMED_SYSTEM_CODE);
126-
} else {
127-
builder.nullFlavor(true);
128-
}
124+
private static Optional<String> getCodingDisplayName(Coding snomedCodeCoding) {
125+
return Optional.ofNullable(snomedCodeCoding.getDisplay());
126+
}
129127

130-
Optional<String> code = extension.stream()
128+
private static Optional<String> getAllergyMainCode(Coding snomedCodeCoding) {
129+
return retrieveDescriptionExtension(snomedCodeCoding)
130+
.flatMap(extension -> extension.getExtension().stream()
131131
.filter(descriptionExt -> DESCRIPTION_ID.equals(descriptionExt.getUrl()))
132-
.map(description -> description.getValue().toString())
133132
.findFirst()
134-
.or(() -> Optional.ofNullable(mainCode.get().getCode()));
135-
code.ifPresent(builder::mainCode);
136-
137-
Optional<String> displayName = Optional.ofNullable(mainCode.get().getDisplay());
138-
displayName.ifPresent(builder::mainDisplayName);
139-
140-
if (codeableConcept.hasText()) {
141-
builder.mainOriginalText(codeableConcept.getText());
142-
} else {
143-
var originalText = findOriginalTextForAllergy(codeableConcept, mainCode, allergyIntoleranceClinicalStatus);
144-
originalText.ifPresent(builder::mainOriginalText);
145-
}
146-
}
147-
return TemplateUtils.fillTemplate(CODEABLE_CONCEPT_CD_TEMPLATE, builder.build());
133+
.map(description -> description.getValue().toString()))
134+
.or(() -> Optional.ofNullable(snomedCodeCoding.getCode()));
148135
}
149136

150137
public String mapCodeableConceptToCdForTransformedActualProblemHeader(CodeableConcept codeableConcept) {
@@ -343,7 +330,7 @@ private Optional<String> findOriginalText(CodeableConcept codeableConcept, Optio
343330
return Optional.ofNullable(codeableConcept.getText());
344331
} else {
345332
if (coding.get().hasDisplay()) {
346-
return Optional.ofNullable(coding.get().getDisplay());
333+
return getCodingDisplayName(coding.get());
347334
} else {
348335
var extension = retrieveDescriptionExtension(coding.get());
349336
return extension.stream()
@@ -362,52 +349,69 @@ private Optional<String> findOriginalTextForAllergy(
362349
Optional<Coding> coding,
363350
AllergyIntolerance.AllergyIntoleranceClinicalStatus allergyIntoleranceClinicalStatus
364351
) {
352+
if (coding.isEmpty()) {
353+
return Optional.empty();
354+
}
355+
365356
if (!allergyIntoleranceClinicalStatus.toCode().isEmpty()) {
366357
if (RESOLVED_CLINICAL_STATUS.equals(allergyIntoleranceClinicalStatus.toCode())) {
367-
if (coding.isPresent()) {
368-
if (codeableConcept.hasText()) {
369-
return Optional.ofNullable(codeableConcept.getText());
370-
} else {
371-
var extension = retrieveDescriptionExtension(coding.get());
372-
if (extension.isPresent()) {
373-
Optional<String> originalText = extension
374-
.get()
375-
.getExtension().stream()
376-
.filter(displayExtension -> DESCRIPTION_DISPLAY.equals(displayExtension.getUrl()))
377-
.map(extension1 -> extension1.getValue().toString())
378-
.findFirst();
379-
380-
if (originalText.isPresent()) {
381-
return originalText;
382-
} else if (coding.get().hasDisplay()) {
383-
return Optional.ofNullable(coding.get().getDisplay());
384-
}
385-
} else if (coding.get().hasDisplay()) {
386-
return Optional.ofNullable(coding.get().getDisplay());
387-
}
388-
}
389-
}
390-
} else if (ACTIVE_CLINICAL_STATUS.equals(allergyIntoleranceClinicalStatus.toCode())) {
391-
Optional<Extension> extension = retrieveDescriptionExtension(coding.get());
392-
if (extension.isPresent()) {
393-
Optional<String> originalText = extension
394-
.get()
395-
.getExtension().stream()
396-
.filter(displayExtension -> DESCRIPTION_DISPLAY.equals(displayExtension.getUrl()))
397-
.map(extension1 -> extension1.getValue().toString())
398-
.findFirst();
399-
if (originalText.isPresent() && StringUtils.isNotBlank(originalText.get())) {
400-
return originalText;
401-
}
402-
}
403-
404-
return Optional.empty();
358+
return getOriginalTextForResolvedAllergy(codeableConcept, coding.get());
359+
}
360+
if (ACTIVE_CLINICAL_STATUS.equals(allergyIntoleranceClinicalStatus.toCode())) {
361+
return getOriginalTextForActiveAllergy(coding.get());
405362
}
406363
}
407364

408365
return CodeableConceptMappingUtils.extractTextOrCoding(codeableConcept);
409366
}
410367

368+
private Optional<String> getOriginalTextForActiveAllergy(Coding coding) {
369+
Optional<Extension> extension = retrieveDescriptionExtension(coding);
370+
371+
if (extension.isPresent()) {
372+
Optional<String> originalText = extension
373+
.get()
374+
.getExtension().stream()
375+
.filter(displayExtension -> DESCRIPTION_DISPLAY.equals(displayExtension.getUrl()))
376+
.map(extension1 -> extension1.getValue().toString())
377+
.findFirst();
378+
if (originalText.isPresent()) {
379+
return originalText;
380+
}
381+
}
382+
383+
return Optional.empty();
384+
}
385+
386+
private Optional<String> getOriginalTextForResolvedAllergy(CodeableConcept codeableConcept, Coding coding) {
387+
388+
if (codeableConcept.hasText()) {
389+
return Optional.ofNullable(codeableConcept.getText());
390+
}
391+
392+
var extension = retrieveDescriptionExtension(coding);
393+
if (extension.isEmpty()) {
394+
return coding.hasDisplay()
395+
? Optional.ofNullable(coding.getDisplay())
396+
: Optional.empty();
397+
}
398+
399+
Optional<String> originalText = extension
400+
.get()
401+
.getExtension().stream()
402+
.filter(displayExtension -> DESCRIPTION_DISPLAY.equals(displayExtension.getUrl()))
403+
.map(extension1 -> extension1.getValue().toString())
404+
.findFirst();
405+
406+
if (originalText.isPresent()) {
407+
return originalText;
408+
} else if (coding.hasDisplay()) {
409+
return Optional.ofNullable(coding.getDisplay());
410+
}
411+
412+
return Optional.empty();
413+
}
414+
411415
private Optional<String> findDisplayText(Coding coding) {
412416
return Optional.ofNullable(coding.getDisplay());
413417
}
@@ -420,7 +424,7 @@ private boolean isPrescribingAgency(Coding coding) {
420424
return coding.hasSystem() && coding.getSystem().equals(CARE_CONNECT_PRESCRIBING_AGENCY_SYSTEM);
421425
}
422426

423-
private Optional<Extension> retrieveDescriptionExtension(Coding coding) {
427+
private static Optional<Extension> retrieveDescriptionExtension(Coding coding) {
424428
return coding
425429
.getExtension()
426430
.stream()
@@ -435,7 +439,6 @@ public String getDisplayFromCodeableConcept(CodeableConcept codeableConcept) {
435439
}
436440

437441
public String mapToNullFlavorCodeableConcept(CodeableConcept codeableConcept) {
438-
439442
var builder = CodeableConceptCdTemplateParameters.builder().nullFlavor(true);
440443
var mainCode = getSnomedCodeCoding(codeableConcept);
441444

@@ -456,15 +459,6 @@ public String mapToNullFlavorCodeableConceptForAllergy(
456459
return TemplateUtils.fillTemplate(CODEABLE_CONCEPT_CD_TEMPLATE, builder.build());
457460
}
458461

459-
private String buildNullFlavourCodeableConceptCd(CodeableConcept codeableConcept, Optional<Coding> snomedCode) {
460-
var builder = CodeableConceptCdTemplateParameters.builder();
461-
builder.nullFlavor(true);
462-
var originalText = findOriginalText(codeableConcept, snomedCode);
463-
originalText.ifPresent(builder::mainOriginalText);
464-
465-
return TemplateUtils.fillTemplate(CODEABLE_CONCEPT_CD_TEMPLATE, builder.build());
466-
}
467-
468462
private Optional<String> getMainCode(Optional<List<Extension>> descriptionExtensions, Coding snomedCodeCoding) {
469463
if (descriptionExtensions.isPresent()) {
470464
var descriptionCode = descriptionExtensions.get().stream()
@@ -494,4 +488,4 @@ private Optional<String> getMainDisplayName(Optional<List<Extension>> descriptio
494488

495489
return Optional.ofNullable(snomedCodeCoding.getDisplay());
496490
}
497-
}
491+
}

0 commit comments

Comments
 (0)