Skip to content

[BI-1465] 6.1 Observation Dataset Export File#259

Merged
dmeidlin merged 27 commits intorelease/0.8from
feature/BI-1465
Jun 8, 2023
Merged

[BI-1465] 6.1 Observation Dataset Export File#259
dmeidlin merged 27 commits intorelease/0.8from
feature/BI-1465

Conversation

@dmeidlin
Copy link
Contributor

@dmeidlin dmeidlin commented Jun 6, 2023

Description

Story: BI-1465

  • ExperimentController#datasetExport was created to add the GET endpoint /${micronaut.bi.api.version}/programs/{programId}/experiments/{experimentId}/export?{params}
  • BrAPITrialService#exportObservations and various helper functions in the same service were created to map existing observation data (if any) for the requested experiment and environments to table rows matching the structure of the experiment import template.
  • ExperimentControllerIntegrationTest class was created to test the various combinations of query param values to the export endpoint.

Dependencies

bi-web feature/BI-1465

Testing

  1. go to the Experiments and observations table
  2. click Download for any experiment
  3. select different file formats and the timestamp checkbox
  4. verify fidelity of downloaded file to request inputs and import data for that experiment

Checklist:

  • I have performed a self-review of my own code
  • I have tested my code and ensured it meets the acceptance criteria of the story
  • I have tested that my code works with both the brapi-java-server and BreedBase
  • I have create/modified unit tests to cover this change
  • I have commented my code, particularly in hard-to-understand areas
  • I have made corresponding changes to documentation
  • I have run TAF: <please include a link to TAF run>

@dmeidlin dmeidlin requested review from a team, davedrp and mlm483 and removed request for a team June 6, 2023 17:37
Comment on lines +40 to +41
@Property(name = "brapi.server.reference-source")
private String referenceSource;
Copy link
Member

Choose a reason for hiding this comment

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

For testability, this should be included as a parameter to the constructor. See BrAPIObservationUnitDAO as an example

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done

List<BrAPIObservationVariable> obsVars = new ArrayList<>();
Map<String, Map<String, Object>> rowByOUId = new HashMap<>();
Map<String, BrAPIStudy> studyByDbId = new HashMap<>();
List<String> requestedEnvNames = StringUtils.isNotBlank(params.getEnvironments()) ?
Copy link
Member

Choose a reason for hiding this comment

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

It may be better for this code to assume that environments is a list of environment UUIDs instead of names. That can help the code further down in this method to be more succinct

Copy link
Contributor Author

Choose a reason for hiding this comment

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

