diff --git a/api/src/main/java/org/openmrs/module/fhir2/api/translators/impl/TaskTranslatorImpl.java b/api/src/main/java/org/openmrs/module/fhir2/api/translators/impl/TaskTranslatorImpl.java index 581483b56..98cc40b8c 100644 --- a/api/src/main/java/org/openmrs/module/fhir2/api/translators/impl/TaskTranslatorImpl.java +++ b/api/src/main/java/org/openmrs/module/fhir2/api/translators/impl/TaskTranslatorImpl.java @@ -21,9 +21,12 @@ import lombok.AccessLevel; import lombok.Setter; +import org.hl7.fhir.r4.model.Annotation; import org.hl7.fhir.r4.model.Identifier; +import org.hl7.fhir.r4.model.Period; import org.hl7.fhir.r4.model.Task; import org.openmrs.module.fhir2.FhirConstants; +import org.openmrs.module.fhir2.api.translators.ConceptTranslator; import org.openmrs.module.fhir2.api.translators.ReferenceTranslator; import org.openmrs.module.fhir2.api.translators.TaskInputTranslator; import org.openmrs.module.fhir2.api.translators.TaskOutputTranslator; @@ -45,6 +48,9 @@ public class TaskTranslatorImpl implements TaskTranslator { @Autowired private TaskOutputTranslator taskOutputTranslator; + @Autowired + private ConceptTranslator conceptTranslator; + @Override public Task toFhirResource(@Nonnull FhirTask openmrsTask) { notNull(openmrsTask, "The openmrsTask object should not be null"); @@ -116,6 +122,30 @@ private void setFhirTaskFields(FhirTask openmrsTask, Task fhirTask) { .filter(Objects::nonNull).collect(Collectors.toList())); } + if (openmrsTask.getTaskCode() != null) { + fhirTask.setCode(conceptTranslator.toFhirResource(openmrsTask.getTaskCode())); + } + + if (openmrsTask.getPartOfReferences() != null && !openmrsTask.getPartOfReferences().isEmpty()) { + fhirTask.setPartOf(openmrsTask.getPartOfReferences().stream().map(referenceTranslator::toFhirResource) + .collect(Collectors.toList())); + } + + if (openmrsTask.getExecutionStartTime() != null || openmrsTask.getExecutionEndTime() != null) { + Period period = new Period(); + if (openmrsTask.getExecutionStartTime() != null) { + period.setStart(openmrsTask.getExecutionStartTime()); + } + if (openmrsTask.getExecutionEndTime() != null) { + period.setEnd(openmrsTask.getExecutionEndTime()); + } + fhirTask.setExecutionPeriod(period); + } + + if (openmrsTask.getComment() != null) { + fhirTask.addNote(new Annotation().setText(openmrsTask.getComment())); + } + fhirTask.setAuthoredOn(openmrsTask.getDateCreated()); if (openmrsTask.getDateChanged() != null) { @@ -183,6 +213,28 @@ private void setOpenmrsTaskFields(FhirTask openmrsTask, Task fhirTask) { .filter(Objects::nonNull).collect(Collectors.toSet())); } + if (fhirTask.hasCode()) { + openmrsTask.setTaskCode(conceptTranslator.toOpenmrsType(fhirTask.getCode())); + } + + if (fhirTask.hasExecutionPeriod()) { + if (fhirTask.getExecutionPeriod().hasStart()) { + openmrsTask.setExecutionStartTime(fhirTask.getExecutionPeriod().getStart()); + } + if (fhirTask.getExecutionPeriod().hasEnd()) { + openmrsTask.setExecutionEndTime(fhirTask.getExecutionPeriod().getEnd()); + } + } + + if (fhirTask.hasPartOf()) { + openmrsTask.setPartOfReferences( + fhirTask.getPartOf().stream().map(referenceTranslator::toOpenmrsType).collect(Collectors.toSet())); + } + + if (fhirTask.hasNote()) { + openmrsTask.setComment(fhirTask.getNoteFirstRep().getText()); + } + openmrsTask.setName(FhirConstants.TASK + "/" + fhirTask.getId()); } diff --git a/api/src/main/java/org/openmrs/module/fhir2/model/FhirTask.java b/api/src/main/java/org/openmrs/module/fhir2/model/FhirTask.java index ad51a7593..3cc1cf3b1 100644 --- a/api/src/main/java/org/openmrs/module/fhir2/model/FhirTask.java +++ b/api/src/main/java/org/openmrs/module/fhir2/model/FhirTask.java @@ -19,16 +19,19 @@ import javax.persistence.Id; import javax.persistence.JoinColumn; import javax.persistence.JoinTable; +import javax.persistence.ManyToOne; import javax.persistence.OneToMany; import javax.persistence.OneToOne; import javax.persistence.Table; +import java.util.Date; import java.util.Set; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; import org.openmrs.BaseOpenmrsMetadata; +import org.openmrs.Concept; @Data @NoArgsConstructor @@ -129,4 +132,37 @@ public enum TaskIntent { @OneToOne(cascade = CascadeType.ALL) @JoinColumn(name = "location_reference_id", referencedColumnName = "reference_id") private FhirReference locationReference; + + /** + * Type of Task + */ + @ManyToOne + @JoinColumn(name = "task_code", referencedColumnName = "concept_id") + protected Concept taskCode; + + /** + * Task that this particular task is part of. + */ + @OneToMany(cascade = CascadeType.ALL) + @JoinTable(name = "fhir_task_part_of_reference", joinColumns = @JoinColumn(name = "task_id"), inverseJoinColumns = @JoinColumn(name = "reference_id")) + private Set partOfReferences; + + /** + * Actual start time of the execution + */ + @Column(name = "execution_start_time") + private Date executionStartTime; + + /** + * Actual end time of the execution + */ + @Column(name = "execution_end_time") + private Date executionEndTime; + + /** + * Comment made about the task + */ + @Column(name = "comment") + private String comment; + } diff --git a/api/src/main/resources/liquibase.xml b/api/src/main/resources/liquibase.xml index 95b30dc04..f52e2958f 100644 --- a/api/src/main/resources/liquibase.xml +++ b/api/src/main/resources/liquibase.xml @@ -1067,4 +1067,47 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/api/src/test/java/org/openmrs/module/fhir2/api/translators/impl/TaskTranslatorImplTest.java b/api/src/test/java/org/openmrs/module/fhir2/api/translators/impl/TaskTranslatorImplTest.java index 9ed167693..00951b09d 100644 --- a/api/src/test/java/org/openmrs/module/fhir2/api/translators/impl/TaskTranslatorImplTest.java +++ b/api/src/test/java/org/openmrs/module/fhir2/api/translators/impl/TaskTranslatorImplTest.java @@ -29,15 +29,20 @@ import java.util.LinkedHashSet; import java.util.List; import java.util.Set; +import java.util.Calendar; import java.util.function.Consumer; import java.util.function.Function; + import org.exparity.hamcrest.date.DateMatchers; +import org.hamcrest.CoreMatchers; +import org.hl7.fhir.r4.model.Annotation; import org.hl7.fhir.r4.model.CodeableConcept; import org.hl7.fhir.r4.model.Coding; import org.hl7.fhir.r4.model.DateTimeType; import org.hl7.fhir.r4.model.DecimalType; import org.hl7.fhir.r4.model.Identifier; +import org.hl7.fhir.r4.model.Period; import org.hl7.fhir.r4.model.Reference; import org.hl7.fhir.r4.model.StringType; import org.hl7.fhir.r4.model.Task; @@ -49,6 +54,7 @@ import org.openmrs.BaseOpenmrsMetadata; import org.openmrs.Concept; import org.openmrs.module.fhir2.FhirConstants; +import org.openmrs.module.fhir2.api.translators.ConceptTranslator; import org.openmrs.module.fhir2.model.FhirReference; import org.openmrs.module.fhir2.model.FhirTask; import org.openmrs.module.fhir2.model.FhirTaskInput; @@ -59,6 +65,8 @@ public class TaskTranslatorImplTest { private static final String TASK_UUID = "d899333c-5bd4-45cc-b1e7-2f9542dbcbf6"; + private static final String PARENT_TASK_UUID = "c899333d-5cd4-45cc-b1e8-2f9542a4bf6"; + private static final String PATIENT_UUID = "123456-abcdef-123456"; private static final String SERVICE_REQUEST_UUID = "4e4851c3-c265-400e-acc9-1f1b0ac7f9c4"; @@ -94,6 +102,9 @@ public class TaskTranslatorImplTest { @Mock private TaskOutputTranslatorImpl taskOutputTranslator; + @Mock + private ConceptTranslator conceptTranslator; + private TaskTranslatorImpl taskTranslator; @Before @@ -102,6 +113,7 @@ public void setup() { taskTranslator.setReferenceTranslator(referenceTranslator); taskTranslator.setTaskInputTranslator(taskInputTranslator); taskTranslator.setTaskOutputTranslator(taskOutputTranslator); + taskTranslator.setConceptTranslator(conceptTranslator); } @Test @@ -203,7 +215,7 @@ public void toOpenmrsType_shouldSetOpenmrsTaskUUIDWhenNull() { @Test public void toOpenmrsType_shouldTranslateNullElements() { - Task task = new Task().setBasedOn(null).setEncounter(null).setFor(null).setOwner(null).setInput(null) + Task task = new Task().setBasedOn(null).setEncounter(null).setFor(null).setOwner(null).setInput(null).setPartOf(null) .setOutput(null); FhirTask result = taskTranslator.toOpenmrsType(task); @@ -946,6 +958,174 @@ public void toOpenmrsType_shouldTranslateMultipleInputValueTypes() { } + // Task Code + @Test + public void toFhirResource_shouldTranslateTaskCode() { + + FhirTask task = new FhirTask(); + Concept openmrsObject = new Concept(); + CodeableConcept fhirObject = newCodeableConcept(); + when(conceptTranslator.toFhirResource(openmrsObject)).thenReturn(fhirObject); + + task.setTaskCode(openmrsObject); + Task result = taskTranslator.toFhirResource(task); + assertThat(result.getCode(), CoreMatchers.notNullValue()); + assertThat(result.getCode(), CoreMatchers.equalTo(fhirObject)); + } + + @Test + public void toOpenmrsType_shouldTranslateTaskCode() { + Task fhirTask = new Task(); + Concept openmrsObject = new Concept(); + CodeableConcept fhirObject = newCodeableConcept(); + when(conceptTranslator.toOpenmrsType(fhirObject)).thenReturn(openmrsObject); + fhirTask.setCode(fhirObject); + + FhirTask task = taskTranslator.toOpenmrsType(fhirTask); + assertThat(task.getTaskCode(), CoreMatchers.notNullValue()); + assertThat(task.getTaskCode(), CoreMatchers.equalTo(openmrsObject)); + } + + @Test + public void toOpenmrsType_shouldUpdateTaskCodeOnExistingTask() { + FhirTask task = new FhirTask(); + task.setTaskCode(null); + task.setUuid(TASK_UUID); + + Concept openmrsObject = new Concept(); + CodeableConcept fhirObject = newCodeableConcept(); + when(conceptTranslator.toOpenmrsType(fhirObject)).thenReturn(openmrsObject); + Task fhirTask = taskTranslator.toFhirResource(task); + fhirTask.setCode(fhirObject); + + FhirTask result = taskTranslator.toOpenmrsType(task, fhirTask); + + assertThat(result, notNullValue()); + assertThat(result.getTaskCode(), equalTo(openmrsObject)); + } + + // Task.partOf + @Test + public void toFhirResource_shouldTranslatePartOf() { + FhirTask task = new FhirTask(); + + shouldTranslateReferenceListToFhir(task, FhirConstants.TASK, PARENT_TASK_UUID, task::setPartOfReferences, + t -> new ArrayList<>(t.getPartOf())); + } + + @Test + public void toOpenmrsType_shouldTranslatePartOf() { + Task task = new Task(); + + shouldTranslateReferenceListToOpenmrs(task, FhirConstants.TASK, PARENT_TASK_UUID, task::setPartOf, + FhirTask::getPartOfReferences); + } + + @Test + public void toOpenmrsType_shouldUpdatePartOf() { + Task task = new Task(); + + shouldUpdateReferenceListInOpenmrs(task, FhirConstants.TASK, PARENT_TASK_UUID, task::setPartOf, + FhirTask::getPartOfReferences); + } + + //Task.executionPeriod + + @Test + public void toFhirResource_shouldTranslateExecutionStartAndEndTimeToExecutionPeriod() { + Date startTime = new Date(); + Calendar calendar = Calendar.getInstance(); + calendar.setTime(startTime); + calendar.add(Calendar.HOUR_OF_DAY, 2); + Date endTime = calendar.getTime(); + + FhirTask task = new FhirTask(); + task.setExecutionStartTime(startTime); + task.setExecutionEndTime(endTime); + Task result = taskTranslator.toFhirResource(task); + + assertThat(result.getExecutionPeriod(), notNullValue()); + assertThat(result.getExecutionPeriod().getStart(), equalTo(startTime)); + assertThat(result.getExecutionPeriod().getEnd(), equalTo(endTime)); + } + + @Test + public void toOpenmrsType_shouldTranslateExecutionPeriodToExecutionStartAndEndTime() { + Task fhirTask = new Task(); + Date startTime = new Date(); + Calendar calendar = Calendar.getInstance(); + calendar.setTime(startTime); + calendar.add(Calendar.HOUR_OF_DAY, 2); + Date endTime = calendar.getTime(); + + Period executionPeriod = new Period(); + executionPeriod.setStart(startTime); + executionPeriod.setEnd(endTime); + fhirTask.setExecutionPeriod(executionPeriod); + FhirTask result = taskTranslator.toOpenmrsType(fhirTask); + + assertThat(result.getExecutionStartTime(), equalTo(startTime)); + assertThat(result.getExecutionEndTime(), equalTo(endTime)); + } + + @Test + public void toOpenmrsType_shouldupdateExecutionTimeOnExistingTask() { + Date startTime = new Date(); + Calendar calendar = Calendar.getInstance(); + calendar.setTime(startTime); + calendar.add(Calendar.HOUR_OF_DAY, 2); + Date endTime = calendar.getTime(); + + FhirTask task = new FhirTask(); + task.setExecutionStartTime(null); + task.setExecutionEndTime(null); + task.setUuid(TASK_UUID); + + Task fhirTask = taskTranslator.toFhirResource(task); + Period executionPeriod = new Period(); + executionPeriod.setStart(startTime); + executionPeriod.setEnd(endTime); + fhirTask.setExecutionPeriod(executionPeriod); + FhirTask result = taskTranslator.toOpenmrsType(task, fhirTask); + + assertThat(result.getExecutionStartTime(), equalTo(startTime)); + assertThat(result.getExecutionEndTime(), equalTo(endTime)); + } + + //Task.note + @Test + public void toFhirResource_shouldTranslateCommentToNote() { + FhirTask task = new FhirTask(); + task.setComment("Test Comment"); + + Task result = taskTranslator.toFhirResource(task); + assertThat(result, notNullValue()); + assertThat(result.getNote().get(0).getText(), equalTo("Test Comment")); + } + + @Test + public void toOpenmrsType_shouldTranslateNoteToComment() { + Task task = new Task(); + task.addNote(new Annotation().setText("Test Comment")); + FhirTask result = taskTranslator.toOpenmrsType(task); + assertThat(result, notNullValue()); + assertThat(result.getComment(), equalTo("Test Comment")); + } + + @Test + public void toOpenmrsType_shouldUpdateCommentOnExistingTask() { + FhirTask task = new FhirTask(); + task.setComment(null); + + Task fhirTask = taskTranslator.toFhirResource(task); + fhirTask.addNote(new Annotation().setText("Test Comment")); + + FhirTask result = taskTranslator.toOpenmrsType(fhirTask); + + assertThat(result, notNullValue()); + assertThat(result.getComment(), equalTo("Test Comment")); + } + // Task.authoredOn @Test public void toFhirResource_shouldTranslateAuthoredOn() { @@ -1142,4 +1322,11 @@ private void shouldUpdateReferenceListInOpenmrs(Task task, String refType, Strin assertThat(resultReference.iterator().next().getReference(), equalTo(refUuid)); assertThat(resultReference.iterator().next().getType(), equalTo(refType)); } + + private CodeableConcept newCodeableConcept() { + CodeableConcept c = new CodeableConcept(); + c.addCoding(new Coding("system", "code", "display")); + return c; + } + }