diff --git a/src/main/java/org/breedinginsight/brapi/v2/constants/BrAPIAdditionalInfoFields.java b/src/main/java/org/breedinginsight/brapi/v2/constants/BrAPIAdditionalInfoFields.java index 9e0289a51..39fd856a2 100644 --- a/src/main/java/org/breedinginsight/brapi/v2/constants/BrAPIAdditionalInfoFields.java +++ b/src/main/java/org/breedinginsight/brapi/v2/constants/BrAPIAdditionalInfoFields.java @@ -43,4 +43,6 @@ public final class BrAPIAdditionalInfoFields { public static final String OBSERVATION_DATASET_ID = "observationDatasetId"; public static final String FEMALE_PARENT_UNKNOWN = "femaleParentUnknown"; public static final String MALE_PARENT_UNKNOWN = "maleParentUnknown"; + + public static final String TREATMENTS = "treatments"; } diff --git a/src/main/java/org/breedinginsight/brapps/importer/daos/BrAPIObservationUnitDAO.java b/src/main/java/org/breedinginsight/brapps/importer/daos/BrAPIObservationUnitDAO.java index 6401c031e..66465a7a4 100644 --- a/src/main/java/org/breedinginsight/brapps/importer/daos/BrAPIObservationUnitDAO.java +++ b/src/main/java/org/breedinginsight/brapps/importer/daos/BrAPIObservationUnitDAO.java @@ -17,11 +17,18 @@ package org.breedinginsight.brapps.importer.daos; +import com.google.gson.Gson; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.reflect.TypeToken; import io.micronaut.context.annotation.Property; +import org.brapi.client.v2.JSON; import org.brapi.client.v2.model.exceptions.ApiException; import org.brapi.client.v2.modules.phenotype.ObservationUnitsApi; +import org.brapi.v2.model.pheno.BrAPIObservationTreatment; import org.brapi.v2.model.pheno.BrAPIObservationUnit; import org.brapi.v2.model.pheno.request.BrAPIObservationUnitSearchRequest; +import org.breedinginsight.brapi.v2.constants.BrAPIAdditionalInfoFields; import org.breedinginsight.brapps.importer.model.ImportUpload; import org.breedinginsight.brapps.importer.services.ExternalReferenceSource; import org.breedinginsight.daos.ProgramDAO; @@ -34,6 +41,7 @@ import javax.inject.Inject; import javax.inject.Singleton; import javax.validation.constraints.NotNull; +import java.lang.reflect.Type; import java.util.*; @Singleton @@ -46,6 +54,9 @@ public class BrAPIObservationUnitDAO { private final String referenceSource; + private final Gson gson = new JSON().getGson(); + private final Type treatmentlistType = new TypeToken>(){}.getType(); + @Inject public BrAPIObservationUnitDAO(ProgramDAO programDAO, ImportDAO importDAO, BrAPIDAOUtil brAPIDAOUtil, BrAPIEndpointProvider brAPIEndpointProvider, ProgramService programService, @Property(name = "brapi.server.reference-source") String referenceSource) { this.programDAO = programDAO; @@ -64,17 +75,19 @@ public List getObservationUnitByName(List observat BrAPIObservationUnitSearchRequest observationUnitSearchRequest = new BrAPIObservationUnitSearchRequest(); observationUnitSearchRequest.programDbIds(List.of(program.getBrapiProgram().getProgramDbId())); observationUnitSearchRequest.observationUnitNames(observationUnitNames); - ObservationUnitsApi api = brAPIEndpointProvider.get(programDAO.getCoreClient(program.getId()), ObservationUnitsApi.class); - return brAPIDAOUtil.search( - api::searchObservationunitsPost, - api::searchObservationunitsSearchResultsDbIdGet, - observationUnitSearchRequest - ); + + return searchObservationUnitsAndProcess(observationUnitSearchRequest, program.getId()); } + /** + * Create observation units, mutates brAPIObservationUnitList + */ public List createBrAPIObservationUnits(List brAPIObservationUnitList, UUID programId, ImportUpload upload) throws ApiException { ObservationUnitsApi api = brAPIEndpointProvider.get(programDAO.getCoreClient(programId), ObservationUnitsApi.class); - return brAPIDAOUtil.post(brAPIObservationUnitList, upload, api::observationunitsPost, importDAO::update); + preprocessObservationUnits(brAPIObservationUnitList); + List ous = brAPIDAOUtil.post(brAPIObservationUnitList, upload, api::observationunitsPost, importDAO::update); + processObservationUnits(ous); + return ous; } public List getObservationUnitsById(Collection observationUnitExternalIds, Program program) throws ApiException { @@ -88,10 +101,7 @@ public List getObservationUnitsById(Collection obs observationUnitSearchRequest.externalReferenceIDs(new ArrayList<>(observationUnitExternalIds)); observationUnitSearchRequest.externalReferenceSources(List.of(String.format("%s/%s", referenceSource, ExternalReferenceSource.OBSERVATION_UNITS.getName()))); - ObservationUnitsApi api = brAPIEndpointProvider.get(programDAO.getCoreClient(program.getId()), ObservationUnitsApi.class); - return brAPIDAOUtil.search(api::searchObservationunitsPost, - api::searchObservationunitsSearchResultsDbIdGet, - observationUnitSearchRequest); + return searchObservationUnitsAndProcess(observationUnitSearchRequest, program.getId()); } public List getObservationUnitsForStudyDbId(@NotNull String studyDbId, Program program) throws ApiException { @@ -100,10 +110,7 @@ public List getObservationUnitsForStudyDbId(@NotNull Strin .getProgramDbId())); observationUnitSearchRequest.studyDbIds(List.of(studyDbId)); - ObservationUnitsApi api = brAPIEndpointProvider.get(programDAO.getCoreClient(program.getId()), ObservationUnitsApi.class); - return brAPIDAOUtil.search(api::searchObservationunitsPost, - api::searchObservationunitsSearchResultsDbIdGet, - observationUnitSearchRequest); + return searchObservationUnitsAndProcess(observationUnitSearchRequest, program.getId()); } public List getObservationUnitsForTrialDbId(@NotNull UUID programId, @NotNull String trialDbId) throws ApiException, DoesNotExistException { Program program = programService.getById(programId).orElseThrow(() -> new DoesNotExistException("Program id does not exist")); @@ -113,9 +120,47 @@ public List getObservationUnitsForTrialDbId(@NotNull UUID .getProgramDbId())); observationUnitSearchRequest.trialDbIds(List.of(trialDbId)); - ObservationUnitsApi api = brAPIEndpointProvider.get(programDAO.getCoreClient(program.getId()), ObservationUnitsApi.class); - return brAPIDAOUtil.search(api::searchObservationunitsPost, + return searchObservationUnitsAndProcess(observationUnitSearchRequest, programId); + } + + + /** + * Perform observation unit search and process returned observation units to handle any modifications to the data + * to be returned by bi-api + */ + private List searchObservationUnitsAndProcess(BrAPIObservationUnitSearchRequest request, UUID programId) throws ApiException { + + ObservationUnitsApi api = brAPIEndpointProvider.get(programDAO.getCoreClient(programId), ObservationUnitsApi.class); + List brapiObservationUnits = brAPIDAOUtil.search(api::searchObservationunitsPost, api::searchObservationunitsSearchResultsDbIdGet, - observationUnitSearchRequest); + request); + + processObservationUnits(brapiObservationUnits); + return brapiObservationUnits; + } + + private void processObservationUnits(List brapiObservationUnits) { + + // if has treatments in additionalInfo, copy to treatments property + for (BrAPIObservationUnit ou : brapiObservationUnits) { + JsonObject additionalInfo = ou.getAdditionalInfo(); + if (additionalInfo != null) { + JsonElement treatmentsElement = additionalInfo.get(BrAPIAdditionalInfoFields.TREATMENTS); + if (treatmentsElement != null) { + List treatments = gson.fromJson(treatmentsElement, treatmentlistType); + ou.setTreatments(treatments); + } + } + } + } + + private void preprocessObservationUnits(List brapiObservationUnits) { + // add treatments to additional info + for (BrAPIObservationUnit obsUnit : brapiObservationUnits) { + List treatments = obsUnit.getTreatments(); + if (treatments != null) { + obsUnit.putAdditionalInfoItem(BrAPIAdditionalInfoFields.TREATMENTS, treatments); + } + } } } diff --git a/src/test/java/org/breedinginsight/brapps/importer/daos/BrAPIObservationUnitDAOTest.java b/src/test/java/org/breedinginsight/brapps/importer/daos/BrAPIObservationUnitDAOTest.java new file mode 100644 index 000000000..ce742dc7c --- /dev/null +++ b/src/test/java/org/breedinginsight/brapps/importer/daos/BrAPIObservationUnitDAOTest.java @@ -0,0 +1,144 @@ +package org.breedinginsight.brapps.importer.daos; + +import com.google.gson.Gson; +import io.kowalski.fannypack.FannyPack; +import io.micronaut.http.client.RxHttpClient; +import io.micronaut.http.client.annotation.Client; +import io.micronaut.test.extensions.junit5.annotation.MicronautTest; +import lombok.SneakyThrows; +import org.brapi.client.v2.JSON; +import org.brapi.v2.model.pheno.BrAPIObservationTreatment; +import org.brapi.v2.model.pheno.BrAPIObservationUnit; +import org.breedinginsight.BrAPITest; +import org.breedinginsight.api.model.v1.request.ProgramRequest; +import org.breedinginsight.api.model.v1.request.SpeciesRequest; +import org.breedinginsight.api.v1.controller.TestTokenValidator; +import org.breedinginsight.brapi.v2.constants.BrAPIAdditionalInfoFields; +import org.breedinginsight.brapps.importer.model.ImportProgress; +import org.breedinginsight.brapps.importer.model.ImportUpload; +import org.breedinginsight.dao.db.tables.pojos.SpeciesEntity; +import org.breedinginsight.daos.SpeciesDAO; +import org.breedinginsight.daos.UserDAO; +import org.breedinginsight.model.Program; +import org.breedinginsight.model.User; +import org.breedinginsight.services.ProgramService; +import org.jooq.DSLContext; +import org.junit.jupiter.api.*; + +import javax.inject.Inject; +import java.util.List; + +import static org.breedinginsight.TestUtils.insertAndFetchTestProgram; +import static org.junit.jupiter.api.Assertions.assertEquals; + +@MicronautTest +@TestInstance(TestInstance.Lifecycle.PER_CLASS) +@TestMethodOrder(MethodOrderer.OrderAnnotation.class) +public class BrAPIObservationUnitDAOTest extends BrAPITest { + + private FannyPack fp; + + @Inject + private DSLContext dsl; + + @Inject + private SpeciesDAO speciesDAO; + + @Inject + private UserDAO userDAO; + + @Inject + private BrAPIObservationUnitDAO obsUnitDAO; + + @Inject + private ProgramService programService; + + private Program validProgram; + + private Gson gson; + private ImportUpload upload; + + private BrAPIObservationTreatment testTreatment; + + @Inject + @Client("/${micronaut.bi.api.version}") + RxHttpClient biClient; + + @BeforeAll + @SneakyThrows + public void setup() { + + ImportProgress progress = ImportProgress.builder().build(); + + upload = ImportUpload.uploadBuilder() + .progress(progress) + .build(); + + gson = new JSON().getGson(); + + // Add species needed to create program + fp = FannyPack.fill("src/test/resources/sql/brapi/species.sql"); + super.getBrapiDsl().execute(fp.get("InsertSpecies")); + SpeciesEntity validSpecies = speciesDAO.findAll().get(0); + + // Insert system admin role so can create program + FannyPack securityFp = FannyPack.fill("src/test/resources/sql/ProgramSecuredAnnotationRuleIntegrationTest.sql"); + User testUser = userDAO.getUserByOrcId(TestTokenValidator.TEST_USER_ORCID).get(); + dsl.execute(securityFp.get("InsertSystemRoleAdmin"), testUser.getId().toString()); + + SpeciesRequest speciesRequest = SpeciesRequest.builder() + .commonName(validSpecies.getCommonName()) + .id(validSpecies.getId()) + .build(); + + ProgramRequest program = ProgramRequest.builder() + .name("Test Program") + .species(speciesRequest) + .key("TEST") + .build(); + + // create test program + validProgram = insertAndFetchTestProgram(gson, biClient, program); + // updated with brapi db id + validProgram = programService.getById(validProgram.getId()).get(); + + testTreatment = new BrAPIObservationTreatment(); + testTreatment.setFactor("ou1 treatment"); + } + + @Test + @SneakyThrows + @Order(1) + public void testCreateObservationUnitAdditionalInfoSingleTreatmentFactor() { + // create observation unit with treatments only in additional info to simulate breedbase not populating + // treatments field + BrAPIObservationUnit ou1 = new BrAPIObservationUnit(); + ou1.setObservationUnitName("test1"); + ou1.putAdditionalInfoItem(BrAPIAdditionalInfoFields.TREATMENTS, List.of(testTreatment)); + ou1.setProgramDbId(validProgram.getBrapiProgram().getProgramDbId()); + + List ous = List.of(ou1); + List createdOus = obsUnitDAO.createBrAPIObservationUnits(ous, validProgram.getId(), upload); + singleTreatmentAsserts(createdOus, testTreatment); + } + + @Test + @SneakyThrows + @Order(2) + public void testGetObservationUnitAdditionalInfoSingleTreatmentFactor() { + List createdOus = obsUnitDAO.getObservationUnitByName(List.of("test1"), validProgram); + singleTreatmentAsserts(createdOus, testTreatment); + } + + private void singleTreatmentAsserts(List obsUnits, BrAPIObservationTreatment expectedTreatment) { + assertEquals(1, obsUnits.size(), "Expected 1 observation unit"); + + BrAPIObservationUnit ou = obsUnits.get(0); + List treatments = ou.getTreatments(); + assertEquals(1, treatments.size(), "Expected treatments property"); + + BrAPIObservationTreatment treatment = treatments.get(0); + assertEquals(expectedTreatment, treatment, "Expected treatments to be same"); + } + +}