diff --git a/pom.xml b/pom.xml
index 95d75a0d1..674e7ffea 100644
--- a/pom.xml
+++ b/pom.xml
@@ -89,7 +89,7 @@
31.0.1-jre
4.9.3
4.3.1
- 2.1-SNAPSHOT
+ 2.2-SNAPSHOT
2.11.0
2.2.1
diff --git a/src/main/java/org/breedinginsight/brapi/v2/dao/BrAPIObservationLevelDAO.java b/src/main/java/org/breedinginsight/brapi/v2/dao/BrAPIObservationLevelDAO.java
index e37e23dbe..258ffbaf6 100644
--- a/src/main/java/org/breedinginsight/brapi/v2/dao/BrAPIObservationLevelDAO.java
+++ b/src/main/java/org/breedinginsight/brapi/v2/dao/BrAPIObservationLevelDAO.java
@@ -20,23 +20,38 @@
import com.google.gson.Gson;
import io.micronaut.http.HttpResponse;
import io.micronaut.http.HttpStatus;
+import io.micronaut.http.server.exceptions.InternalServerException;
import lombok.extern.slf4j.Slf4j;
import okhttp3.HttpUrl;
import okhttp3.MediaType;
import okhttp3.Request;
import okhttp3.RequestBody;
+import org.brapi.client.v2.ApiResponse;
import org.brapi.client.v2.JSON;
import org.brapi.client.v2.model.exceptions.ApiException;
+import org.brapi.client.v2.modules.phenotype.ObservationLevelNamesApi;
+import org.brapi.client.v2.modules.phenotype.ObservationUnitsApi;
+import org.brapi.v2.model.pheno.BrAPIObservationUnitHierarchyLevel;
+import org.brapi.v2.model.pheno.BrAPIObservationUnitLevelRelationship;
+import org.brapi.v2.model.pheno.response.BrAPIObservationLevelListResponse;
+import org.brapi.v2.model.pheno.response.BrAPIObservationLevelListResponseResult;
+import org.brapi.v2.model.pheno.response.BrAPIObservationLevelSingleResponse;
+import org.breedinginsight.daos.ProgramDAO;
import org.breedinginsight.model.DatasetLevel;
import org.breedinginsight.model.Program;
+import org.breedinginsight.services.brapi.BrAPIEndpointProvider;
+import org.breedinginsight.services.exceptions.DoesNotExistException;
import org.breedinginsight.utilities.BrAPIDAOUtil;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
+import org.breedinginsight.utilities.Utilities;
import javax.inject.Inject;
import javax.inject.Singleton;
+import java.util.List;
+import java.util.Optional;
@Slf4j
@Singleton
@@ -46,12 +61,19 @@ public class BrAPIObservationLevelDAO {
private final BrAPIDAOUtil brAPIDAOUtil;
private final Gson gson = new JSON().getGson();
+ private final BrAPIEndpointProvider brAPIEndpointProvider;
+ private final ProgramDAO programDAO;
+
@Inject
- public BrAPIObservationLevelDAO(BrAPIDAOUtil brAPIDAOUtil) {
+ public BrAPIObservationLevelDAO(BrAPIDAOUtil brAPIDAOUtil,
+ BrAPIEndpointProvider brAPIEndpointProvider,
+ ProgramDAO programDAO) {
this.brAPIDAOUtil = brAPIDAOUtil;
+ this.brAPIEndpointProvider = brAPIEndpointProvider;
+ this.programDAO = programDAO;
}
- public HttpResponse createObservationLevelName(Program program, String levelName, DatasetLevel levelOrder, String programDbId) throws ApiException {
+ public HttpResponse createObservationLevelName(Program program, String levelName, DatasetLevel levelOrder, String programDbId) {
HttpUrl url = HttpUrl.parse(brAPIDAOUtil.getProgramBrAPIBaseUrl(program.getId()))
.newBuilder()
.addPathSegment("observationlevelnames")
@@ -75,25 +97,90 @@ public HttpResponse createObservationLevelName(Program program, String l
return brAPIDAOUtil.makeCall(request);
}
+ public BrAPIObservationUnitHierarchyLevel createLevelName(Program program,
+ String programDbId,
+ String levelName,
+ DatasetLevel levelOrder) throws ApiException {
+ ObservationLevelNamesApi api = brAPIEndpointProvider.get(programDAO.getCoreClient(program.getId()), ObservationLevelNamesApi.class);
+
+ ApiResponse response;
+
+
+ BrAPIObservationUnitHierarchyLevel level = new BrAPIObservationUnitHierarchyLevel();
+
+ level.setLevelName(levelName.toLowerCase());
+ level.setLevelOrder(levelOrder.getValue());
+ level.setProgramDbId(programDbId);
+
+ try {
+ response = api.observationLevelNamesPost(List.of(level));
+ } catch (ApiException e) {
+ log.warn(Utilities.generateApiExceptionLogMessage(e));
+ throw new InternalServerException("Error making BrAPI call", e);
+ }
+
+ return Optional.of(response)
+ .map(ApiResponse::getBody)
+ .map(BrAPIObservationLevelListResponse::getResult)
+ .map(BrAPIObservationLevelListResponseResult::getData)
+ .flatMap(data -> data.stream().findFirst())
+ .orElseThrow(() -> new ApiException(String.format("BrAPI indicated level name [%s] was created but no levelNameDbId was returned upon its creation", levelName)));
+ }
+
+ public List getObservationLevelNamesByProgramId(Program program, String programDbId) {
+ ObservationLevelNamesApi api = brAPIEndpointProvider.get(programDAO.getCoreClient(program.getId()), ObservationLevelNamesApi.class);
+
+ ApiResponse response;
+
+ int pageSize = 100;
+
+ try {
+ response = api.observationLevelNamesGet(programDbId,
+ false,
+ 0,
+ pageSize);
+ } catch (ApiException e) {
+ log.warn(Utilities.generateApiExceptionLogMessage(e));
+ throw new InternalServerException("Error making BrAPI call", e);
+ }
+
+ if (response.getBody().getMetadata().getPagination().getTotalCount() > 100) {
+ throw new InternalServerException(String.format("More level names exist than requested [%s]", pageSize));
+ }
+
+ return response.getBody().getResult().getData();
+ }
+
+ public List getGlobalObservationLevelNames(Program program) {
+ return getObservationLevelNamesByProgramId(program, null);
+ }
+
public void deleteObservationLevelName(Program program, String levelDbId) {
- HttpUrl url = HttpUrl.parse(brAPIDAOUtil.getProgramBrAPIBaseUrl(program.getId()))
- .newBuilder()
- .addPathSegment("observationlevelnames")
- .addPathSegment(levelDbId)
- .build();
- var request = new Request.Builder()
- .url(url)
- .delete()
- .addHeader("Content-Type", "application/json")
- .build();
+ ObservationLevelNamesApi api = brAPIEndpointProvider.get(programDAO.getCoreClient(program.getId()), ObservationLevelNamesApi.class);
+
try {
- HttpResponse response = brAPIDAOUtil.makeCall(request);
- if (response.getStatus() != HttpStatus.OK && response.getStatus() != HttpStatus.NO_CONTENT && response.getStatus() != HttpStatus.ACCEPTED) {
- log.warn("Observation level delete returned status {} for {}", response.getStatus(), levelDbId);
- }
- } catch (Exception e) {
- log.warn("Failed to delete observation level {}", levelDbId, e);
+ api.observationLevelNameDbIdDelete(levelDbId);
+ } catch (ApiException e) {
+ log.warn(Utilities.generateApiExceptionLogMessage(e));
+ throw new InternalServerException("Error making BrAPI call", e);
}
}
+ public BrAPIObservationUnitHierarchyLevel updateObservationLevelName(Program program,
+ String levelNameDbId,
+ BrAPIObservationUnitHierarchyLevel level) {
+ ObservationLevelNamesApi api = brAPIEndpointProvider.get(programDAO.getCoreClient(program.getId()), ObservationLevelNamesApi.class);
+
+ ApiResponse response;
+
+ try {
+ response = api.observationLevelNameDbIdPut(levelNameDbId, level);
+ } catch (ApiException e) {
+ log.warn(Utilities.generateApiExceptionLogMessage(e));
+ throw new InternalServerException("Error making BrAPI call", e);
+ }
+
+ return response.getBody().getResult();
+ }
+
}
diff --git a/src/main/java/org/breedinginsight/brapi/v2/services/BrAPIObservationLevelService.java b/src/main/java/org/breedinginsight/brapi/v2/services/BrAPIObservationLevelService.java
new file mode 100644
index 000000000..8d48979cb
--- /dev/null
+++ b/src/main/java/org/breedinginsight/brapi/v2/services/BrAPIObservationLevelService.java
@@ -0,0 +1,60 @@
+package org.breedinginsight.brapi.v2.services;
+
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.tuple.ImmutablePair;
+import org.apache.commons.lang3.tuple.Pair;
+import org.brapi.client.v2.ApiResponse;
+import org.brapi.client.v2.model.exceptions.ApiException;
+import org.brapi.client.v2.modules.phenotype.ObservationUnitsApi;
+import org.brapi.v2.model.pheno.BrAPIObservationUnitHierarchyLevel;
+import org.brapi.v2.model.pheno.BrAPIObservationUnitLevelRelationship;
+import org.brapi.v2.model.pheno.response.BrAPIObservationLevelListResponse;
+import org.breedinginsight.brapi.v2.dao.BrAPIObservationLevelDAO;
+import org.breedinginsight.model.DatasetLevel;
+import org.breedinginsight.model.Program;
+import org.breedinginsight.services.ProgramService;
+import org.breedinginsight.services.exceptions.DoesNotExistException;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+@Slf4j
+@Singleton
+public class BrAPIObservationLevelService {
+ private final BrAPIObservationLevelDAO brAPIObservationLevelDAO;
+ private final ProgramService programService;
+
+
+ @Inject
+ public BrAPIObservationLevelService(BrAPIObservationLevelDAO brAPIObservationLevelDAO,
+ ProgramService programService) {
+ this.brAPIObservationLevelDAO = brAPIObservationLevelDAO;
+ this.programService = programService;
+ }
+
+ /**
+ * @return Pair[GlobalLevelNames, ProgrammaticLevelNames]
+ */
+ public Pair, List> getGlobalAndProgrammaticLevelNames(Program program, String brapiProgramDbId) {
+ List globalLevels = brAPIObservationLevelDAO.getGlobalObservationLevelNames(program);
+ List programmaticLevels = brAPIObservationLevelDAO.getObservationLevelNamesByProgramId(program, brapiProgramDbId);
+
+
+ return new ImmutablePair<>(globalLevels, programmaticLevels);
+ }
+
+ public List getProgrammaticLevelNames(Program program, String brapiProgramDbId) {
+ return brAPIObservationLevelDAO.getObservationLevelNamesByProgramId(program, brapiProgramDbId);
+ }
+
+ public BrAPIObservationUnitHierarchyLevel createObservationLevel(Program program,
+ String brapiProgramDbId,
+ String levelName,
+ DatasetLevel levelOrder) throws ApiException {
+ return brAPIObservationLevelDAO.createLevelName(program, brapiProgramDbId, levelName, levelOrder);
+ }
+
+}
diff --git a/src/main/java/org/breedinginsight/brapi/v2/services/BrAPITrialService.java b/src/main/java/org/breedinginsight/brapi/v2/services/BrAPITrialService.java
index 92a7ac2db..d59b0928c 100644
--- a/src/main/java/org/breedinginsight/brapi/v2/services/BrAPITrialService.java
+++ b/src/main/java/org/breedinginsight/brapi/v2/services/BrAPITrialService.java
@@ -6,8 +6,6 @@
import com.github.filosganga.geogson.model.positions.SinglePosition;
import com.google.gson.JsonObject;
import io.micronaut.context.annotation.Property;
-import io.micronaut.http.HttpResponse;
-import io.micronaut.http.HttpStatus;
import io.micronaut.http.MediaType;
import io.micronaut.http.server.exceptions.InternalServerException;
import io.micronaut.http.server.types.files.StreamedFile;
@@ -81,6 +79,7 @@ public class BrAPITrialService {
private final DistributedLockService lockService;
private static final String SHEET_NAME = "Data";
private final DatasetService datasetService;
+ private final BrAPIObservationLevelService observationLevelService;
@Inject
public BrAPITrialService(@Property(name = "brapi.server.reference-source") String referenceSource,
@@ -96,7 +95,8 @@ public BrAPITrialService(@Property(name = "brapi.server.reference-source") Strin
BrAPIGermplasmDAO germplasmDAO,
FileMappingUtil fileMappingUtil,
DistributedLockService lockService,
- DatasetService datasetService) {
+ DatasetService datasetService,
+ BrAPIObservationLevelService observationLevelService) {
this.referenceSource = referenceSource;
this.trialDAO = trialDAO;
@@ -112,6 +112,7 @@ public BrAPITrialService(@Property(name = "brapi.server.reference-source") Strin
this.fileMappingUtil = fileMappingUtil;
this.lockService = lockService;
this.datasetService = datasetService;
+ this.observationLevelService = observationLevelService;
}
public List getExperiments(UUID programId) throws ApiException, DoesNotExistException {
@@ -466,14 +467,13 @@ public Dataset createSubEntityDataset(Program program, UUID experimentId, SubEnt
}
String programBrapiDbId = program.getBrapiProgram() != null ? program.getBrapiProgram().getProgramDbId() : null;
- HttpResponse levelResponse = observationLevelDAO.createObservationLevelName(program, datasetName, DatasetLevel.SUB_OBS_UNIT, programBrapiDbId);
- // 409 and 200 are expected response codes, anything else error out
- // 409 means level already exists so we just use the name in OUs
- // 200 means level was created successfully and can use the name in OUs
- if (levelResponse.getStatus() != HttpStatus.CONFLICT && levelResponse.getStatus() != HttpStatus.OK) {
- throw new ApiException(levelResponse.getStatus().getCode(), "Unable to create observation level: " + levelResponse.getStatus().getReason());
- }
+ String subEntityLevelNameDbId = datasetService.getOrCreateLevelNameForDataset(program,
+ programBrapiDbId,
+ datasetName,
+ DatasetLevel.SUB_OBS_UNIT
+ );
+
List expOUs = ouDAO.getObservationUnitsForDataset(topLevelDataset.getId().toString(), program);
for (BrAPIObservationUnit expUnit : expOUs) {
@@ -484,14 +484,14 @@ public Dataset createSubEntityDataset(Program program, UUID experimentId, SubEnt
for (int i=1; i<=request.getRepeatedMeasures(); i++) {
subObsUnits.add(
createSubObservationUnit(
- datasetName,
Integer.toString(i),
program,
envSeqValue,
expUnit,
this.referenceSource,
subEntityDatasetId,
- UUID.randomUUID()
+ UUID.randomUUID(),
+ subEntityLevelNameDbId
)
);
}
@@ -529,14 +529,14 @@ public Dataset createSubEntityDataset(Program program, UUID experimentId, SubEnt
}
public BrAPIObservationUnit createSubObservationUnit(
- String subEntityDatasetName,
String subUnitId,
Program program,
String seqVal,
BrAPIObservationUnit expUnit,
String referenceSource,
UUID datasetId,
- UUID id
+ UUID id,
+ String subEntityLevelNameDbId
) {
BrAPIObservationUnit observationUnit = new BrAPIObservationUnit();
@@ -612,42 +612,39 @@ public BrAPIObservationUnit createSubObservationUnit(
// ObservationLevel entry for Sub-Obs Unit.
BrAPIObservationUnitLevelRelationship level = new BrAPIObservationUnitLevelRelationship();
- level.setLevelName(subEntityDatasetName);
+ level.setLevelNameDbId(subEntityLevelNameDbId);
level.setLevelCode(Utilities.appendProgramKey(subUnitId, program.getKey(), seqVal));
- level.setLevelOrder(DatasetLevel.SUB_OBS_UNIT.getValue());
position.setObservationLevel(level);
// ObservationLevelRelationships.
List levelRelationships = new ArrayList<>();
+ // TODO: Figure out if we actually need to add the sub entity level to the level relationships BI-2823
levelRelationships.add(level);
- // ObservationLevelRelationships for block.
- BrAPIObservationUnitLevelRelationship expBlockLevel = expUnit.getObservationUnitPosition()
- .getObservationLevelRelationships().stream()
- .filter(x -> x.getLevelName().equals(BrAPIConstants.REPLICATE.getValue())).findFirst().orElse(null);
- if (expBlockLevel != null) {
- BrAPIObservationUnitLevelRelationship blockLevel = new BrAPIObservationUnitLevelRelationship();
- blockLevel.setLevelName(expBlockLevel.getLevelName());
- blockLevel.setLevelCode(expBlockLevel.getLevelCode());
- blockLevel.setLevelOrder(expBlockLevel.getLevelOrder());
- levelRelationships.add(blockLevel);
- }
// ObservationLevelRelationships for rep.
BrAPIObservationUnitLevelRelationship expRepLevel = expUnit.getObservationUnitPosition()
.getObservationLevelRelationships().stream()
- .filter(x -> x.getLevelName().equals(BrAPIConstants.BLOCK.getValue())).findFirst().orElse(null);
+ .filter(x -> x.getLevelName().equals(BrAPIConstants.REPLICATE.getValue())).findFirst().orElse(null);
if (expRepLevel != null) {
BrAPIObservationUnitLevelRelationship repLevel = new BrAPIObservationUnitLevelRelationship();
- repLevel.setLevelName(expRepLevel.getLevelName());
+ repLevel.setLevelNameDbId(expRepLevel.getLevelNameDbId());
repLevel.setLevelCode(expRepLevel.getLevelCode());
- repLevel.setLevelOrder(expRepLevel.getLevelOrder());
levelRelationships.add(repLevel);
}
+ // ObservationLevelRelationships for block.
+ BrAPIObservationUnitLevelRelationship expBlockLevel = expUnit.getObservationUnitPosition()
+ .getObservationLevelRelationships().stream()
+ .filter(x -> x.getLevelName().equals(BrAPIConstants.BLOCK.getValue())).findFirst().orElse(null);
+ if (expBlockLevel != null) {
+ BrAPIObservationUnitLevelRelationship blockLevel = new BrAPIObservationUnitLevelRelationship();
+ blockLevel.setLevelNameDbId(expBlockLevel.getLevelNameDbId());
+ blockLevel.setLevelCode(expBlockLevel.getLevelCode());
+ levelRelationships.add(blockLevel);
+ }
// ObservationLevelRelationships for top-level Exp Unit linking.
BrAPIObservationUnitLevelRelationship expUnitLevel = new BrAPIObservationUnitLevelRelationship();
- expUnitLevel.setLevelName(expUnit.getObservationUnitPosition().getObservationLevel().getLevelName().toLowerCase());
+ expUnitLevel.setLevelNameDbId(expUnit.getObservationUnitPosition().getObservationLevel().getLevelNameDbId());
String expUnitUUID = Utilities.getExternalReference(expUnit.getExternalReferences(), referenceSource, ExternalReferenceSource.OBSERVATION_UNITS).orElseThrow().getReferenceId();
expUnitLevel.setLevelCode(Utilities.appendProgramKey(expUnitUUID, program.getKey(), seqVal));
- expUnitLevel.setLevelOrder(DatasetLevel.EXP_UNIT.getValue());
levelRelationships.add(expUnitLevel);
position.setObservationLevelRelationships(levelRelationships);
diff --git a/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/service/DatasetService.java b/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/service/DatasetService.java
index fbc5186db..18157fd11 100644
--- a/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/service/DatasetService.java
+++ b/src/main/java/org/breedinginsight/brapps/importer/services/processors/experiment/service/DatasetService.java
@@ -18,6 +18,7 @@
package org.breedinginsight.brapps.importer.services.processors.experiment.service;
import io.micronaut.context.annotation.Property;
+import org.apache.commons.lang3.StringUtils;
import org.brapi.client.v2.model.exceptions.ApiException;
import org.brapi.v2.model.BrAPIExternalReference;
import org.brapi.v2.model.core.BrAPIListSummary;
@@ -25,11 +26,15 @@
import org.brapi.v2.model.core.BrAPITrial;
import org.brapi.v2.model.core.request.BrAPIListNewRequest;
import org.brapi.v2.model.core.response.BrAPIListDetails;
+import org.brapi.v2.model.pheno.BrAPIObservationUnitHierarchyLevel;
import org.breedinginsight.brapi.v2.constants.BrAPIAdditionalInfoFields;
import org.breedinginsight.brapi.v2.dao.BrAPIListDAO;
+import org.breedinginsight.brapi.v2.dao.BrAPIObservationLevelDAO;
+import org.breedinginsight.brapi.v2.services.BrAPIObservationLevelService;
import org.breedinginsight.brapps.importer.model.response.ImportObjectState;
import org.breedinginsight.brapps.importer.model.response.PendingImportObject;
import org.breedinginsight.brapps.importer.services.ExternalReferenceSource;
+import org.breedinginsight.model.DatasetLevel;
import org.breedinginsight.model.DatasetMetadata;
import org.breedinginsight.model.Program;
import org.breedinginsight.utilities.Utilities;
@@ -37,16 +42,20 @@
import javax.inject.Inject;
import javax.inject.Singleton;
import java.util.*;
+import java.util.stream.Collectors;
@Singleton
public class DatasetService {
private final BrAPIListDAO brAPIListDAO;
@Property(name = "brapi.server.reference-source")
private String BRAPI_REFERENCE_SOURCE;
+ private final BrAPIObservationLevelService observationLevelService;
@Inject
- public DatasetService(BrAPIListDAO brapiListDAO) {
+ public DatasetService(BrAPIListDAO brapiListDAO,
+ BrAPIObservationLevelService brAPIObservationLevelService) {
this.brAPIListDAO = brapiListDAO;
+ this.observationLevelService = brAPIObservationLevelService;
}
/**
* Module: Dataset Utility
@@ -162,4 +171,49 @@ public BrAPIListDetails constructDatasetDetails(
dataSetDetails.setExternalReferences(refs);
return dataSetDetails;
}
+
+ /**
+ * @return brapiLevelNameDbId of found or created record
+ */
+ public String getOrCreateLevelNameForDataset(Program program,
+ String brapiProgramDbId,
+ String levelName,
+ DatasetLevel levelOrder) throws ApiException {
+
+ String existingLevelNameDbId = findLevelNameByNameAndOrder(program, brapiProgramDbId, levelName, levelOrder);
+
+ if (StringUtils.isNotBlank(existingLevelNameDbId)) {
+ return existingLevelNameDbId;
+ }
+
+ // Level name does not exist and needs to be created.
+ BrAPIObservationUnitHierarchyLevel createdLevelName = observationLevelService.createObservationLevel(program, brapiProgramDbId, levelName, levelOrder);
+
+ return createdLevelName.getLevelNameDbId();
+ }
+
+ /**
+ * This method retrieves the programmatic level names and then matches the level names on the submitted
+ * level name and order.
+ *
+ * @return levelNameDbId of the matched level name
+ */
+ private String findLevelNameByNameAndOrder(Program program,
+ String brapiProgramDbId,
+ String levelName,
+ DatasetLevel levelOrder) {
+ var programmaticLevelNames = observationLevelService.getProgrammaticLevelNames(program, brapiProgramDbId);
+
+ List levelNameStreamResult
+ = programmaticLevelNames.stream()
+ .filter(ouln -> ouln.getLevelName().equals(levelName.toLowerCase()) && ouln.getLevelOrder() == levelOrder.getValue())
+ .limit(1)
+ .collect(Collectors.toList());
+
+ if (levelNameStreamResult.isEmpty()) {
+ return null;
+ }
+
+ return levelNameStreamResult.get(0).getLevelNameDbId();
+ }
}