agreed, and made the change

} catch (ApiException err) {
log.error("Error fetching observation units for a study by its DbId" +
Utilities.generateApiExceptionLogMessage(err), err);
err.printStackTrace();
Copy link
Member

Choose a reason for hiding this comment

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

This isn't needed as you've already passed the exception object to the log.error method (and that method will print the stack trace)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done

Comment on lines +190 to +193
BrAPIObservationUnit ou = ous.stream()
.filter(unit -> obs.getObservationUnitDbId().equals(unit.getObservationUnitDbId()))
.findAny()
.orElseThrow(RuntimeException::new);
Copy link
Member

Choose a reason for hiding this comment

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

I think it would be worth creating a map outside of the for loop of ouByOUId to reduce the runtime complexity

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done

Comment on lines +224 to +226
BrAPIObservationVariable var = obsVars.stream()
.filter(obsVar -> obs.getObservationVariableName().equals(obsVar.getObservationVariableName()))
.collect(Collectors.toList()).get(0);
Copy link
Member

Choose a reason for hiding this comment

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

It would also be worth changing this to make a map of variableByDbId, and have that map be created and held in addBrAPIObsToRecords. This will also reduce the runtime complexity. This method could then be updated to accept a single BrAPIObservationVariable instead of a list of BrAPIObservationVariables

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done


public BrAPITrial getExperiment(Program program, UUID experimentId) throws ApiException {
List<BrAPITrial> experiments = trialDAO.getTrialsByExperimentIds(List.of(experimentId), program);
return experiments.get(0);
Copy link
Member

Choose a reason for hiding this comment

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

May be worth having a check here to ensure the list returned has at least one element. If it doesn't, throw an error. This will prevent ArrayIndexOutOfBounds errors from occurring

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done

Column obsVarColumn = new Column();
obsVarColumn.setDataType(Column.ColumnDataType.STRING);
if (var.getScale().getDataType().equals(BrAPITraitDataType.ORDINAL)) {
obsVarColumn.setDataType(Column.ColumnDataType.INTEGER);
Copy link
Member

Choose a reason for hiding this comment

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

I think this has to be a string for ordinal. At least on the DeltaBreed UI, we don't enforce that the value of the ordinal be numeric.
Screenshot 2023-06-06 at 3 26 09 PM

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done

if (fileType.equals(FileType.CSV)){
downloadFile = CSVWriter.writeToDownload(columns, exportRows, fileType);
} else {
downloadFile = ExcelWriter.writeToDownload("Dataset Export", columns, exportRows, fileType);
Copy link
Member

Choose a reason for hiding this comment

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

I think the sheetName should be "Experiment Data" to match the experiment import template

Copy link
Contributor Author

Choose a reason for hiding this comment

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

made the change

// For backward compatibility
private static final String OLD_GERMPLASM_EXCEL_DATA_SHEET_NAME = "Germplasm Import";
private static final String OLD_EXPERIMENT_EXCEL_DATA_SHEET_NAME = "Experiment Data";
private static final String DATASET_EXPORT_EXCEL_DATA_SHEET_NAME = "Dataset Export";
Copy link
Member

Choose a reason for hiding this comment

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

This isn't needed if the sheet name on export is "Experiment Data"

Copy link
Contributor Author

Choose a reason for hiding this comment

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

deleted

//For backward compatibility allow old sheet names
if( sheet == null){ sheet = workbook.getSheet(OLD_GERMPLASM_EXCEL_DATA_SHEET_NAME); }
if( sheet == null){ sheet = workbook.getSheet(OLD_EXPERIMENT_EXCEL_DATA_SHEET_NAME); }
if( sheet == null){ sheet = workbook.getSheet(DATASET_EXPORT_EXCEL_DATA_SHEET_NAME); }
Copy link
Member

Choose a reason for hiding this comment

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

This isn't needed if the sheet name on export is "Experiment Data"

Copy link
Contributor Author

Choose a reason for hiding this comment

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

deleted

Copy link
Contributor

@mlm483 mlm483 left a comment

Choose a reason for hiding this comment

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

This is a big feature, overall looks good! I found one bug, and I requested a few changes, mostly cleanup and documentation.

  • ExperimentControllerIntegrationTest looks good. I don't see any of the test files in src/test/resources/files/experiment_controller/ being used, you may want to remove them.
  • There seems to be an issue casting timestamps to strings when generating Excel files. BrAPI Java TestServer was backing service. To reproduce:
    1. Import Germplasm: RILS and Parents.xls
    2. Import Ontology: bi_lettuce_ontology_v1.xlsx
    3. Import Experiment: SCY RIL exp2 TRUNC.xls
    4. Download KRSP22-2 dataset With Timestamps, select .xlsx file type.
    5. Error.
    • in browser: An error occurred while generating the download file. Contact the development team at bidevteam@cornell.edu. (Good actionable message! 👍)
    • bi-api stacktrace: o.b.brapi.v2.ExperimentController - class java.time.OffsetDateTime cannot be cast to class java.lang.String (java.time.OffsetDateTime and java.lang.String are in module java.base of loader 'bootstrap') java.lang.ClassCastException: class java.time.OffsetDateTime cannot be cast to class java.lang.String (java.time.OffsetDateTime and java.lang.String are in module java.base of loader 'bootstrap') at org.breedinginsight.services.writers.ExcelWriter.writeToWorkbook(ExcelWriter.java:58) at org.breedinginsight.services.writers.ExcelWriter.writeToDownload(ExcelWriter.java:77) at org.breedinginsight.brapi.v2.services.BrAPITrialService.exportObservations(BrAPITrialService.java:172) at org.breedinginsight.brapi.v2.ExperimentController.datasetExport(ExperimentController.java:101)

@davedrp
Copy link
Contributor

davedrp commented Jun 6, 2023

Testing Results:
While testing, I found an inconsistency. The "import" of Experiments & Observations allows alphanumeric values for 'row' and 'column' ( for example; row='A'). But the download will error-out unless theses values are numeric.

@dmeidlin
Copy link
Contributor Author

dmeidlin commented Jun 7, 2023

@davedrp Row and Column types were changed from double to string, and the download works now.

@dmeidlin
Copy link
Contributor Author

dmeidlin commented Jun 8, 2023

@mlm483 Observation timestamps are being parsed to strings now, and the export works.

Copy link
Member

@timparsons timparsons left a comment

Choose a reason for hiding this comment

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

Just a few small changes

try {
for (BrAPIStudy study: expStudies) {
ous.addAll(ouDAO.getObservationUnitsForStudyDbId(study.getStudyDbId(), program));
ous.forEach(ou -> ouByOUDbId.put(ou.getObservationUnitDbId(), ou));
Copy link
Member

Choose a reason for hiding this comment

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

I think this will end up looping over all OUs for X number of studies. So during the first iteration, just the OUs for the first study will be added to the map. In the second iteration, all the OUs from the first study and the second study will be added to the map (although the OUs from the first study will just overwrite the existing records in the map), and so on until all studies have been iterated over.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good catch. I changed the forEach to loop over the current study ous instead of all ous.

return response;
} catch (Exception e) {
log.info(e.getMessage(), e);
e.printStackTrace();
Copy link
Member

Choose a reason for hiding this comment

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

This can be removed as the log.info will print the stack trace

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done

Comment on lines +250 to +251
if (var.getScale().getDataType().equals(BrAPITraitDataType.ORDINAL)) {
row.put(varName, Integer.parseInt(obs.getValue()));
Copy link
Member

Choose a reason for hiding this comment

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

I think this has to be set as a String type for the same reason specified on this comment

Copy link
Contributor Author

Choose a reason for hiding this comment

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

yep, done.

@dmeidlin dmeidlin requested review from mlm483 and timparsons June 8, 2023 14:52
Copy link
Contributor

@mlm483 mlm483 left a comment

Choose a reason for hiding this comment

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

Looks good. Approved, I would just recommend explaining the call to addBrAPIObsToRecords on line 161 of BrAPITrialService.java, see comment.

Comment on lines +161 to +170
addBrAPIObsToRecords(
dataset,
experiment,
program,
ouByOUDbId,
studyByDbId,
rowByOUId,
params.isIncludeTimestamps(),
obsVars
);
Copy link
Contributor

Choose a reason for hiding this comment

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

Again, because there are so many parameters and it's not immediately obvious which is being mutated, I would add a comment above this method call.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

np, added a comment that rowByOUId is being updated.

@davedrp
Copy link
Contributor

davedrp commented Jun 8, 2023

Dev-Test Passed

@dmeidlin dmeidlin merged commit b5fc289 into release/0.8 Jun 8, 2023
@dmeidlin dmeidlin deleted the feature/BI-1465 branch June 8, 2023 15:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants