diff --git a/projectmanagement/src/main/java/life/qbic/projectmanagement/domain/project/experiment/BiologicalReplicate.java b/projectmanagement/src/main/java/life/qbic/projectmanagement/domain/project/experiment/BiologicalReplicate.java index 184310a1b..e72e7a85e 100644 --- a/projectmanagement/src/main/java/life/qbic/projectmanagement/domain/project/experiment/BiologicalReplicate.java +++ b/projectmanagement/src/main/java/life/qbic/projectmanagement/domain/project/experiment/BiologicalReplicate.java @@ -5,6 +5,7 @@ import jakarta.persistence.Entity; import java.io.Serial; import java.io.Serializable; +import java.util.Comparator; import java.util.Objects; /** @@ -102,4 +103,22 @@ public boolean equals(Object o) { public String toString() { return "BiologicalReplicate{" + "id=" + id + ", label='" + label + '\'' + '}'; } + + /** + * Provides sorting functionality for labels ending in numbers, e.g. label1 < label2 < label10. + * This is based on label length and only works for labels starting with the same letters. + */ + public static class LexicographicLabelComparator implements Comparator { + + @Override + public int compare(BiologicalReplicate r1, BiologicalReplicate r2) { + int l1 = r1.label.length(); + int l2 = r2.label.length(); + if (l1 == l2) { + return r1.label.compareTo(r2.label); + } else { + return l1-l2; + } + } + } } diff --git a/vaadinfrontend/src/main/java/life/qbic/datamanager/views/projects/project/samples/SampleDetailsComponent.java b/vaadinfrontend/src/main/java/life/qbic/datamanager/views/projects/project/samples/SampleDetailsComponent.java index b9434fd61..58bd6b7b0 100644 --- a/vaadinfrontend/src/main/java/life/qbic/datamanager/views/projects/project/samples/SampleDetailsComponent.java +++ b/vaadinfrontend/src/main/java/life/qbic/datamanager/views/projects/project/samples/SampleDetailsComponent.java @@ -85,6 +85,8 @@ public class SampleDetailsComponent extends PageArea implements Serializable { public final Button registerButton = new Button("Register"); private final Button metadataDownloadButton = new Button("Download Metadata"); private final TabSheet sampleExperimentTabSheet = new TabSheet(); + + //This should be rebuilt from scratch to avoid reset issues if it was opened before private final BatchRegistrationDialog batchRegistrationDialog = new BatchRegistrationDialog(); private static final Logger log = getLogger(SampleDetailsComponent.class); private final transient SampleDetailsComponentHandler sampleDetailsComponentHandler; diff --git a/vaadinfrontend/src/main/java/life/qbic/datamanager/views/projects/project/samples/registration/batch/BatchInformationLayout.java b/vaadinfrontend/src/main/java/life/qbic/datamanager/views/projects/project/samples/registration/batch/BatchInformationLayout.java index 737e08424..7cd2b10bf 100644 --- a/vaadinfrontend/src/main/java/life/qbic/datamanager/views/projects/project/samples/registration/batch/BatchInformationLayout.java +++ b/vaadinfrontend/src/main/java/life/qbic/datamanager/views/projects/project/samples/registration/batch/BatchInformationLayout.java @@ -2,6 +2,7 @@ import com.vaadin.flow.component.button.Button; import com.vaadin.flow.component.button.ButtonVariant; +import com.vaadin.flow.component.checkbox.Checkbox; import com.vaadin.flow.component.html.Div; import com.vaadin.flow.component.html.Span; import com.vaadin.flow.component.icon.Icon; @@ -30,6 +31,7 @@ public class BatchInformationLayout extends Div { public final TextField batchNameField = new TextField("Batch Name"); public final RadioButtonGroup dataTypeSelection = new RadioButtonGroup<>(); + public final Checkbox prefillSelection = new Checkbox("Prefill complete sample batch"); public final Button cancelButton = new Button("Cancel"); public final Button nextButton = new Button("Next"); public final Select experimentSelect = new Select<>(); @@ -62,7 +64,7 @@ private void initBatchLayout() { private void initDataTypeLayout() { Div dataTypeLayout = new Div(); - dataTypeLayout.addClassName("data-type-information"); + dataTypeLayout.addClassName("prefill-information"); Span dataTypeHeader = new Span("Type of Data"); dataTypeHeader.addClassName("title"); dataTypeLayout.add(dataTypeHeader); @@ -72,6 +74,12 @@ private void initDataTypeLayout() { dataTypeLayout.add(dataTypeDescription); initDataTypeSelection(); dataTypeLayout.add(dataTypeSelection); + + Div prefillDescription = new Div(); + prefillDescription.add( + "If you want to register a complete batch (all possible permutations of conditions and replicates), some information can be prefilled."); + dataTypeLayout.add(prefillDescription); + dataTypeLayout.add(prefillSelection); add(dataTypeLayout); } @@ -139,6 +147,7 @@ private void reset() { private void resetChildValues() { dataTypeSelection.setValue(MetadataType.TRANSCRIPTOMICS_GENOMICS); + prefillSelection.setValue(false); experimentSelect.clear(); batchNameField.clear(); } diff --git a/vaadinfrontend/src/main/java/life/qbic/datamanager/views/projects/project/samples/registration/batch/BatchRegistrationDialog.java b/vaadinfrontend/src/main/java/life/qbic/datamanager/views/projects/project/samples/registration/batch/BatchRegistrationDialog.java index 97d4ab542..52755532f 100644 --- a/vaadinfrontend/src/main/java/life/qbic/datamanager/views/projects/project/samples/registration/batch/BatchRegistrationDialog.java +++ b/vaadinfrontend/src/main/java/life/qbic/datamanager/views/projects/project/samples/registration/batch/BatchRegistrationDialog.java @@ -139,14 +139,22 @@ private void setTabSelectionListener() { } private void generateSampleRegistrationLayout() { - //We only reload the spreadsheet if the user selects an experiment and switches back in the tabSheet to select a different one + //we always set the batch name, as it can change without affecting the rest of the spreadsheet + sampleSpreadsheetLayout.setBatchName(batchInformationLayout.batchNameField.getValue()); + //we need to reset the layout, if the experiment has changed if (hasExperimentInformationChanged()) { sampleSpreadsheetLayout.resetLayout(); } - sampleSpreadsheetLayout.setBatchName(batchInformationLayout.batchNameField.getValue()); - sampleSpreadsheetLayout.setExperiment(batchInformationLayout.experimentSelect.getValue()); - sampleSpreadsheetLayout.generateSampleRegistrationSheet( - batchInformationLayout.dataTypeSelection.getValue()); + //We need to build the spreadsheet upon initialization or if the user changed the experiment + if (sampleSpreadsheetLayout.getExperiment() == null || hasExperimentInformationChanged()) { + sampleSpreadsheetLayout.setExperiment(batchInformationLayout.experimentSelect.getValue()); + sampleSpreadsheetLayout.generateSampleRegistrationSheet( + batchInformationLayout.dataTypeSelection.getValue()); + } + //With the spreadsheet prepared, we can fill the prefilled information in the spreadsheet + sampleSpreadsheetLayout.prefillConditionsAndReplicates( + batchInformationLayout.prefillSelection.getValue()); + sampleSpreadsheetLayout.reloadSpreadsheet(); } private boolean hasExperimentInformationChanged() { @@ -200,6 +208,7 @@ public void resetAndClose() { } private void reset() { + batchInformationLayout.reset(); sampleSpreadsheetLayout.resetLayout(); tabStepper.setSelectedTab(batchInformationTab); diff --git a/vaadinfrontend/src/main/java/life/qbic/datamanager/views/projects/project/samples/registration/batch/SampleRegistrationSpreadsheet.java b/vaadinfrontend/src/main/java/life/qbic/datamanager/views/projects/project/samples/registration/batch/SampleRegistrationSpreadsheet.java index 730877e97..4f975d579 100644 --- a/vaadinfrontend/src/main/java/life/qbic/datamanager/views/projects/project/samples/registration/batch/SampleRegistrationSpreadsheet.java +++ b/vaadinfrontend/src/main/java/life/qbic/datamanager/views/projects/project/samples/registration/batch/SampleRegistrationSpreadsheet.java @@ -22,6 +22,7 @@ import java.util.stream.StreamSupport; import life.qbic.application.commons.Result; import life.qbic.projectmanagement.domain.project.experiment.BiologicalReplicate; +import life.qbic.projectmanagement.domain.project.experiment.BiologicalReplicate.LexicographicLabelComparator; import life.qbic.projectmanagement.domain.project.experiment.BiologicalReplicateId; import life.qbic.projectmanagement.domain.project.experiment.Condition; import life.qbic.projectmanagement.domain.project.experiment.Experiment; @@ -99,8 +100,10 @@ public void addSheetToSpreadsheet(MetadataType metaDataType) { setSpreadsheetComponentFactory(dropdownCellFactory); //initialise first rows based on known sample size addRowsForInitialSamples(numberOfSamples); + } + + public void reloadSpreadsheet() { refreshAllCellValues(); - //Only reloads based on first row and first column with index = 1, meaning row and column style has to be refreshed manually reloadVisibleCellContents(); } @@ -477,7 +480,10 @@ public Result areInputsValid() { if (SpreadsheetMethods.cellToStringOrNull(cell).isBlank()) { invalidCells.add(cell); } else { - validCells.add(cell); + // if a background color was set, but the cell is valid, we need to change the style + if(cell.getCellStyle().getFillBackgroundColorColor()!=null) { + validCells.add(cell); + } } } } @@ -521,7 +527,7 @@ private void highlightInvalidCells(Collection invalidCells) { cell.setCellStyle(invalidStyle); } //We need to refresh the cells so the style change takes effect. - this.refreshCells(invalidCells); + refreshCells(invalidCells); } /** @@ -588,6 +594,36 @@ private BiologicalReplicateId retrieveBiologicalReplicateId(String replicateLabe return biologicalReplicateId; } + public void prefillConditionsAndReplicates(boolean isPrefilled) { + int conditionColIndex = header.indexOf(SamplesheetHeaderName.CONDITION); + int replicateColIndex = header.indexOf(SamplesheetHeaderName.BIOLOGICAL_REPLICATE_ID); + int rowIndex = 0; + Set conditions = conditionsToReplicates.keySet(); + for(String condition : conditions) { + List sortedLabels = conditionsToReplicates.get(condition).stream() + .sorted(new LexicographicLabelComparator()).map(BiologicalReplicate::label).toList(); + for (String label : sortedLabels) { + rowIndex++; + Cell replicateCell = this.getCell(rowIndex, replicateColIndex); + Cell conditionCell = this.getCell(rowIndex, conditionColIndex); + if (isPrefilled) { + //prefill cells + replicateCell.setCellValue(label); + conditionCell.setCellValue(condition); + } else { + //remove prefilled info, except if there is only one condition + replicateCell.setCellValue(""); + String neutralValue = ""; + if (conditions.size() == 1) { + neutralValue = condition; + } + conditionCell.setCellValue(neutralValue); + replicateCell.setCellValue(""); + } + } + } + } + /** * Record containing the provided mandatory specific information for the genomic * {@link MetadataType} sheet diff --git a/vaadinfrontend/src/main/java/life/qbic/datamanager/views/projects/project/samples/registration/batch/SampleSpreadsheetLayout.java b/vaadinfrontend/src/main/java/life/qbic/datamanager/views/projects/project/samples/registration/batch/SampleSpreadsheetLayout.java index 7addacb74..fcdf3b17a 100644 --- a/vaadinfrontend/src/main/java/life/qbic/datamanager/views/projects/project/samples/registration/batch/SampleSpreadsheetLayout.java +++ b/vaadinfrontend/src/main/java/life/qbic/datamanager/views/projects/project/samples/registration/batch/SampleSpreadsheetLayout.java @@ -112,7 +112,13 @@ public void generateSampleRegistrationSheet(MetadataType metaDataType) { } public void resetLayout() { + sampleRegistrationSpreadsheet.getCellSelectionManager().clear(); sampleInformationLayoutHandler.reset(); + experiment = null; + } + + public void reloadSpreadsheet() { + sampleRegistrationSpreadsheet.reloadSpreadsheet(); } public void setBatchName(String text) { @@ -137,6 +143,10 @@ public ExperimentId getExperiment() { return experiment; } + public void prefillConditionsAndReplicates(boolean isPrefilled) { + sampleRegistrationSpreadsheet.prefillConditionsAndReplicates(isPrefilled); + } + private class SampleInformationLayoutHandler implements Serializable { @Serial @@ -149,6 +159,7 @@ private void reset() { private void resetChildValues() { resetInstructions(); resetSpreadSheet(); + hideErrorInstructions(); } private void resetInstructions() {