Skip to content
Open
Show file tree
Hide file tree
Changes from 3 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
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@
import org.openmrs.Visit;
import org.openmrs.api.APIException;
import org.openmrs.module.attachments.obs.Attachment;
import org.openmrs.module.attachments.obs.DefaultAttachmentHandler;
import org.openmrs.module.attachments.obs.ImageAttachmentHandler;
import org.openmrs.module.attachments.obs.TestHelper;
import org.openmrs.test.BaseModuleContextSensitiveTest;
import org.springframework.beans.factory.annotation.Autowired;
Expand Down Expand Up @@ -305,4 +307,27 @@ public void getAttachments_shouldThrowWhenFetchingNonComplexObs() throws Excepti
//
as.getAttachments(patient, encounter, true);
}

@Test
public void getAttachments_shouldReturnAttachmentsAssociatedWithSpecifiedConcept() throws Exception {
//
// setup
//
Concept concept = testHelper.createComplexConcept("f4fab86d-4a1d-4245-8aa2-19f49b5ab07a", "Random files",
DefaultAttachmentHandler.class.getSimpleName(), "Random binary files");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A detail but why is "files" plural?

Patient patient = ctx.getPatientService().getPatient(2);
Obs obs = testHelper.saveComplexObs(null, concept, null, 1, 0).get(0);

//
// replay
//
List<Attachment> attachments = as.getAttachments(patient, concept);

//
// verify
//
Assert.assertEquals(1, attachments.size());
Assert.assertEquals(obs.getUuid(), attachments.get(0).getUuid());

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import java.util.List;

import org.openmrs.Concept;
import org.openmrs.Encounter;
import org.openmrs.Patient;
import org.openmrs.Visit;
Expand Down Expand Up @@ -60,5 +61,13 @@ public interface AttachmentsService {
*/
List<Attachment> getAttachments(Patient patient, Visit visit, boolean includeVoided);

/**
* Get a patient's attachments that are associated with a specified concept.
*
* @param concept
* @throws APIException if non-complex obs are mistakenly returned
*/
List<Attachment> getAttachments(Patient patient, Concept concept);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is my main issue, the changes to AttachmentsService are possibly much wider than thought here.

I believe this is where the existing API could be deprecated and a new one is to be introduced that caters for the new concept argument.

Copy link
Member Author

@samuelmale samuelmale May 11, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe this is where the existing API could be deprecated and a new one is to be introduced that caters for the new concept argument.

@mks-d Looks like some good time is required for this one; do you think its okay to handle this in a different/followup ticket?


Attachment save(Attachment attachment, String reason);
}
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,21 @@ public List<Attachment> getAttachments(Patient patient, final Visit visit, boole
return attachments;
}

@Override
public List<Attachment> getAttachments(Patient patient, Concept concept) {
List<Obs> obsList = ctx.getObsService().getObservationsByPersonAndConcept(patient, concept);
List<Attachment> attachments = new ArrayList<>();
for (Obs obs : obsList) {
if (!obs.isComplex()) {
throw new APIException(NON_COMPLEX_OBS_ERR);
}
obs = getComplexObs(obs);
attachments.add(new Attachment(obs, ctx.getComplexDataHelper()));
}

return attachments;
}

// Get list of attachment complex concepts
protected List<Concept> getAttachmentConcepts() {
List<String> conceptsComplex = ctx.getConceptComplexList();
Expand Down Expand Up @@ -161,4 +176,5 @@ private Obs getComplexObs(Obs obs) {
String view = ctx.getComplexViewHelper().getView(obs, AttachmentsConstants.ATT_VIEW_THUMBNAIL);
return ctx.getObsService().getComplexObs(obs.getId(), view);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import net.coobird.thumbnailator.Thumbnails;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.openmrs.Concept;
import org.openmrs.ConceptComplex;
import org.openmrs.Encounter;
import org.openmrs.Obs;
Expand Down Expand Up @@ -66,8 +67,18 @@ protected void prepareComplexObs(Visit visit, Person person, Encounter encounter

public Obs saveImageAttachment(Visit visit, Person person, Encounter encounter, String fileCaption,
MultipartFile multipartFile, String instructions) throws IOException {

conceptComplex = context.getConceptComplex(ContentFamily.IMAGE);
return saveImageAttachment(visit, person, encounter, conceptComplex, fileCaption, multipartFile, instructions);
}

public Obs saveImageAttachment(Visit visit, Person person, Encounter encounter, Concept conceptComplex,
String fileCaption, MultipartFile multipartFile, String instructions) throws IOException {
if (conceptComplex != null) {
this.conceptComplex = (ConceptComplex) conceptComplex;

} else {
this.conceptComplex = context.getConceptComplex(ContentFamily.IMAGE);
}
prepareComplexObs(visit, person, encounter, fileCaption);

Object image = multipartFile.getInputStream();
Expand All @@ -85,6 +96,16 @@ public Obs saveImageAttachment(Visit visit, Person person, Encounter encounter,
public Obs saveOtherAttachment(Visit visit, Person person, Encounter encounter, String fileCaption,
MultipartFile multipartFile, String instructions) throws IOException {
conceptComplex = context.getConceptComplex(ContentFamily.OTHER);
return saveOtherAttachment(visit, person, encounter, conceptComplex, fileCaption, multipartFile, instructions);
}

public Obs saveOtherAttachment(Visit visit, Person person, Encounter encounter, Concept conceptComplex,
String fileCaption, MultipartFile multipartFile, String instructions) throws IOException {
if (conceptComplex != null) {
this.conceptComplex = (ConceptComplex) conceptComplex;
} else {
this.conceptComplex = context.getConceptComplex(ContentFamily.OTHER);
}
prepareComplexObs(visit, person, encounter, fileCaption);

obs.setComplexData(complexDataHelper.build(instructions, multipartFile.getOriginalFilename(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@ public class Attachment extends BaseOpenmrsData implements java.io.Serializable

protected ComplexData complexData = null;


public Attachment() {
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,18 @@
package org.openmrs.module.attachments.obs;

import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import java.util.Random;

import javax.imageio.ImageIO;

import org.apache.commons.io.FilenameUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.RandomStringUtils;
Expand All @@ -23,6 +28,7 @@
import org.openmrs.Patient;
import org.openmrs.Provider;
import org.openmrs.Visit;
import org.openmrs.api.ConceptService;
import org.openmrs.api.EncounterService;
import org.openmrs.api.context.Context;
import org.openmrs.module.attachments.AttachmentsActivator;
Expand Down Expand Up @@ -257,6 +263,22 @@ public String getTestComplexObsFilePath() {
* @return List of saved attachments/complex obs.
*/
public List<Obs> saveComplexObs(Encounter encounter, int count, int otherCount) throws IOException {
return saveComplexObs(encounter, null, null, count, otherCount);
}

/**
* Boilerplate method to save a collection of complex obs based on the encounter.
*
* @param encounter target encounter for save the complex obs. Leave null to save encounter-less
* complex obs.
* @param concept The concept that will be associated with attachment obs to be saved.
* @param otherConcept The concept that will be associated with other complex obs to be saved.
* @param count The number of the attachments/complex obs to be saved.
* @param otherCount The number of other complex obs to be saved.
* @return List of saved attachments/complex obs.
*/
public List<Obs> saveComplexObs(Encounter encounter, Concept concept, Concept otherConcept, int count, int otherCount)
throws IOException {
List<Obs> obsList = new ArrayList<>();
byte[] randomData = new byte[20];
Patient patient = (encounter == null) ? context.getPatientService().getPatient(2) : encounter.getPatient();
Expand All @@ -270,14 +292,14 @@ public List<Obs> saveComplexObs(Encounter encounter, int count, int otherCount)
String filename = RandomStringUtils.randomAlphabetic(7) + ".ext";
MockMultipartFile multipartRandomFile = new MockMultipartFile(FilenameUtils.getBaseName(filename), filename,
"application/octet-stream", randomData);
obsList.add(obsSaver.saveOtherAttachment(visit, patient, encounter, fileCaption, multipartRandomFile,
obsList.add(obsSaver.saveOtherAttachment(visit, patient, encounter, concept, fileCaption, multipartRandomFile,
ValueComplex.INSTRUCTIONS_DEFAULT));
}

// Saves a complex obs as if they had been saved outside of Attachments
for (int i = 0; i < otherCount; i++) {
Obs obs = new Obs();
obs.setConcept(otherConceptComplex);
obs.setConcept(otherConcept != null ? otherConcept : otherConceptComplex);
obs.setObsDatetime(new Date());
obs.setPerson(patient);
obs.setEncounter(encounter);
Expand All @@ -294,4 +316,34 @@ public List<Obs> saveComplexObs(Encounter encounter, int count, int otherCount)
}
return obsList;
}

/**
* Factory method that constructs and persists a ComplexConcept object
*
* @return complexConcept
*/
public Concept createComplexConcept(String uuid, String name, String handler, String description) {
ConceptService conceptService = Context.getConceptService();
ConceptComplex conceptComplex = new ConceptComplex();
conceptComplex.setUuid(uuid);
conceptComplex.setHandler(handler);
ConceptName conceptName = new ConceptName(name, Locale.ENGLISH);
conceptComplex.setFullySpecifiedName(conceptName);
conceptComplex.setPreferredName(conceptName);
conceptComplex.setConceptClass(conceptService.getConceptClassByName("Question"));
conceptComplex.setDatatype(conceptService.getConceptDatatypeByUuid(ConceptDatatype.COMPLEX_UUID));
conceptComplex.addDescription(new ConceptDescription(description, Locale.ENGLISH));

return conceptService.saveConcept(conceptComplex);
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't there any such concept in the standard test dataset, or one of Core's datasets?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not to my knowledge. The standard dataset has limited complex concepts.


public static byte[] loadImageResourceToByteArray(ClassLoader loader, String imageName, String contentType)
throws IOException {
InputStream inputStream = loader.getResourceAsStream(imageName);
BufferedImage img = ImageIO.read(inputStream);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ImageIO.write(img, contentType, baos);

return baos.toByteArray();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang.BooleanUtils;
import org.apache.commons.lang.StringUtils;
import org.openmrs.Concept;
import org.openmrs.Encounter;
import org.openmrs.Obs;
import org.openmrs.Patient;
Expand Down Expand Up @@ -57,8 +58,8 @@ public class AttachmentResource1_10 extends DataDelegatingCrudResource<Attachmen
private ComplexObsSaver obsSaver = Context.getRegisteredComponent(AttachmentsConstants.COMPONENT_COMPLEXOBS_SAVER,
ComplexObsSaver.class);

private AttachmentsContext ctx = Context
.getRegisteredComponent(AttachmentsConstants.COMPONENT_ATT_CONTEXT, AttachmentsContext.class);
private AttachmentsContext ctx = Context.getRegisteredComponent(AttachmentsConstants.COMPONENT_ATT_CONTEXT,
AttachmentsContext.class);

@Override
public Attachment newDelegate() {
Expand Down Expand Up @@ -104,6 +105,8 @@ public Object upload(MultipartFile file, RequestContext context) throws Response
Visit visit = Context.getVisitService().getVisitByUuid(context.getParameter("visit"));
Encounter encounter = Context.getEncounterService().getEncounterByUuid(context.getParameter("encounter"));
Provider provider = Context.getProviderService().getProviderByUuid(context.getParameter("provider"));
final String conceptComplexUuid = context.getParameter("concept");
Concept conceptComplex = null;
String fileCaption = context.getParameter("fileCaption");
String instructions = context.getParameter("instructions");
String base64Content = context.getParameter("base64Content");
Expand All @@ -112,7 +115,7 @@ public Object upload(MultipartFile file, RequestContext context) throws Response
file = new Base64MultipartFile(base64Content);
}
// Verify File Size
if (ctx.getMaxUploadFileSize() * 1024 * 1024 < (double)file.getSize()) {
if (ctx.getMaxUploadFileSize() * 1024 * 1024 < (double) file.getSize()) {
throw new IllegalRequestException("The file exceeds the maximum size");
}

Expand All @@ -132,6 +135,17 @@ public Object upload(MultipartFile file, RequestContext context) throws Response
}
}

// Verify concept complex
if (StringUtils.isNotBlank(conceptComplexUuid)) {
conceptComplex = Context.getConceptService().getConceptByUuid(context.getParameter("concept"));
if (conceptComplex == null) {
throw new IllegalArgumentException("Cannot find concept with UUID: " + conceptComplexUuid);
}
if (!conceptComplex.isComplex()) {
throw new IllegalArgumentException("A complex concept must be set in order to create complex obs.");
}
}

if (visit != null && encounter == null) {
encounter = ctx.getAttachmentEncounter(patient, visit, provider);
}
Expand All @@ -144,12 +158,14 @@ public Object upload(MultipartFile file, RequestContext context) throws Response
Obs obs;
switch (getContentFamily(file.getContentType())) {
case IMAGE:
obs = obsSaver.saveImageAttachment(visit, patient, encounter, fileCaption, file, instructions);
obs = obsSaver.saveImageAttachment(visit, patient, encounter, conceptComplex, fileCaption, file,
instructions);
break;

case OTHER:
default:
obs = obsSaver.saveOtherAttachment(visit, patient, encounter, fileCaption, file, instructions);
obs = obsSaver.saveOtherAttachment(visit, patient, encounter, conceptComplex, fileCaption, file,
instructions);
break;
}

Expand Down Expand Up @@ -200,7 +216,7 @@ public static void voidEncounterIfEmpty(EncounterService encounterService, Strin
* @param includeVoided
*/
public List<Attachment> search(AttachmentsService as, Patient patient, Visit visit, Encounter encounter,
String includeEncounterless, boolean includeVoided) {
String includeEncounterless, Concept concept, boolean includeVoided) {

List<Attachment> attachmentList = new ArrayList<>();

Expand All @@ -218,10 +234,12 @@ public List<Attachment> search(AttachmentsService as, Patient patient, Visit vis
if (visit != null && encounter == null) {
attachmentList = as.getAttachments(patient, visit, includeVoided);
}
if (encounter == null && visit == null) {
if (encounter == null && visit == null && concept == null) {
attachmentList = as.getAttachments(patient, includeVoided);
}

if (concept != null) {
attachmentList = as.getAttachments(patient, concept);
}
}
return attachmentList;
}
Expand All @@ -245,6 +263,7 @@ protected PageableResult doSearch(RequestContext context) {
Encounter encounter = Context.getEncounterService().getEncounterByUuid(context.getParameter("encounter"));
String includeEncounterless = context.getParameter("includeEncounterless");
Boolean includeVoided = BooleanUtils.toBoolean(context.getParameter("includeVoided"));
Concept concept = Context.getConceptService().getConceptByUuid(context.getParameter("concept"));

// Verify Parameters
if (patient == null) {
Expand All @@ -257,7 +276,7 @@ protected PageableResult doSearch(RequestContext context) {

// Search Attachments
List<Attachment> attachmentList = search(ctx.getAttachmentsService(), patient, visit, encounter,
includeEncounterless, includeVoided);
includeEncounterless, concept, includeVoided);

if (attachmentList != null) {
return new NeedsPaging<Attachment>(attachmentList, context);
Expand Down
Loading