From 2b9dc75bd4f7de972da73f73039980a6d5a95117 Mon Sep 17 00:00:00 2001 From: Tobias Koch Date: Tue, 10 Sep 2024 11:14:10 +0200 Subject: [PATCH 01/20] WIP --- .../NGSMeasurementContentProvider.java | 147 ++++++++++++++++-- .../NGSMeasurementContentProviderTest.groovy | 30 ++++ 2 files changed, 163 insertions(+), 14 deletions(-) create mode 100644 user-interface/src/test/groovy/life/qbic/datamanager/views/projects/project/measurements/download/NGSMeasurementContentProviderTest.groovy diff --git a/user-interface/src/main/java/life/qbic/datamanager/views/projects/project/measurements/download/NGSMeasurementContentProvider.java b/user-interface/src/main/java/life/qbic/datamanager/views/projects/project/measurements/download/NGSMeasurementContentProvider.java index 05d96a4ae..500770f75 100644 --- a/user-interface/src/main/java/life/qbic/datamanager/views/projects/project/measurements/download/NGSMeasurementContentProvider.java +++ b/user-interface/src/main/java/life/qbic/datamanager/views/projects/project/measurements/download/NGSMeasurementContentProvider.java @@ -4,20 +4,26 @@ import java.io.ByteArrayOutputStream; import java.io.IOException; +import java.util.Collections; import java.util.LinkedList; import java.util.List; +import java.util.Optional; import life.qbic.application.commons.ApplicationException; import life.qbic.application.commons.ApplicationException.ErrorCode; import life.qbic.datamanager.views.general.download.DownloadContentProvider; import life.qbic.datamanager.views.projects.project.measurements.NGSMeasurementEntry; import life.qbic.logging.api.Logger; +import life.qbic.projectmanagement.application.measurement.NGSMeasurementMetadata; +import life.qbic.projectmanagement.domain.model.measurement.NGSMeasurement; import org.apache.poi.ss.usermodel.Cell; import org.apache.poi.ss.usermodel.CellStyle; import org.apache.poi.ss.usermodel.FillPatternType; import org.apache.poi.ss.usermodel.Font; +import org.apache.poi.ss.usermodel.Name; import org.apache.poi.ss.usermodel.Row; import org.apache.poi.ss.usermodel.Sheet; import org.apache.poi.ss.usermodel.Workbook; +import org.apache.poi.ss.util.CellReference; import org.apache.poi.xssf.usermodel.DefaultIndexedColorMap; import org.apache.poi.xssf.usermodel.XSSFColor; import org.apache.poi.xssf.usermodel.XSSFFont; @@ -26,8 +32,8 @@ /** NGS Measurement Content Provider *

* Implementation of the {@link DownloadContentProvider} providing the content and file name for any files created - * from {@link life.qbic.projectmanagement.domain.model.measurement.NGSMeasurement} - * and {@link life.qbic.projectmanagement.application.measurement.NGSMeasurementMetadata} + * from {@link NGSMeasurement} + * and {@link NGSMeasurementMetadata} *

*/ public class NGSMeasurementContentProvider implements DownloadContentProvider { @@ -42,6 +48,7 @@ public class NGSMeasurementContentProvider implements DownloadContentProvider { private final List measurements = new LinkedList<>(); private static final String DEFAULT_FILE_NAME_PREFIX = "QBiC"; private String fileNamePrefix = DEFAULT_FILE_NAME_PREFIX ; + private static final int DEFAULT_GENERATED_ROW_COUNT = 2_000; private static void setAutoWidth(Sheet sheet) { for (int col = 0; col <= NGSMeasurementColumns.values().length; col++) { @@ -171,6 +178,61 @@ private void defineReadOnlyHeaderStyle(Workbook workbook) { readOnlyHeaderStyle.setFont(fontHeader); } + /** + * Adds values to the sheet and returns the named area where they were added. The named area can + * later be used by calling {} + * + * @param name the name to choose for the area + * @param sheet the sheet where to add the values + * @param propertyValues the property values to add + * @return a defined name for a range of cells in the workbook. + */ + protected static Name addValueListWithName(String name, Sheet sheet, + PropertyValues propertyValues) { + Row headerRow = getRowNeverNull(sheet, 0); + var columnNumber = Math.max(1, + headerRow.getLastCellNum()); // we want to obtain 1 for the first to come if there are none and not -1 -.- + var columnIndex = columnNumber - 1; + + // create header cell + Cell headerRowCell = headerRow.createCell(columnIndex); + headerRowCell.setCellValue(propertyValues.propertyName()); + + for (int i = 0; i < propertyValues.size(); i++) { + var rowIndex = i + 1; // +1 because of header row + Row valueRow = getRowNeverNull(sheet, rowIndex); + valueRow.createCell(columnIndex).setCellValue(propertyValues.get(i)); + } + var reference = "'%s'!$%s$%s:$%s$%s".formatted( //e.g. 'My Sheet'!$A$1:$E$23 + sheet.getSheetName(), + CellReference.convertNumToColString(columnIndex), + 1, + CellReference.convertNumToColString(columnIndex), + propertyValues.size() + 1 + ); + var namedArea = sheet.getWorkbook().createName(); + namedArea.setNameName(name); + namedArea.setRefersToFormula(reference); + return namedArea; + } + + protected record PropertyValues(String propertyName, List values) { + + public int size() { + return values.size(); + } + + public String get(int index) { + return values.get(index); + } + + public PropertyValues(String propertyName, List values) { + this.propertyName = propertyName; + this.values = Collections.unmodifiableList(values); + } + } + + @Override public byte[] getContent() { if (measurements.isEmpty()) { @@ -180,23 +242,66 @@ public byte[] getContent() { ByteArrayOutputStream byteArrayOutputStream; try (Workbook workbook = new XSSFWorkbook()) { - Sheet sheet = workbook.createSheet("NGS Measurement Metadata"); - Row header = sheet.createRow(0); - defineReadOnlyHeaderStyle(workbook); - defineReadOnlyCellStyle(workbook); - defineBoldStyle(workbook); - formatHeader(header); + Sheet hiddenSheet = workbook.createSheet("hidden"); + //TODO hide sheet + Row hiddenSheetRow = hiddenSheet.createRow(0); + //sequencing read type + Cell hiddenSequencingReadTypeHeaderCell = hiddenSheetRow.createCell(0); + hiddenSequencingReadTypeHeaderCell.setCellValue("Sequencing read type"); + + var values = List.of("test", "test2", "test 42"); + var maxRowNumber = values.size() + 1; // already 1 based but we need to add the header row + var sequencingReadTypeColumnIndex = hiddenSequencingReadTypeHeaderCell.getColumnIndex(); + //insert values into spreadsheet + for (int i = 0; i < values.size(); i++) { + var rowIndex = i + 1; // we start with offset one because of the header row + Row row = getRowNeverNull(hiddenSheet, rowIndex); + row.createCell(sequencingReadTypeColumnIndex).setCellValue(values.get(i)); + } - int rowCounter = 1; + // row numbers start with 1 e.g. A1, A2, ... ; indexes start with 0 e.g. [0,0], [0,1], ... + String startColumnLetters = CellReference.convertNumToColString( + sequencingReadTypeColumnIndex); //takes an index not a number + int startRowNumber = + hiddenSequencingReadTypeHeaderCell.getRowIndex() + 1; // we need to add the header + String stopColumnLetters = CellReference.convertNumToColString( + sequencingReadTypeColumnIndex); //takes an index not a number + int stopRowNumber = maxRowNumber; + String reference = "'%s'!$%s$%s:$%s$%s".formatted( //e.g. 'My Sheet'!$A$1:$E$23 + hiddenSheet.getSheetName(), + startColumnLetters, + startRowNumber, + stopColumnLetters, + stopRowNumber + ); + Name sequencingReadTypeValues = workbook.createName(); + sequencingReadTypeValues.setRefersToFormula(reference); + sequencingReadTypeValues.setNameName("sequencingReadTypes"); - for (NGSMeasurementEntry ngsMeasurementEntry : measurements) { - Row entry = sheet.createRow(rowCounter); - createMeasurementEntry(ngsMeasurementEntry, entry); - rowCounter++; + Sheet sheet = workbook.createSheet("NGS Measurement Metadata"); + workbook.setSheetOrder(sheet.getSheetName(), 0); + //TODO create header row + Row headerRow = sheet.createRow(0); + for (int i = 1; i < DEFAULT_GENERATED_ROW_COUNT; i++) { + Row row = sheet.createRow(i); } - setAutoWidth(sheet); +// Row header = sheet.createRow(0); +// defineReadOnlyHeaderStyle(workbook); +// defineReadOnlyCellStyle(workbook); +// defineBoldStyle(workbook); +// formatHeader(header); +// +// int rowCounter = 1; +// +// for (NGSMeasurementEntry ngsMeasurementEntry : measurements) { +// Row entry = sheet.createRow(rowCounter); +// createMeasurementEntry(ngsMeasurementEntry, entry); +// rowCounter++; +// } +// +// setAutoWidth(sheet); byteArrayOutputStream = new ByteArrayOutputStream(); workbook.write(byteArrayOutputStream); @@ -208,6 +313,20 @@ public byte[] getContent() { return byteArrayOutputStream.toByteArray(); } + private static String getCellValue(Cell cell) { + return switch (cell.getCellType()) { + case FORMULA, _NONE, BLANK, ERROR -> ""; + case STRING -> cell.getStringCellValue(); + case BOOLEAN -> Boolean.toString(cell.getBooleanCellValue()); + case NUMERIC -> Double.toString(cell.getNumericCellValue()); + }; + } + + private static Row getRowNeverNull(Sheet sheet, int index) { + return Optional.ofNullable(sheet.getRow(index)) + .orElse(sheet.createRow(index)); + } + @Override public String getFileName() { return String.join("_" , fileNamePrefix, FILE_NAME_SUFFIX); diff --git a/user-interface/src/test/groovy/life/qbic/datamanager/views/projects/project/measurements/download/NGSMeasurementContentProviderTest.groovy b/user-interface/src/test/groovy/life/qbic/datamanager/views/projects/project/measurements/download/NGSMeasurementContentProviderTest.groovy new file mode 100644 index 000000000..efbfe33cc --- /dev/null +++ b/user-interface/src/test/groovy/life/qbic/datamanager/views/projects/project/measurements/download/NGSMeasurementContentProviderTest.groovy @@ -0,0 +1,30 @@ +package life.qbic.datamanager.views.projects.project.measurements.download + +import org.apache.poi.ss.usermodel.Workbook +import org.apache.poi.ss.usermodel.WorkbookFactory +import spock.lang.Specification + +/** + * TODO! + * short description + * + *

detailed description

+ * + * @since + */ +class NGSMeasurementContentProviderTest extends Specification { + + def "test that column reference works"() { + given: + Workbook workbook = WorkbookFactory.create(true) + def sheet = workbook.createSheet("My sheet") + when: + var result = NGSMeasurementContentProvider.addValueListWithName("namedArea", sheet, List.of("test1", "test2", "aböüß"), "test values") + then: + result.getRefersToFormula() == "'My sheet'!\$A\$1:\$A\$4" + result.getNameName() == "namedArea" + workbook.getName("namedArea") != null + + + } +} From 7ca7d7262c4f5c26c7bb4e36c37a8f2edfcef43b Mon Sep 17 00:00:00 2001 From: Tobias Koch Date: Tue, 10 Sep 2024 19:58:45 +0200 Subject: [PATCH 02/20] wip --- .../NGSMeasurementContentProvider.java | 179 ++++++++++-------- 1 file changed, 98 insertions(+), 81 deletions(-) diff --git a/user-interface/src/main/java/life/qbic/datamanager/views/projects/project/measurements/download/NGSMeasurementContentProvider.java b/user-interface/src/main/java/life/qbic/datamanager/views/projects/project/measurements/download/NGSMeasurementContentProvider.java index 500770f75..50753dbff 100644 --- a/user-interface/src/main/java/life/qbic/datamanager/views/projects/project/measurements/download/NGSMeasurementContentProvider.java +++ b/user-interface/src/main/java/life/qbic/datamanager/views/projects/project/measurements/download/NGSMeasurementContentProvider.java @@ -4,6 +4,7 @@ import java.io.ByteArrayOutputStream; import java.io.IOException; +import java.util.Arrays; import java.util.Collections; import java.util.LinkedList; import java.util.List; @@ -21,6 +22,7 @@ import org.apache.poi.ss.usermodel.Font; import org.apache.poi.ss.usermodel.Name; import org.apache.poi.ss.usermodel.Row; +import org.apache.poi.ss.usermodel.Row.MissingCellPolicy; import org.apache.poi.ss.usermodel.Sheet; import org.apache.poi.ss.usermodel.Workbook; import org.apache.poi.ss.util.CellReference; @@ -29,11 +31,11 @@ import org.apache.poi.xssf.usermodel.XSSFFont; import org.apache.poi.xssf.usermodel.XSSFWorkbook; -/** NGS Measurement Content Provider +/** + * NGS Measurement Content Provider *

- * Implementation of the {@link DownloadContentProvider} providing the content and file name for any files created - * from {@link NGSMeasurement} - * and {@link NGSMeasurementMetadata} + * Implementation of the {@link DownloadContentProvider} providing the content and file name for any + * files created from {@link NGSMeasurement} and {@link NGSMeasurementMetadata} *

*/ public class NGSMeasurementContentProvider implements DownloadContentProvider { @@ -47,9 +49,23 @@ public class NGSMeasurementContentProvider implements DownloadContentProvider { private static CellStyle boldStyle; private final List measurements = new LinkedList<>(); private static final String DEFAULT_FILE_NAME_PREFIX = "QBiC"; - private String fileNamePrefix = DEFAULT_FILE_NAME_PREFIX ; + private String fileNamePrefix = DEFAULT_FILE_NAME_PREFIX; private static final int DEFAULT_GENERATED_ROW_COUNT = 2_000; + private enum SequencingReadType { + SINGLE_END("singe-end"), + PAIRED_END("paired-end"); + private final String presentationString; + + SequencingReadType(String presentationString) { + this.presentationString = presentationString; + } + + static List getOptions() { + return Arrays.stream(values()).map(it -> it.presentationString).toList(); + } + } + private static void setAutoWidth(Sheet sheet) { for (int col = 0; col <= NGSMeasurementColumns.values().length; col++) { sheet.autoSizeColumn(col); @@ -78,68 +94,71 @@ private static void setCellStyle(Cell cell, boolean isReadOnly) { } } - private static void createMeasurementEntry(NGSMeasurementEntry ngsMeasurementEntry, Row entry) { - var measureCol = entry.createCell(NGSMeasurementColumns.MEASUREMENTCODE.columnNumber()); + private static void writeMeasurementIntoRow(NGSMeasurementEntry ngsMeasurementEntry, Row entry) { + var measureCol = getCellNeverNull(entry, NGSMeasurementColumns.MEASUREMENTCODE.columnNumber()); measureCol.setCellValue(ngsMeasurementEntry.measurementCode()); setCellStyle(measureCol, NGSMeasurementColumns.MEASUREMENTCODE.readOnly()); - var sampleIdCol = entry.createCell(NGSMeasurementColumns.SAMPLEID.columnNumber()); + var sampleIdCol = getCellNeverNull(entry, NGSMeasurementColumns.SAMPLEID.columnNumber()); sampleIdCol.setCellValue(ngsMeasurementEntry.sampleInformation().sampleId()); setCellStyle(sampleIdCol, NGSMeasurementColumns.SAMPLEID.readOnly()); - var sampleNameCol = entry.createCell(NGSMeasurementColumns.SAMPLENAME.columnNumber()); + var sampleNameCol = getCellNeverNull(entry, NGSMeasurementColumns.SAMPLENAME.columnNumber()); sampleNameCol.setCellValue(ngsMeasurementEntry.sampleInformation().sampleName()); setCellStyle(sampleNameCol, NGSMeasurementColumns.SAMPLENAME.readOnly()); - var orgIdCol = entry.createCell(NGSMeasurementColumns.ORGANISATIONID.columnNumber()); + var orgIdCol = getCellNeverNull(entry, NGSMeasurementColumns.ORGANISATIONID.columnNumber()); orgIdCol.setCellValue(ngsMeasurementEntry.organisationId()); setCellStyle(orgIdCol, NGSMeasurementColumns.ORGANISATIONID.readOnly()); - var organisationNameCol = entry.createCell( + var organisationNameCol = getCellNeverNull(entry, NGSMeasurementColumns.ORGANISATIONNAME.columnNumber()); + organisationNameCol.setCellValue(ngsMeasurementEntry.organisationName()); setCellStyle(organisationNameCol, NGSMeasurementColumns.ORGANISATIONNAME.readOnly()); - var facilityCol = entry.createCell(NGSMeasurementColumns.FACILITY.columnNumber()); + var facilityCol = getCellNeverNull(entry, NGSMeasurementColumns.FACILITY.columnNumber()); facilityCol.setCellValue(ngsMeasurementEntry.facility()); setCellStyle(facilityCol, NGSMeasurementColumns.FACILITY.readOnly); - var instrumentCol = entry.createCell(NGSMeasurementColumns.INSTRUMENT.columnNumber()); + var instrumentCol = getCellNeverNull(entry, NGSMeasurementColumns.INSTRUMENT.columnNumber()); instrumentCol.setCellValue(ngsMeasurementEntry.instrumentCURI()); setCellStyle(instrumentCol, NGSMeasurementColumns.INSTRUMENT.readOnly()); - var instrumentNameCol = entry.createCell(NGSMeasurementColumns.INSTRUMENTNAME.columnNumber()); + var instrumentNameCol = getCellNeverNull(entry, + NGSMeasurementColumns.INSTRUMENTNAME.columnNumber()); instrumentNameCol.setCellValue(ngsMeasurementEntry.instrumentName()); setCellStyle(instrumentNameCol, NGSMeasurementColumns.INSTRUMENTNAME.readOnly()); - var readTypeCol = entry.createCell(NGSMeasurementColumns.SEQUENCINGREADTYPE.columnNumber()); + var readTypeCol = getCellNeverNull(entry, + NGSMeasurementColumns.SEQUENCINGREADTYPE.columnNumber()); readTypeCol.setCellValue(ngsMeasurementEntry.readType()); setCellStyle(readTypeCol, NGSMeasurementColumns.SEQUENCINGREADTYPE.readOnly()); - var libraryKitCol = entry.createCell(NGSMeasurementColumns.LIBRARYKIT.columnNumber()); + var libraryKitCol = getCellNeverNull(entry, NGSMeasurementColumns.LIBRARYKIT.columnNumber()); libraryKitCol.setCellValue(ngsMeasurementEntry.libraryKit()); setCellStyle(libraryKitCol, NGSMeasurementColumns.LIBRARYKIT.readOnly()); - var flowCellCol = entry.createCell(NGSMeasurementColumns.FLOWCELL.columnNumber()); + var flowCellCol = getCellNeverNull(entry, NGSMeasurementColumns.FLOWCELL.columnNumber()); flowCellCol.setCellValue(ngsMeasurementEntry.flowCell()); setCellStyle(flowCellCol, NGSMeasurementColumns.FLOWCELL.readOnly()); - var runProtocolCol = entry.createCell(NGSMeasurementColumns.RUNPROTOCOL.columnNumber()); + var runProtocolCol = getCellNeverNull(entry, NGSMeasurementColumns.RUNPROTOCOL.columnNumber()); runProtocolCol.setCellValue(ngsMeasurementEntry.runProtocol()); setCellStyle(runProtocolCol, NGSMeasurementColumns.RUNPROTOCOL.readOnly()); - var poolGroupCol = entry.createCell(NGSMeasurementColumns.POOLGROUP.columnNumber()); + var poolGroupCol = getCellNeverNull(entry, NGSMeasurementColumns.POOLGROUP.columnNumber()); poolGroupCol.setCellValue(ngsMeasurementEntry.samplePoolGroup()); setCellStyle(poolGroupCol, NGSMeasurementColumns.POOLGROUP.readOnly()); - var indexI7Col = entry.createCell(NGSMeasurementColumns.INDEXI7.columnNumber()); + var indexI7Col = getCellNeverNull(entry, NGSMeasurementColumns.INDEXI7.columnNumber()); indexI7Col.setCellValue(ngsMeasurementEntry.indexI7()); setCellStyle(indexI7Col, NGSMeasurementColumns.INDEXI7.readOnly()); - var indexI5Col = entry.createCell(NGSMeasurementColumns.INDEXI5.columnNumber()); + var indexI5Col = getCellNeverNull(entry, NGSMeasurementColumns.INDEXI5.columnNumber()); indexI5Col.setCellValue(ngsMeasurementEntry.indexI5()); setCellStyle(indexI5Col, NGSMeasurementColumns.INDEXI5.readOnly()); - var commentCol = entry.createCell(NGSMeasurementColumns.COMMENT.columnNumber()); + var commentCol = getCellNeverNull(entry, NGSMeasurementColumns.COMMENT.columnNumber()); commentCol.setCellValue(ngsMeasurementEntry.comment()); setCellStyle(commentCol, NGSMeasurementColumns.COMMENT.readOnly()); } @@ -242,66 +261,34 @@ public byte[] getContent() { ByteArrayOutputStream byteArrayOutputStream; try (Workbook workbook = new XSSFWorkbook()) { + defineReadOnlyHeaderStyle(workbook); + defineReadOnlyCellStyle(workbook); + defineBoldStyle(workbook); Sheet hiddenSheet = workbook.createSheet("hidden"); //TODO hide sheet - Row hiddenSheetRow = hiddenSheet.createRow(0); - //sequencing read type - Cell hiddenSequencingReadTypeHeaderCell = hiddenSheetRow.createCell(0); - hiddenSequencingReadTypeHeaderCell.setCellValue("Sequencing read type"); - - var values = List.of("test", "test2", "test 42"); - var maxRowNumber = values.size() + 1; // already 1 based but we need to add the header row - var sequencingReadTypeColumnIndex = hiddenSequencingReadTypeHeaderCell.getColumnIndex(); - //insert values into spreadsheet - for (int i = 0; i < values.size(); i++) { - var rowIndex = i + 1; // we start with offset one because of the header row - Row row = getRowNeverNull(hiddenSheet, rowIndex); - row.createCell(sequencingReadTypeColumnIndex).setCellValue(values.get(i)); - } - - // row numbers start with 1 e.g. A1, A2, ... ; indexes start with 0 e.g. [0,0], [0,1], ... - String startColumnLetters = CellReference.convertNumToColString( - sequencingReadTypeColumnIndex); //takes an index not a number - int startRowNumber = - hiddenSequencingReadTypeHeaderCell.getRowIndex() + 1; // we need to add the header - String stopColumnLetters = CellReference.convertNumToColString( - sequencingReadTypeColumnIndex); //takes an index not a number - int stopRowNumber = maxRowNumber; - String reference = "'%s'!$%s$%s:$%s$%s".formatted( //e.g. 'My Sheet'!$A$1:$E$23 - hiddenSheet.getSheetName(), - startColumnLetters, - startRowNumber, - stopColumnLetters, - stopRowNumber - ); - Name sequencingReadTypeValues = workbook.createName(); - sequencingReadTypeValues.setRefersToFormula(reference); - sequencingReadTypeValues.setNameName("sequencingReadTypes"); + Name sequencingReadTypeArea = addValueListWithName("sequencingReadTypes", hiddenSheet, + new PropertyValues("Sequencing read type", SequencingReadType.getOptions())); Sheet sheet = workbook.createSheet("NGS Measurement Metadata"); workbook.setSheetOrder(sheet.getSheetName(), 0); - //TODO create header row - Row headerRow = sheet.createRow(0); - for (int i = 1; i < DEFAULT_GENERATED_ROW_COUNT; i++) { - Row row = sheet.createRow(i); + + Row header = getRowNeverNull(sheet, 0); + for (NGSMeasurementColumns value : NGSMeasurementColumns.values()) { + var cell = header.createCell(value.columnNumber()); + cell.setCellValue(value.headerName()); + setHeaderStyle(cell, value.readOnly()); } -// Row header = sheet.createRow(0); -// defineReadOnlyHeaderStyle(workbook); -// defineReadOnlyCellStyle(workbook); -// defineBoldStyle(workbook); -// formatHeader(header); -// -// int rowCounter = 1; -// -// for (NGSMeasurementEntry ngsMeasurementEntry : measurements) { -// Row entry = sheet.createRow(rowCounter); -// createMeasurementEntry(ngsMeasurementEntry, entry); -// rowCounter++; -// } -// -// setAutoWidth(sheet); + for (int i = 0; i < measurements.size(); i++) { + var measurement = measurements.get(i); + var rowIndex = i + 1; + Row row = getRowNeverNull(sheet, rowIndex); + writeMeasurementIntoRow(measurement, row); + } + //TODO add data validation + + setAutoWidth(sheet); byteArrayOutputStream = new ByteArrayOutputStream(); workbook.write(byteArrayOutputStream); @@ -327,16 +314,23 @@ private static Row getRowNeverNull(Sheet sheet, int index) { .orElse(sheet.createRow(index)); } + private static Cell getCellNeverNull(Row row, int colIndex) { + return Optional.ofNullable(row.getCell(colIndex, MissingCellPolicy.RETURN_BLANK_AS_NULL)) + .orElse(row.createCell(colIndex)); + } + @Override public String getFileName() { - return String.join("_" , fileNamePrefix, FILE_NAME_SUFFIX); + return String.join("_", fileNamePrefix, FILE_NAME_SUFFIX); } /** * NGS Measurement Columns * - *

Enumeration of the columns shown in the file used for NGS measurement registration and edit in the context of measurement file based upload. - * Provides the name of the header column, the column index and if the column should be set to readOnly in the generated sheet + *

Enumeration of the columns shown in the file used for NGS measurement registration and edit + * in the context of measurement file based upload. + * Provides the name of the header column, the column index and if the column should be set to + * readOnly in the generated sheet *

*/ enum NGSMeasurementColumns { @@ -361,7 +355,7 @@ enum NGSMeasurementColumns { INSTRUMENTNAME("Instrument Name", 8, true), SEQUENCINGREADTYPE("Sequencing Read Type", 9, - false), + false, List.of("single-end", "paired-end")), LIBRARYKIT("Library Kit", 10, false), FLOWCELL("Flow Cell", 11, @@ -373,17 +367,36 @@ enum NGSMeasurementColumns { INDEXI5("Index i5", 14, false), COMMENT("Comment", 15, - false); + false), + ; private final String headerName; private final int columnNumber; private final boolean readOnly; + private final List allowedValues; - NGSMeasurementColumns(String headerName, int columnNumber, boolean readOnly) { + /** + * @param headerName the name in the header + * @param columnIndex the index of the column this property is in + * @param readOnly is the property read only + * @param allowedValues a list of allowed values; null if not applicable + */ + NGSMeasurementColumns(String headerName, int columnIndex, boolean readOnly, + List allowedValues) { this.headerName = headerName; - this.columnNumber = columnNumber; + this.columnNumber = columnIndex; this.readOnly = readOnly; + this.allowedValues = allowedValues; + } + + /** + * @param headerName the name in the header + * @param columnIndex the index of the column this property is in + * @param readOnly is the property read only + */ + NGSMeasurementColumns(String headerName, int columnIndex, boolean readOnly) { + this(headerName, columnIndex, readOnly, null); } public String headerName() { @@ -397,5 +410,9 @@ public int columnNumber() { public boolean readOnly() { return readOnly; } + + public List allowedValues() { + return Optional.ofNullable(allowedValues).orElse(Collections.emptyList()); + } } } From d45eb8717d3123a15835a4b18dce84ff8434a1ac Mon Sep 17 00:00:00 2001 From: Tobias Koch Date: Fri, 13 Sep 2024 12:40:43 +0200 Subject: [PATCH 03/20] make it work --- .../NGSMeasurementContentProvider.java | 121 ++++++++++++------ 1 file changed, 82 insertions(+), 39 deletions(-) diff --git a/user-interface/src/main/java/life/qbic/datamanager/views/projects/project/measurements/download/NGSMeasurementContentProvider.java b/user-interface/src/main/java/life/qbic/datamanager/views/projects/project/measurements/download/NGSMeasurementContentProvider.java index 50753dbff..134fa77b3 100644 --- a/user-interface/src/main/java/life/qbic/datamanager/views/projects/project/measurements/download/NGSMeasurementContentProvider.java +++ b/user-interface/src/main/java/life/qbic/datamanager/views/projects/project/measurements/download/NGSMeasurementContentProvider.java @@ -18,6 +18,9 @@ import life.qbic.projectmanagement.domain.model.measurement.NGSMeasurement; import org.apache.poi.ss.usermodel.Cell; import org.apache.poi.ss.usermodel.CellStyle; +import org.apache.poi.ss.usermodel.DataValidation; +import org.apache.poi.ss.usermodel.DataValidationConstraint; +import org.apache.poi.ss.usermodel.DataValidationHelper; import org.apache.poi.ss.usermodel.FillPatternType; import org.apache.poi.ss.usermodel.Font; import org.apache.poi.ss.usermodel.Name; @@ -25,6 +28,8 @@ import org.apache.poi.ss.usermodel.Row.MissingCellPolicy; import org.apache.poi.ss.usermodel.Sheet; import org.apache.poi.ss.usermodel.Workbook; +import org.apache.poi.ss.util.CellRangeAddress; +import org.apache.poi.ss.util.CellRangeAddressList; import org.apache.poi.ss.util.CellReference; import org.apache.poi.xssf.usermodel.DefaultIndexedColorMap; import org.apache.poi.xssf.usermodel.XSSFColor; @@ -50,10 +55,10 @@ public class NGSMeasurementContentProvider implements DownloadContentProvider { private final List measurements = new LinkedList<>(); private static final String DEFAULT_FILE_NAME_PREFIX = "QBiC"; private String fileNamePrefix = DEFAULT_FILE_NAME_PREFIX; - private static final int DEFAULT_GENERATED_ROW_COUNT = 2_000; + private static final int DEFAULT_GENERATED_ROW_COUNT = 200; private enum SequencingReadType { - SINGLE_END("singe-end"), + SINGLE_END("single-end"), PAIRED_END("paired-end"); private final String presentationString; @@ -74,7 +79,7 @@ private static void setAutoWidth(Sheet sheet) { private static void formatHeader(Row header) { for (NGSMeasurementColumns value : NGSMeasurementColumns.values()) { - var cell = header.createCell(value.columnNumber()); + var cell = header.createCell(value.columnIndex()); cell.setCellValue(value.headerName()); setHeaderStyle(cell, value.readOnly()); } @@ -95,70 +100,70 @@ private static void setCellStyle(Cell cell, boolean isReadOnly) { } private static void writeMeasurementIntoRow(NGSMeasurementEntry ngsMeasurementEntry, Row entry) { - var measureCol = getCellNeverNull(entry, NGSMeasurementColumns.MEASUREMENTCODE.columnNumber()); + var measureCol = getOrCreateCell(entry, NGSMeasurementColumns.MEASUREMENTCODE.columnIndex()); measureCol.setCellValue(ngsMeasurementEntry.measurementCode()); setCellStyle(measureCol, NGSMeasurementColumns.MEASUREMENTCODE.readOnly()); - var sampleIdCol = getCellNeverNull(entry, NGSMeasurementColumns.SAMPLEID.columnNumber()); + var sampleIdCol = getOrCreateCell(entry, NGSMeasurementColumns.SAMPLEID.columnIndex()); sampleIdCol.setCellValue(ngsMeasurementEntry.sampleInformation().sampleId()); setCellStyle(sampleIdCol, NGSMeasurementColumns.SAMPLEID.readOnly()); - var sampleNameCol = getCellNeverNull(entry, NGSMeasurementColumns.SAMPLENAME.columnNumber()); + var sampleNameCol = getOrCreateCell(entry, NGSMeasurementColumns.SAMPLENAME.columnIndex()); sampleNameCol.setCellValue(ngsMeasurementEntry.sampleInformation().sampleName()); setCellStyle(sampleNameCol, NGSMeasurementColumns.SAMPLENAME.readOnly()); - var orgIdCol = getCellNeverNull(entry, NGSMeasurementColumns.ORGANISATIONID.columnNumber()); + var orgIdCol = getOrCreateCell(entry, NGSMeasurementColumns.ORGANISATIONID.columnIndex()); orgIdCol.setCellValue(ngsMeasurementEntry.organisationId()); setCellStyle(orgIdCol, NGSMeasurementColumns.ORGANISATIONID.readOnly()); - var organisationNameCol = getCellNeverNull(entry, - NGSMeasurementColumns.ORGANISATIONNAME.columnNumber()); + var organisationNameCol = getOrCreateCell(entry, + NGSMeasurementColumns.ORGANISATIONNAME.columnIndex()); organisationNameCol.setCellValue(ngsMeasurementEntry.organisationName()); setCellStyle(organisationNameCol, NGSMeasurementColumns.ORGANISATIONNAME.readOnly()); - var facilityCol = getCellNeverNull(entry, NGSMeasurementColumns.FACILITY.columnNumber()); + var facilityCol = getOrCreateCell(entry, NGSMeasurementColumns.FACILITY.columnIndex()); facilityCol.setCellValue(ngsMeasurementEntry.facility()); setCellStyle(facilityCol, NGSMeasurementColumns.FACILITY.readOnly); - var instrumentCol = getCellNeverNull(entry, NGSMeasurementColumns.INSTRUMENT.columnNumber()); + var instrumentCol = getOrCreateCell(entry, NGSMeasurementColumns.INSTRUMENT.columnIndex()); instrumentCol.setCellValue(ngsMeasurementEntry.instrumentCURI()); setCellStyle(instrumentCol, NGSMeasurementColumns.INSTRUMENT.readOnly()); - var instrumentNameCol = getCellNeverNull(entry, - NGSMeasurementColumns.INSTRUMENTNAME.columnNumber()); + var instrumentNameCol = getOrCreateCell(entry, + NGSMeasurementColumns.INSTRUMENTNAME.columnIndex()); instrumentNameCol.setCellValue(ngsMeasurementEntry.instrumentName()); setCellStyle(instrumentNameCol, NGSMeasurementColumns.INSTRUMENTNAME.readOnly()); - var readTypeCol = getCellNeverNull(entry, - NGSMeasurementColumns.SEQUENCINGREADTYPE.columnNumber()); + var readTypeCol = getOrCreateCell(entry, + NGSMeasurementColumns.SEQUENCINGREADTYPE.columnIndex()); readTypeCol.setCellValue(ngsMeasurementEntry.readType()); setCellStyle(readTypeCol, NGSMeasurementColumns.SEQUENCINGREADTYPE.readOnly()); - var libraryKitCol = getCellNeverNull(entry, NGSMeasurementColumns.LIBRARYKIT.columnNumber()); + var libraryKitCol = getOrCreateCell(entry, NGSMeasurementColumns.LIBRARYKIT.columnIndex()); libraryKitCol.setCellValue(ngsMeasurementEntry.libraryKit()); setCellStyle(libraryKitCol, NGSMeasurementColumns.LIBRARYKIT.readOnly()); - var flowCellCol = getCellNeverNull(entry, NGSMeasurementColumns.FLOWCELL.columnNumber()); + var flowCellCol = getOrCreateCell(entry, NGSMeasurementColumns.FLOWCELL.columnIndex()); flowCellCol.setCellValue(ngsMeasurementEntry.flowCell()); setCellStyle(flowCellCol, NGSMeasurementColumns.FLOWCELL.readOnly()); - var runProtocolCol = getCellNeverNull(entry, NGSMeasurementColumns.RUNPROTOCOL.columnNumber()); + var runProtocolCol = getOrCreateCell(entry, NGSMeasurementColumns.RUNPROTOCOL.columnIndex()); runProtocolCol.setCellValue(ngsMeasurementEntry.runProtocol()); setCellStyle(runProtocolCol, NGSMeasurementColumns.RUNPROTOCOL.readOnly()); - var poolGroupCol = getCellNeverNull(entry, NGSMeasurementColumns.POOLGROUP.columnNumber()); + var poolGroupCol = getOrCreateCell(entry, NGSMeasurementColumns.POOLGROUP.columnIndex()); poolGroupCol.setCellValue(ngsMeasurementEntry.samplePoolGroup()); setCellStyle(poolGroupCol, NGSMeasurementColumns.POOLGROUP.readOnly()); - var indexI7Col = getCellNeverNull(entry, NGSMeasurementColumns.INDEXI7.columnNumber()); + var indexI7Col = getOrCreateCell(entry, NGSMeasurementColumns.INDEXI7.columnIndex()); indexI7Col.setCellValue(ngsMeasurementEntry.indexI7()); setCellStyle(indexI7Col, NGSMeasurementColumns.INDEXI7.readOnly()); - var indexI5Col = getCellNeverNull(entry, NGSMeasurementColumns.INDEXI5.columnNumber()); + var indexI5Col = getOrCreateCell(entry, NGSMeasurementColumns.INDEXI5.columnIndex()); indexI5Col.setCellValue(ngsMeasurementEntry.indexI5()); setCellStyle(indexI5Col, NGSMeasurementColumns.INDEXI5.readOnly()); - var commentCol = getCellNeverNull(entry, NGSMeasurementColumns.COMMENT.columnNumber()); + var commentCol = getOrCreateCell(entry, NGSMeasurementColumns.COMMENT.columnIndex()); commentCol.setCellValue(ngsMeasurementEntry.comment()); setCellStyle(commentCol, NGSMeasurementColumns.COMMENT.readOnly()); } @@ -208,7 +213,7 @@ private void defineReadOnlyHeaderStyle(Workbook workbook) { */ protected static Name addValueListWithName(String name, Sheet sheet, PropertyValues propertyValues) { - Row headerRow = getRowNeverNull(sheet, 0); + Row headerRow = getOrCreateRow(sheet, 0); var columnNumber = Math.max(1, headerRow.getLastCellNum()); // we want to obtain 1 for the first to come if there are none and not -1 -.- var columnIndex = columnNumber - 1; @@ -219,13 +224,13 @@ protected static Name addValueListWithName(String name, Sheet sheet, for (int i = 0; i < propertyValues.size(); i++) { var rowIndex = i + 1; // +1 because of header row - Row valueRow = getRowNeverNull(sheet, rowIndex); + Row valueRow = getOrCreateRow(sheet, rowIndex); valueRow.createCell(columnIndex).setCellValue(propertyValues.get(i)); } - var reference = "'%s'!$%s$%s:$%s$%s".formatted( //e.g. 'My Sheet'!$A$1:$E$23 + var reference = "'%s'!$%s$%s:$%s$%s".formatted( //e.g. 'My Sheet'!$A$2:$E$23 sheet.getSheetName(), CellReference.convertNumToColString(columnIndex), - 1, + 2, //ignore the header CellReference.convertNumToColString(columnIndex), propertyValues.size() + 1 ); @@ -271,24 +276,57 @@ public byte[] getContent() { new PropertyValues("Sequencing read type", SequencingReadType.getOptions())); Sheet sheet = workbook.createSheet("NGS Measurement Metadata"); - workbook.setSheetOrder(sheet.getSheetName(), 0); - Row header = getRowNeverNull(sheet, 0); + Row header = getOrCreateRow(sheet, 0); for (NGSMeasurementColumns value : NGSMeasurementColumns.values()) { - var cell = header.createCell(value.columnNumber()); + var cell = header.createCell(value.columnIndex()); cell.setCellValue(value.headerName()); setHeaderStyle(cell, value.readOnly()); } - for (int i = 0; i < measurements.size(); i++) { - var measurement = measurements.get(i); - var rowIndex = i + 1; - Row row = getRowNeverNull(sheet, rowIndex); + var startIndex = 1; // start in row number 2 with index 1 as the header row has number 1 index 0 + int rowIndex = startIndex; + for (NGSMeasurementEntry measurement : measurements) { + Row row = getOrCreateRow(sheet, rowIndex); writeMeasurementIntoRow(measurement, row); + rowIndex++; + } + + var generatedRowCount = rowIndex - startIndex; + assert generatedRowCount == measurements.size() : "all measurements have a corresponding row"; + + for (int i = generatedRowCount + startIndex; i < DEFAULT_GENERATED_ROW_COUNT; i++) { + Row row = getOrCreateRow(sheet, i); + for (int j = 0; j < NGSMeasurementColumns.maxColumnIndex(); j++) { + getOrCreateCell(row, j); // make sure all cells exist + } } + //TODO add data validation + CellRangeAddressList sequencingReadTypeValueCells = new CellRangeAddressList(startIndex, + DEFAULT_GENERATED_ROW_COUNT - 1, //need the index + NGSMeasurementColumns.SEQUENCINGREADTYPE.columnIndex(), + NGSMeasurementColumns.SEQUENCINGREADTYPE.columnIndex()); + DataValidationHelper dvHelper = sheet.getDataValidationHelper(); + DataValidationConstraint dvConstraint = dvHelper.createFormulaListConstraint( + sequencingReadTypeArea.getNameName()); + System.out.println("dvConstraint.getFormula1() = " + dvConstraint.getFormula1()); + DataValidation sequencingReadTypeValidation = dvHelper.createValidation(dvConstraint, + sequencingReadTypeValueCells); + System.out.println("sequencingReadTypeValidation.getRegions() = " + + Arrays.stream(sequencingReadTypeValidation.getRegions().getCellRangeAddresses()) + .map(CellRangeAddress::formatAsString).toList()); + System.out.println("sequencingReadTypeValidation.getSuppressDropDownArrow() = " + + sequencingReadTypeValidation.getSuppressDropDownArrow()); + sequencingReadTypeValidation.setShowErrorBox(true); + sequencingReadTypeValidation.createErrorBox("Invalid entry", + "Please select from " + String.join(", ", + SequencingReadType.getOptions())); + sheet.addValidationData(sequencingReadTypeValidation); setAutoWidth(sheet); + workbook.setSheetOrder(sheet.getSheetName(), 0); + workbook.setActiveSheet(0); byteArrayOutputStream = new ByteArrayOutputStream(); workbook.write(byteArrayOutputStream); @@ -309,12 +347,12 @@ private static String getCellValue(Cell cell) { }; } - private static Row getRowNeverNull(Sheet sheet, int index) { + private static Row getOrCreateRow(Sheet sheet, int index) { return Optional.ofNullable(sheet.getRow(index)) .orElse(sheet.createRow(index)); } - private static Cell getCellNeverNull(Row row, int colIndex) { + private static Cell getOrCreateCell(Row row, int colIndex) { return Optional.ofNullable(row.getCell(colIndex, MissingCellPolicy.RETURN_BLANK_AS_NULL)) .orElse(row.createCell(colIndex)); } @@ -371,10 +409,15 @@ enum NGSMeasurementColumns { ; private final String headerName; - private final int columnNumber; + private final int columnIndex; private final boolean readOnly; private final List allowedValues; + static int maxColumnIndex() { + return Arrays.stream(values()) + .mapToInt(NGSMeasurementColumns::columnIndex) + .max().orElse(0); + } /** * @param headerName the name in the header @@ -385,7 +428,7 @@ enum NGSMeasurementColumns { NGSMeasurementColumns(String headerName, int columnIndex, boolean readOnly, List allowedValues) { this.headerName = headerName; - this.columnNumber = columnIndex; + this.columnIndex = columnIndex; this.readOnly = readOnly; this.allowedValues = allowedValues; } @@ -403,8 +446,8 @@ public String headerName() { return headerName; } - public int columnNumber() { - return columnNumber; + public int columnIndex() { + return columnIndex; } public boolean readOnly() { From f0e073b1c7203631cae30566d59bec5ae295153d Mon Sep 17 00:00:00 2001 From: Tobias Koch Date: Fri, 13 Sep 2024 17:53:12 +0200 Subject: [PATCH 04/20] Extract behaviour --- .../spreadsheet/XLSXTemplateHelper.java | 205 ++++++++++++++++++ .../NGSMeasurementContentProvider.java | 105 ++------- .../spreadsheet/XLSXTemplateHelperTest.groovy | 22 ++ 3 files changed, 242 insertions(+), 90 deletions(-) create mode 100644 user-interface/src/main/java/life/qbic/datamanager/spreadsheet/XLSXTemplateHelper.java create mode 100644 user-interface/src/test/java/life/qbic/datamanager/spreadsheet/XLSXTemplateHelperTest.groovy diff --git a/user-interface/src/main/java/life/qbic/datamanager/spreadsheet/XLSXTemplateHelper.java b/user-interface/src/main/java/life/qbic/datamanager/spreadsheet/XLSXTemplateHelper.java new file mode 100644 index 000000000..1ddec66fe --- /dev/null +++ b/user-interface/src/main/java/life/qbic/datamanager/spreadsheet/XLSXTemplateHelper.java @@ -0,0 +1,205 @@ +package life.qbic.datamanager.spreadsheet; + +import static java.util.Objects.isNull; +import static java.util.Objects.nonNull; + +import java.util.List; +import java.util.Random; +import java.util.function.Predicate; +import java.util.stream.Collectors; +import org.apache.poi.ss.usermodel.Cell; +import org.apache.poi.ss.usermodel.DataValidation; +import org.apache.poi.ss.usermodel.DataValidationConstraint; +import org.apache.poi.ss.usermodel.DataValidationHelper; +import org.apache.poi.ss.usermodel.Name; +import org.apache.poi.ss.usermodel.Row; +import org.apache.poi.ss.usermodel.Sheet; +import org.apache.poi.ss.usermodel.SheetVisibility; +import org.apache.poi.ss.usermodel.Workbook; +import org.apache.poi.ss.util.CellRangeAddressList; +import org.apache.poi.ss.util.CellReference; + +/** + * Helps to create excel template sheets. + *

+ * This class provides methods to set up validation, lock and hide sheets and a safe way to access + * rows and cells in a sheet. + * + * @since 1.5.0 + */ +public class XLSXTemplateHelper { + + private static final Random RANDOM = new Random(); + + protected XLSXTemplateHelper() { + //hide constructor as static methods only are used + } + + /** + * Asks for a specific row with an index starting with index 0 for the first row. If no row + * exists, creates a new and empty row. + * + * @param sheet the sheet to ask for the row + * @param rowIndex the index of the row starting with 0 for the first row. + * @return the row in the sheet + * @since 1.5.0 + */ + public static Row getOrCreateRow(Sheet sheet, int rowIndex) { + Row row = sheet.getRow(rowIndex); + if (isNull(row)) { + row = sheet.createRow(rowIndex); + } + return row; + } + + /** + * Asks for a specific cell with a column index starting with index 0 for the first column. If no + * cell exists, creates a new and blank cell. + * + * @param row the row to ask for the cell + * @param cellIndex the column index of the cell in the row. Starting with 0 for the first + * column. + * @return the cell in the row + * @since 1.5.0 + */ + public static Cell getOrCreateCell(Row row, int cellIndex) { + Cell cell = row.getCell(cellIndex); + if (nonNull(cell)) { + return cell; + } + return row.createCell(cellIndex); + } + + /** + * The value within the cell as String + * + * @param cell the cell to read the value from, must not be null. + * @return the value of the cell. Never null. + * @since 1.5.0 + */ + public static String getCellValueAsString(Cell cell) { + if (isNull(cell)) { + throw new IllegalArgumentException("cell is null"); + } + return switch (cell.getCellType()) { + case FORMULA, _NONE, BLANK, ERROR -> ""; + case STRING -> cell.getStringCellValue(); + case BOOLEAN -> Boolean.toString(cell.getBooleanCellValue()); + case NUMERIC -> Double.toString(cell.getNumericCellValue()); + }; + } + + /** + * Adds values to the sheet and returns the named area where they were added. + * + * @param sheet the sheet where to add the values + * @param propertyName the name of the property + * @param options the available options to choose a value from + * @return a defined name for a range of cells in the workbook. + * @see Name + * @since 1.5.0 + */ + public static Name createOptionArea(Sheet sheet, String propertyName, + List options) { + Row headerRow = getOrCreateRow(sheet, 0); + var columnNumber = Math.max(1, + headerRow.getLastCellNum()); // we want to obtain 1 for the first to come if there are none and not -1 -.- + var columnIndex = columnNumber - 1; + + // create header cell + Cell headerRowCell = headerRow.createCell(columnIndex); + headerRowCell.setCellValue(propertyName); + + var startIndex = 1; // ignore the header at 0 + var rowIndex = startIndex; + for (String option : options) { + Row valueRow = getOrCreateRow(sheet, rowIndex); + getOrCreateCell(valueRow, columnIndex) + .setCellValue(option); + rowIndex++; + } + var reference = "'%s'!$%s$%s:$%s$%s".formatted( //e.g. 'My Sheet'!$A$2:$E$23 + sheet.getSheetName(), + CellReference.convertNumToColString(columnIndex), + 1 + startIndex, //shift by start index + CellReference.convertNumToColString(columnIndex), + options.size() + startIndex //shift by start index + ); + var namedArea = sheet.getWorkbook().createName(); + + namedArea.setNameName(toCamelCase(propertyName)); + namedArea.setRefersToFormula(reference); + return namedArea; + } + + /** + * Converts a string to camel case. Leaves the first character as is. Considers non-word + * characters as well as underscores to be word separators. + *

+ * For example: "this is a sentence" and "this_is_a_string" become "thisIsASentence" + * + * @param input the input to camel case + * @return a camel case representation of the input + * @since 1.5.0 + */ + protected static String toCamelCase(String input) { + StringBuilder stringBuilder = new StringBuilder(input); + Predicate isWordSeparator = character -> String.valueOf(character).matches("\\W|_"); + for (int i = 0; i < stringBuilder.length(); i++) { + if (isWordSeparator.test(stringBuilder.charAt(i))) { + stringBuilder.deleteCharAt( + i); //remove the separator shifting the next character into position i + if (stringBuilder.length() <= i) { + //the last character was removed + break; + } + stringBuilder.replace(i, i + 1, + String.valueOf(stringBuilder.charAt(i)).toUpperCase());//capitalize next character + } + } + return stringBuilder.toString(); + } + + /** + * Adds data validation to an area in the spreadsheet. Requires the valid options to be set + * beforehand as a name. This can be done by using {@link #createOptionArea(Sheet, String, List)} + * + * @param sheet the sheet in which the validation should be added + * @param startColIdx the start column of the validated values >= 0 + * @param startRowIdx the start row of the validated values >= 0 + * @param stopColIdx the last column of the validated values >= startColIdx + * @param stopRowIdx the last row of the validated values >= startRowIdx + * @param allowedValues the named area defining the allowed values + * @since 1.5.0 + */ + public static void addDataValidation(Sheet sheet, int startColIdx, int startRowIdx, + int stopColIdx, int stopRowIdx, Name allowedValues) { + CellRangeAddressList validatedCells = new CellRangeAddressList(startRowIdx, + stopRowIdx, + startColIdx, + stopColIdx); + DataValidationHelper dataValidationHelper = sheet.getDataValidationHelper(); + DataValidationConstraint formulaListConstraint = dataValidationHelper + .createFormulaListConstraint(allowedValues.getNameName()); + DataValidation validation = dataValidationHelper.createValidation(formulaListConstraint, + validatedCells); + validation.setSuppressDropDownArrow(true); // shows dropdown if true + validation.setShowErrorBox(true); + validation.createErrorBox("Invalid choice", "Please select a value from the dropdown list."); + sheet.addValidationData(validation); + } + + + public static void hideSheet(Workbook workbook, Sheet sheet) { + workbook.setSheetVisibility(workbook.getSheetIndex(sheet), SheetVisibility.VERY_HIDDEN); + } + + public static void lockSheet(Sheet sheet) { + String randomPassword = RANDOM.ints(16) + .mapToObj(Integer::toString) + .collect(Collectors.joining()); + sheet.protectSheet(randomPassword); + } + + +} diff --git a/user-interface/src/main/java/life/qbic/datamanager/views/projects/project/measurements/download/NGSMeasurementContentProvider.java b/user-interface/src/main/java/life/qbic/datamanager/views/projects/project/measurements/download/NGSMeasurementContentProvider.java index 134fa77b3..0c8479a52 100644 --- a/user-interface/src/main/java/life/qbic/datamanager/views/projects/project/measurements/download/NGSMeasurementContentProvider.java +++ b/user-interface/src/main/java/life/qbic/datamanager/views/projects/project/measurements/download/NGSMeasurementContentProvider.java @@ -1,5 +1,8 @@ package life.qbic.datamanager.views.projects.project.measurements.download; +import static life.qbic.datamanager.spreadsheet.XLSXTemplateHelper.createOptionArea; +import static life.qbic.datamanager.spreadsheet.XLSXTemplateHelper.hideSheet; +import static life.qbic.datamanager.spreadsheet.XLSXTemplateHelper.lockSheet; import static life.qbic.logging.service.LoggerFactory.logger; import java.io.ByteArrayOutputStream; @@ -11,6 +14,7 @@ import java.util.Optional; import life.qbic.application.commons.ApplicationException; import life.qbic.application.commons.ApplicationException.ErrorCode; +import life.qbic.datamanager.spreadsheet.XLSXTemplateHelper; import life.qbic.datamanager.views.general.download.DownloadContentProvider; import life.qbic.datamanager.views.projects.project.measurements.NGSMeasurementEntry; import life.qbic.logging.api.Logger; @@ -18,9 +22,6 @@ import life.qbic.projectmanagement.domain.model.measurement.NGSMeasurement; import org.apache.poi.ss.usermodel.Cell; import org.apache.poi.ss.usermodel.CellStyle; -import org.apache.poi.ss.usermodel.DataValidation; -import org.apache.poi.ss.usermodel.DataValidationConstraint; -import org.apache.poi.ss.usermodel.DataValidationHelper; import org.apache.poi.ss.usermodel.FillPatternType; import org.apache.poi.ss.usermodel.Font; import org.apache.poi.ss.usermodel.Name; @@ -28,9 +29,6 @@ import org.apache.poi.ss.usermodel.Row.MissingCellPolicy; import org.apache.poi.ss.usermodel.Sheet; import org.apache.poi.ss.usermodel.Workbook; -import org.apache.poi.ss.util.CellRangeAddress; -import org.apache.poi.ss.util.CellRangeAddressList; -import org.apache.poi.ss.util.CellReference; import org.apache.poi.xssf.usermodel.DefaultIndexedColorMap; import org.apache.poi.xssf.usermodel.XSSFColor; import org.apache.poi.xssf.usermodel.XSSFFont; @@ -202,60 +200,6 @@ private void defineReadOnlyHeaderStyle(Workbook workbook) { readOnlyHeaderStyle.setFont(fontHeader); } - /** - * Adds values to the sheet and returns the named area where they were added. The named area can - * later be used by calling {} - * - * @param name the name to choose for the area - * @param sheet the sheet where to add the values - * @param propertyValues the property values to add - * @return a defined name for a range of cells in the workbook. - */ - protected static Name addValueListWithName(String name, Sheet sheet, - PropertyValues propertyValues) { - Row headerRow = getOrCreateRow(sheet, 0); - var columnNumber = Math.max(1, - headerRow.getLastCellNum()); // we want to obtain 1 for the first to come if there are none and not -1 -.- - var columnIndex = columnNumber - 1; - - // create header cell - Cell headerRowCell = headerRow.createCell(columnIndex); - headerRowCell.setCellValue(propertyValues.propertyName()); - - for (int i = 0; i < propertyValues.size(); i++) { - var rowIndex = i + 1; // +1 because of header row - Row valueRow = getOrCreateRow(sheet, rowIndex); - valueRow.createCell(columnIndex).setCellValue(propertyValues.get(i)); - } - var reference = "'%s'!$%s$%s:$%s$%s".formatted( //e.g. 'My Sheet'!$A$2:$E$23 - sheet.getSheetName(), - CellReference.convertNumToColString(columnIndex), - 2, //ignore the header - CellReference.convertNumToColString(columnIndex), - propertyValues.size() + 1 - ); - var namedArea = sheet.getWorkbook().createName(); - namedArea.setNameName(name); - namedArea.setRefersToFormula(reference); - return namedArea; - } - - protected record PropertyValues(String propertyName, List values) { - - public int size() { - return values.size(); - } - - public String get(int index) { - return values.get(index); - } - - public PropertyValues(String propertyName, List values) { - this.propertyName = propertyName; - this.values = Collections.unmodifiableList(values); - } - } - @Override public byte[] getContent() { @@ -271,9 +215,9 @@ public byte[] getContent() { defineBoldStyle(workbook); Sheet hiddenSheet = workbook.createSheet("hidden"); - //TODO hide sheet - Name sequencingReadTypeArea = addValueListWithName("sequencingReadTypes", hiddenSheet, - new PropertyValues("Sequencing read type", SequencingReadType.getOptions())); + + Name sequencingReadTypeArea = createOptionArea(hiddenSheet, + "Sequencing read type", SequencingReadType.getOptions()); Sheet sheet = workbook.createSheet("NGS Measurement Metadata"); @@ -295,39 +239,20 @@ public byte[] getContent() { var generatedRowCount = rowIndex - startIndex; assert generatedRowCount == measurements.size() : "all measurements have a corresponding row"; - for (int i = generatedRowCount + startIndex; i < DEFAULT_GENERATED_ROW_COUNT; i++) { - Row row = getOrCreateRow(sheet, i); - for (int j = 0; j < NGSMeasurementColumns.maxColumnIndex(); j++) { - getOrCreateCell(row, j); // make sure all cells exist - } - } - - //TODO add data validation - CellRangeAddressList sequencingReadTypeValueCells = new CellRangeAddressList(startIndex, - DEFAULT_GENERATED_ROW_COUNT - 1, //need the index + XLSXTemplateHelper.addDataValidation(sheet, NGSMeasurementColumns.SEQUENCINGREADTYPE.columnIndex(), - NGSMeasurementColumns.SEQUENCINGREADTYPE.columnIndex()); - DataValidationHelper dvHelper = sheet.getDataValidationHelper(); - DataValidationConstraint dvConstraint = dvHelper.createFormulaListConstraint( - sequencingReadTypeArea.getNameName()); - System.out.println("dvConstraint.getFormula1() = " + dvConstraint.getFormula1()); - DataValidation sequencingReadTypeValidation = dvHelper.createValidation(dvConstraint, - sequencingReadTypeValueCells); - System.out.println("sequencingReadTypeValidation.getRegions() = " - + Arrays.stream(sequencingReadTypeValidation.getRegions().getCellRangeAddresses()) - .map(CellRangeAddress::formatAsString).toList()); - System.out.println("sequencingReadTypeValidation.getSuppressDropDownArrow() = " - + sequencingReadTypeValidation.getSuppressDropDownArrow()); - sequencingReadTypeValidation.setShowErrorBox(true); - sequencingReadTypeValidation.createErrorBox("Invalid entry", - "Please select from " + String.join(", ", - SequencingReadType.getOptions())); - sheet.addValidationData(sequencingReadTypeValidation); + startIndex, + NGSMeasurementColumns.SEQUENCINGREADTYPE.columnIndex(), + DEFAULT_GENERATED_ROW_COUNT - 1, + sequencingReadTypeArea); setAutoWidth(sheet); workbook.setSheetOrder(sheet.getSheetName(), 0); workbook.setActiveSheet(0); + lockSheet(hiddenSheet); + hideSheet(workbook, hiddenSheet); + byteArrayOutputStream = new ByteArrayOutputStream(); workbook.write(byteArrayOutputStream); } catch (IOException e) { diff --git a/user-interface/src/test/java/life/qbic/datamanager/spreadsheet/XLSXTemplateHelperTest.groovy b/user-interface/src/test/java/life/qbic/datamanager/spreadsheet/XLSXTemplateHelperTest.groovy new file mode 100644 index 000000000..8fabc001f --- /dev/null +++ b/user-interface/src/test/java/life/qbic/datamanager/spreadsheet/XLSXTemplateHelperTest.groovy @@ -0,0 +1,22 @@ +package life.qbic.datamanager.spreadsheet + +import spock.lang.Specification + +class XLSXTemplateHelperTest extends Specification { + def "test to camel case"() { + expect: + XLSXTemplateHelper.toCamelCase(input).equals(output) + + where: + input | output + "this is a test" | "thisIsATest" + "this is 4 test" | "thisIs4Test" + "this-is-a-test" | "thisIsATest" + "this_is_a_test" | "thisIsATest" + "this is_a-test" | "thisIsATest" + "thisisatest" | "thisisatest" + "thisIsATest" | "thisIsATest" + "this is a test*" | "thisIsATest" + + } +} From 82f0b1d195fb52dc461cec87ff6f20069149f6cf Mon Sep 17 00:00:00 2001 From: Tobias Koch Date: Fri, 13 Sep 2024 17:56:08 +0200 Subject: [PATCH 05/20] remove unused method --- .../download/NGSMeasurementContentProvider.java | 9 --------- 1 file changed, 9 deletions(-) diff --git a/user-interface/src/main/java/life/qbic/datamanager/views/projects/project/measurements/download/NGSMeasurementContentProvider.java b/user-interface/src/main/java/life/qbic/datamanager/views/projects/project/measurements/download/NGSMeasurementContentProvider.java index 0c8479a52..033582092 100644 --- a/user-interface/src/main/java/life/qbic/datamanager/views/projects/project/measurements/download/NGSMeasurementContentProvider.java +++ b/user-interface/src/main/java/life/qbic/datamanager/views/projects/project/measurements/download/NGSMeasurementContentProvider.java @@ -263,15 +263,6 @@ public byte[] getContent() { return byteArrayOutputStream.toByteArray(); } - private static String getCellValue(Cell cell) { - return switch (cell.getCellType()) { - case FORMULA, _NONE, BLANK, ERROR -> ""; - case STRING -> cell.getStringCellValue(); - case BOOLEAN -> Boolean.toString(cell.getBooleanCellValue()); - case NUMERIC -> Double.toString(cell.getNumericCellValue()); - }; - } - private static Row getOrCreateRow(Sheet sheet, int index) { return Optional.ofNullable(sheet.getRow(index)) .orElse(sheet.createRow(index)); From 265accbe79e0bdbe96e3eda423aece623a65928a Mon Sep 17 00:00:00 2001 From: Tobias Koch Date: Mon, 16 Sep 2024 08:52:38 +0200 Subject: [PATCH 06/20] Adapt proteomics template Fixes the edit case and renames instrument to MS device fixes #786 --- .../MeasurementProteomicsValidator.java | 4 + .../NGSMeasurementContentProvider.java | 104 ++++---- .../ProteomicsMeasurementContentProvider.java | 235 ++++++++++-------- 3 files changed, 176 insertions(+), 167 deletions(-) diff --git a/project-management/src/main/java/life/qbic/projectmanagement/application/measurement/validation/MeasurementProteomicsValidator.java b/project-management/src/main/java/life/qbic/projectmanagement/application/measurement/validation/MeasurementProteomicsValidator.java index c83caec57..696054645 100644 --- a/project-management/src/main/java/life/qbic/projectmanagement/application/measurement/validation/MeasurementProteomicsValidator.java +++ b/project-management/src/main/java/life/qbic/projectmanagement/application/measurement/validation/MeasurementProteomicsValidator.java @@ -180,6 +180,10 @@ public static boolean isDigestionMethod(String input) { public String getName() { return name; } + + public static List getOptions() { + return Arrays.stream(values()).map(DigestionMethod::getName).toList(); + } } private class ValidationPolicy { diff --git a/user-interface/src/main/java/life/qbic/datamanager/views/projects/project/measurements/download/NGSMeasurementContentProvider.java b/user-interface/src/main/java/life/qbic/datamanager/views/projects/project/measurements/download/NGSMeasurementContentProvider.java index 033582092..540b2cfac 100644 --- a/user-interface/src/main/java/life/qbic/datamanager/views/projects/project/measurements/download/NGSMeasurementContentProvider.java +++ b/user-interface/src/main/java/life/qbic/datamanager/views/projects/project/measurements/download/NGSMeasurementContentProvider.java @@ -8,7 +8,6 @@ import java.io.ByteArrayOutputStream; import java.io.IOException; import java.util.Arrays; -import java.util.Collections; import java.util.LinkedList; import java.util.List; import java.util.Optional; @@ -98,27 +97,27 @@ private static void setCellStyle(Cell cell, boolean isReadOnly) { } private static void writeMeasurementIntoRow(NGSMeasurementEntry ngsMeasurementEntry, Row entry) { - var measureCol = getOrCreateCell(entry, NGSMeasurementColumns.MEASUREMENTCODE.columnIndex()); + var measureCol = getOrCreateCell(entry, NGSMeasurementColumns.MEASUREMENT_ID.columnIndex()); measureCol.setCellValue(ngsMeasurementEntry.measurementCode()); - setCellStyle(measureCol, NGSMeasurementColumns.MEASUREMENTCODE.readOnly()); + setCellStyle(measureCol, NGSMeasurementColumns.MEASUREMENT_ID.readOnly()); - var sampleIdCol = getOrCreateCell(entry, NGSMeasurementColumns.SAMPLEID.columnIndex()); + var sampleIdCol = getOrCreateCell(entry, NGSMeasurementColumns.SAMPLE_ID.columnIndex()); sampleIdCol.setCellValue(ngsMeasurementEntry.sampleInformation().sampleId()); - setCellStyle(sampleIdCol, NGSMeasurementColumns.SAMPLEID.readOnly()); + setCellStyle(sampleIdCol, NGSMeasurementColumns.SAMPLE_ID.readOnly()); - var sampleNameCol = getOrCreateCell(entry, NGSMeasurementColumns.SAMPLENAME.columnIndex()); + var sampleNameCol = getOrCreateCell(entry, NGSMeasurementColumns.SAMPLE_NAME.columnIndex()); sampleNameCol.setCellValue(ngsMeasurementEntry.sampleInformation().sampleName()); - setCellStyle(sampleNameCol, NGSMeasurementColumns.SAMPLENAME.readOnly()); + setCellStyle(sampleNameCol, NGSMeasurementColumns.SAMPLE_NAME.readOnly()); - var orgIdCol = getOrCreateCell(entry, NGSMeasurementColumns.ORGANISATIONID.columnIndex()); + var orgIdCol = getOrCreateCell(entry, NGSMeasurementColumns.ORGANISATION_ID.columnIndex()); orgIdCol.setCellValue(ngsMeasurementEntry.organisationId()); - setCellStyle(orgIdCol, NGSMeasurementColumns.ORGANISATIONID.readOnly()); + setCellStyle(orgIdCol, NGSMeasurementColumns.ORGANISATION_ID.readOnly()); var organisationNameCol = getOrCreateCell(entry, - NGSMeasurementColumns.ORGANISATIONNAME.columnIndex()); + NGSMeasurementColumns.ORGANISATION_NAME.columnIndex()); organisationNameCol.setCellValue(ngsMeasurementEntry.organisationName()); - setCellStyle(organisationNameCol, NGSMeasurementColumns.ORGANISATIONNAME.readOnly()); + setCellStyle(organisationNameCol, NGSMeasurementColumns.ORGANISATION_NAME.readOnly()); var facilityCol = getOrCreateCell(entry, NGSMeasurementColumns.FACILITY.columnIndex()); facilityCol.setCellValue(ngsMeasurementEntry.facility()); @@ -128,38 +127,39 @@ private static void writeMeasurementIntoRow(NGSMeasurementEntry ngsMeasurementEn setCellStyle(instrumentCol, NGSMeasurementColumns.INSTRUMENT.readOnly()); var instrumentNameCol = getOrCreateCell(entry, - NGSMeasurementColumns.INSTRUMENTNAME.columnIndex()); + NGSMeasurementColumns.INSTRUMENT_NAME.columnIndex()); instrumentNameCol.setCellValue(ngsMeasurementEntry.instrumentName()); - setCellStyle(instrumentNameCol, NGSMeasurementColumns.INSTRUMENTNAME.readOnly()); + setCellStyle(instrumentNameCol, NGSMeasurementColumns.INSTRUMENT_NAME.readOnly()); var readTypeCol = getOrCreateCell(entry, - NGSMeasurementColumns.SEQUENCINGREADTYPE.columnIndex()); + NGSMeasurementColumns.SEQUENCING_READ_TYPE.columnIndex()); readTypeCol.setCellValue(ngsMeasurementEntry.readType()); - setCellStyle(readTypeCol, NGSMeasurementColumns.SEQUENCINGREADTYPE.readOnly()); + setCellStyle(readTypeCol, NGSMeasurementColumns.SEQUENCING_READ_TYPE.readOnly()); - var libraryKitCol = getOrCreateCell(entry, NGSMeasurementColumns.LIBRARYKIT.columnIndex()); + var libraryKitCol = getOrCreateCell(entry, NGSMeasurementColumns.LIBRARY_KIT.columnIndex()); libraryKitCol.setCellValue(ngsMeasurementEntry.libraryKit()); - setCellStyle(libraryKitCol, NGSMeasurementColumns.LIBRARYKIT.readOnly()); + setCellStyle(libraryKitCol, NGSMeasurementColumns.LIBRARY_KIT.readOnly()); - var flowCellCol = getOrCreateCell(entry, NGSMeasurementColumns.FLOWCELL.columnIndex()); + var flowCellCol = getOrCreateCell(entry, NGSMeasurementColumns.FLOW_CELL.columnIndex()); flowCellCol.setCellValue(ngsMeasurementEntry.flowCell()); - setCellStyle(flowCellCol, NGSMeasurementColumns.FLOWCELL.readOnly()); + setCellStyle(flowCellCol, NGSMeasurementColumns.FLOW_CELL.readOnly()); - var runProtocolCol = getOrCreateCell(entry, NGSMeasurementColumns.RUNPROTOCOL.columnIndex()); + var runProtocolCol = getOrCreateCell(entry, + NGSMeasurementColumns.SEQUENCING_RUN_PROTOCOL.columnIndex()); runProtocolCol.setCellValue(ngsMeasurementEntry.runProtocol()); - setCellStyle(runProtocolCol, NGSMeasurementColumns.RUNPROTOCOL.readOnly()); + setCellStyle(runProtocolCol, NGSMeasurementColumns.SEQUENCING_RUN_PROTOCOL.readOnly()); - var poolGroupCol = getOrCreateCell(entry, NGSMeasurementColumns.POOLGROUP.columnIndex()); + var poolGroupCol = getOrCreateCell(entry, NGSMeasurementColumns.POOL_GROUP.columnIndex()); poolGroupCol.setCellValue(ngsMeasurementEntry.samplePoolGroup()); - setCellStyle(poolGroupCol, NGSMeasurementColumns.POOLGROUP.readOnly()); + setCellStyle(poolGroupCol, NGSMeasurementColumns.POOL_GROUP.readOnly()); - var indexI7Col = getOrCreateCell(entry, NGSMeasurementColumns.INDEXI7.columnIndex()); + var indexI7Col = getOrCreateCell(entry, NGSMeasurementColumns.INDEX_I7.columnIndex()); indexI7Col.setCellValue(ngsMeasurementEntry.indexI7()); - setCellStyle(indexI7Col, NGSMeasurementColumns.INDEXI7.readOnly()); + setCellStyle(indexI7Col, NGSMeasurementColumns.INDEX_I7.readOnly()); - var indexI5Col = getOrCreateCell(entry, NGSMeasurementColumns.INDEXI5.columnIndex()); + var indexI5Col = getOrCreateCell(entry, NGSMeasurementColumns.INDEX_I5.columnIndex()); indexI5Col.setCellValue(ngsMeasurementEntry.indexI5()); - setCellStyle(indexI5Col, NGSMeasurementColumns.INDEXI5.readOnly()); + setCellStyle(indexI5Col, NGSMeasurementColumns.INDEX_I5.readOnly()); var commentCol = getOrCreateCell(entry, NGSMeasurementColumns.COMMENT.columnIndex()); commentCol.setCellValue(ngsMeasurementEntry.comment()); @@ -240,9 +240,9 @@ public byte[] getContent() { assert generatedRowCount == measurements.size() : "all measurements have a corresponding row"; XLSXTemplateHelper.addDataValidation(sheet, - NGSMeasurementColumns.SEQUENCINGREADTYPE.columnIndex(), + NGSMeasurementColumns.SEQUENCING_READ_TYPE.columnIndex(), startIndex, - NGSMeasurementColumns.SEQUENCINGREADTYPE.columnIndex(), + NGSMeasurementColumns.SEQUENCING_READ_TYPE.columnIndex(), DEFAULT_GENERATED_ROW_COUNT - 1, sequencingReadTypeArea); @@ -289,36 +289,36 @@ public String getFileName() { */ enum NGSMeasurementColumns { - MEASUREMENTCODE("Measurement ID", 0, + MEASUREMENT_ID("Measurement ID", 0, true), - SAMPLEID("QBiC Sample Id", 1, + SAMPLE_ID("QBiC Sample Id", 1, true), - SAMPLENAME( + SAMPLE_NAME( "Sample Name", 2, true), - POOLGROUP("Sample Pool Group", 3, + POOL_GROUP("Sample Pool Group", 3, true), - ORGANISATIONID("Organisation ID", 4, + ORGANISATION_ID("Organisation ID", 4, false), - ORGANISATIONNAME("Organisation Name", 5, + ORGANISATION_NAME("Organisation Name", 5, true), FACILITY("Facility", 6, false), INSTRUMENT("Instrument", 7, false), - INSTRUMENTNAME("Instrument Name", 8, + INSTRUMENT_NAME("Instrument Name", 8, true), - SEQUENCINGREADTYPE("Sequencing Read Type", 9, - false, List.of("single-end", "paired-end")), - LIBRARYKIT("Library Kit", 10, + SEQUENCING_READ_TYPE("Sequencing Read Type", 9, false), - FLOWCELL("Flow Cell", 11, + LIBRARY_KIT("Library Kit", 10, false), - RUNPROTOCOL("Sequencing Run Protocol", 12, + FLOW_CELL("Flow Cell", 11, false), - INDEXI7("Index i7", 13, + SEQUENCING_RUN_PROTOCOL("Sequencing Run Protocol", 12, false), - INDEXI5("Index i5", 14, + INDEX_I7("Index i7", 13, + false), + INDEX_I5("Index i5", 14, false), COMMENT("Comment", 15, false), @@ -327,7 +327,6 @@ enum NGSMeasurementColumns { private final String headerName; private final int columnIndex; private final boolean readOnly; - private final List allowedValues; static int maxColumnIndex() { return Arrays.stream(values()) @@ -339,23 +338,11 @@ static int maxColumnIndex() { * @param headerName the name in the header * @param columnIndex the index of the column this property is in * @param readOnly is the property read only - * @param allowedValues a list of allowed values; null if not applicable */ - NGSMeasurementColumns(String headerName, int columnIndex, boolean readOnly, - List allowedValues) { + NGSMeasurementColumns(String headerName, int columnIndex, boolean readOnly) { this.headerName = headerName; this.columnIndex = columnIndex; this.readOnly = readOnly; - this.allowedValues = allowedValues; - } - - /** - * @param headerName the name in the header - * @param columnIndex the index of the column this property is in - * @param readOnly is the property read only - */ - NGSMeasurementColumns(String headerName, int columnIndex, boolean readOnly) { - this(headerName, columnIndex, readOnly, null); } public String headerName() { @@ -370,8 +357,5 @@ public boolean readOnly() { return readOnly; } - public List allowedValues() { - return Optional.ofNullable(allowedValues).orElse(Collections.emptyList()); - } } } diff --git a/user-interface/src/main/java/life/qbic/datamanager/views/projects/project/measurements/download/ProteomicsMeasurementContentProvider.java b/user-interface/src/main/java/life/qbic/datamanager/views/projects/project/measurements/download/ProteomicsMeasurementContentProvider.java index 285023830..90c29b92e 100644 --- a/user-interface/src/main/java/life/qbic/datamanager/views/projects/project/measurements/download/ProteomicsMeasurementContentProvider.java +++ b/user-interface/src/main/java/life/qbic/datamanager/views/projects/project/measurements/download/ProteomicsMeasurementContentProvider.java @@ -1,5 +1,11 @@ package life.qbic.datamanager.views.projects.project.measurements.download; +import static life.qbic.datamanager.spreadsheet.XLSXTemplateHelper.addDataValidation; +import static life.qbic.datamanager.spreadsheet.XLSXTemplateHelper.createOptionArea; +import static life.qbic.datamanager.spreadsheet.XLSXTemplateHelper.getOrCreateCell; +import static life.qbic.datamanager.spreadsheet.XLSXTemplateHelper.getOrCreateRow; +import static life.qbic.datamanager.spreadsheet.XLSXTemplateHelper.hideSheet; +import static life.qbic.datamanager.spreadsheet.XLSXTemplateHelper.lockSheet; import static life.qbic.logging.service.LoggerFactory.logger; import java.io.ByteArrayOutputStream; @@ -11,9 +17,13 @@ import life.qbic.datamanager.views.general.download.DownloadContentProvider; import life.qbic.datamanager.views.projects.project.measurements.ProteomicsMeasurementEntry; import life.qbic.logging.api.Logger; +import life.qbic.projectmanagement.application.measurement.ProteomicsMeasurementMetadata; +import life.qbic.projectmanagement.application.measurement.validation.MeasurementProteomicsValidator.DigestionMethod; +import life.qbic.projectmanagement.domain.model.measurement.ProteomicsMeasurement; import org.apache.poi.ss.usermodel.CellStyle; import org.apache.poi.ss.usermodel.FillPatternType; import org.apache.poi.ss.usermodel.Font; +import org.apache.poi.ss.usermodel.Name; import org.apache.poi.ss.usermodel.Row; import org.apache.poi.ss.usermodel.Sheet; import org.apache.poi.ss.usermodel.Workbook; @@ -25,8 +35,8 @@ /** Proteomics Measurement Content Provider *

* Implementation of the {@link DownloadContentProvider} providing the content and file name for any files created - * from {@link life.qbic.projectmanagement.domain.model.measurement.ProteomicsMeasurement} - * and {@link life.qbic.projectmanagement.application.measurement.ProteomicsMeasurementMetadata} + * from {@link ProteomicsMeasurement} + * and {@link ProteomicsMeasurementMetadata} *

*/ public class ProteomicsMeasurementContentProvider implements DownloadContentProvider { @@ -38,6 +48,8 @@ public class ProteomicsMeasurementContentProvider implements DownloadContentProv private final List measurements = new LinkedList<>(); private static final String DEFAULT_FILE_NAME_PREFIX = "QBiC"; private String fileNamePrefix = DEFAULT_FILE_NAME_PREFIX; + private static final int DEFAULT_GENERATED_ROW_COUNT = 200; + private static void setAutoWidth(Sheet sheet) { for (int col = 0; col <= 18; col++) { @@ -45,100 +57,36 @@ private static void setAutoWidth(Sheet sheet) { } } - private static void formatHeader(Row header, CellStyle readOnlyHeader, CellStyle boldStyle) { - var h1 = header.createCell(0); - h1.setCellValue("Measurement ID"); - h1.setCellStyle(readOnlyHeader); - - var h2 = header.createCell(1); - h2.setCellValue("QBiC Sample ID"); - h2.setCellStyle(readOnlyHeader); - - var h3 = header.createCell(2); - h3.setCellValue("Sample Name"); - h3.setCellStyle(readOnlyHeader); - - var h4 = header.createCell(3); - h4.setCellValue("Sample Pool Group"); - h4.setCellStyle(readOnlyHeader); - - var h5 = header.createCell(4); - h5.setCellValue("Organisation ID"); - h5.setCellStyle(boldStyle); - - var h6 = header.createCell(5); - h6.setCellValue("Organisation Name"); - h6.setCellStyle(readOnlyHeader); - - var h7 = header.createCell(6); - h7.setCellValue("Facility"); - h7.setCellStyle(boldStyle); - - var h8 = header.createCell(7); - h8.setCellValue("Instrument"); - h8.setCellStyle(boldStyle); - - var h9 = header.createCell(8); - h9.setCellValue("Instrument Name"); - h9.setCellStyle(readOnlyHeader); - - header.createCell(9).setCellValue("Cycle/Fraction Name"); - header.createCell(10).setCellValue("Digestion Method"); - header.createCell(11).setCellValue("Digestion Enzyme"); - header.createCell(12).setCellValue("Enrichment Method"); - header.createCell(13).setCellValue("Injection Volume (uL)"); - header.createCell(14).setCellValue("LC Column"); - header.createCell(15).setCellValue("LCMS Method"); - header.createCell(16).setCellValue("Labeling Type"); - header.createCell(17).setCellValue("Label"); - header.createCell(18).setCellValue("Comment"); - - for (int i = 9; i < 19; i++) { - header.getCell(i).setCellStyle(boldStyle); - } - } - - private static void createMeasurementEntry(ProteomicsMeasurementEntry pxpEntry, Row entry, + private static void createMeasurementEntry(ProteomicsMeasurementEntry pxpEntry, Row entryRow, CellStyle readOnlyStyle) { - var measureCol = entry.createCell(0); - measureCol.setCellValue(pxpEntry.measurementCode()); - measureCol.setCellStyle(readOnlyStyle); - - var sampleIdCol = entry.createCell(1); - sampleIdCol.setCellValue(pxpEntry.sampleInformation().sampleId()); - sampleIdCol.setCellStyle(readOnlyStyle); - - var sampleNameCol = entry.createCell(2); - sampleNameCol.setCellValue(pxpEntry.sampleInformation().sampleName()); - sampleNameCol.setCellStyle(readOnlyStyle); - - var samplePoolCol = entry.createCell(3); - samplePoolCol.setCellValue(pxpEntry.samplePoolGroup()); - samplePoolCol.setCellStyle(readOnlyStyle); - - entry.createCell(4).setCellValue(pxpEntry.organisationId()); - - var organisationNameCol = entry.createCell(5); - organisationNameCol.setCellValue(pxpEntry.organisationName()); - organisationNameCol.setCellStyle(readOnlyStyle); - - entry.createCell(6).setCellValue(pxpEntry.facility()); - entry.createCell(7).setCellValue(pxpEntry.instrumentCURI()); - - var instumentNameCol = entry.createCell(8); - instumentNameCol.setCellValue(pxpEntry.instrumentName()); - instumentNameCol.setCellStyle(readOnlyStyle); - - entry.createCell(9).setCellValue(pxpEntry.fractionName()); - entry.createCell(10).setCellValue(pxpEntry.digestionMethod()); - entry.createCell(11).setCellValue(pxpEntry.digestionEnzyme()); - entry.createCell(12).setCellValue(pxpEntry.enrichmentMethod()); - entry.createCell(13).setCellValue(Integer.parseInt(pxpEntry.injectionVolume())); - entry.createCell(14).setCellValue(pxpEntry.lcColumn()); - entry.createCell(15).setCellValue(pxpEntry.lcmsMethod()); - entry.createCell(16).setCellValue(pxpEntry.labelingType()); - entry.createCell(17).setCellValue(pxpEntry.label()); - entry.createCell(18).setCellValue(pxpEntry.comment()); + for (ProteomicsMeasurementColumns measurementColumn : ProteomicsMeasurementColumns.values()) { + var value = switch (measurementColumn) { + case MEASUREMENT_ID -> pxpEntry.measurementCode(); + case SAMPLE_ID -> pxpEntry.sampleInformation().sampleId(); + case SAMPLE_NAME -> pxpEntry.sampleInformation().sampleName(); + case POOL_GROUP -> pxpEntry.samplePoolGroup(); + case ORGANISATION_ID -> pxpEntry.organisationId(); + case ORGANISATION_NAME -> pxpEntry.organisationName(); + case FACILITY -> pxpEntry.facility(); + case MS_DEVICE -> pxpEntry.instrumentCURI(); + case MS_DEVICE_NAME -> pxpEntry.instrumentName(); + case CYCLE_FRACTION_NAME -> pxpEntry.fractionName(); + case DIGESTION_METHOD -> pxpEntry.digestionMethod(); + case DIGESTION_ENZYME -> pxpEntry.digestionEnzyme(); + case ENRICHMENT_METHOD -> pxpEntry.enrichmentMethod(); + case INJECTION_VOLUME -> pxpEntry.injectionVolume(); + case LC_COLUMN -> pxpEntry.lcColumn(); + case LCMS_METHOD -> pxpEntry.lcmsMethod(); + case LABELING_TYPE -> pxpEntry.labelingType(); + case LABEL -> pxpEntry.label(); + case COMMENT -> pxpEntry.comment(); + }; + var cell = getOrCreateCell(entryRow, measurementColumn.columnIndex()); + cell.setCellValue(value); + if (measurementColumn.readOnly()) { + cell.setCellStyle(readOnlyStyle); + } + } } public void setMeasurements(List measurements, String fileNamePrefix) { @@ -156,26 +104,21 @@ public byte[] getContent() { ByteArrayOutputStream byteArrayOutputStream; try (Workbook workbook = new XSSFWorkbook()) { - Sheet sheet = workbook.createSheet("Proteomics Measurement Metadata"); - Row header = sheet.createRow(0); - - CellStyle readOnlyHeader = workbook.createCellStyle(); - readOnlyHeader.setFillForegroundColor( + CellStyle readOnlyHeaderStyle = workbook.createCellStyle(); + readOnlyHeaderStyle.setFillForegroundColor( new XSSFColor(LIGHT_GREY, new DefaultIndexedColorMap())); - readOnlyHeader.setFillPattern(FillPatternType.SOLID_FOREGROUND); + readOnlyHeaderStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND); XSSFFont fontHeader = (XSSFFont) workbook.createFont(); fontHeader.setBold(true); fontHeader.setColor(new XSSFColor(DARK_GREY, new DefaultIndexedColorMap())); - readOnlyHeader.setFont(fontHeader); + readOnlyHeaderStyle.setFont(fontHeader); CellStyle boldStyle = workbook.createCellStyle(); Font fontBold = workbook.createFont(); fontBold.setBold(true); boldStyle.setFont(fontBold); - formatHeader(header, readOnlyHeader, boldStyle); - CellStyle readOnlyStyle = workbook.createCellStyle(); readOnlyStyle.setFillForegroundColor(new XSSFColor(LIGHT_GREY, new DefaultIndexedColorMap())); readOnlyStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND); @@ -183,15 +126,47 @@ public byte[] getContent() { font.setColor(new XSSFColor(DARK_GREY, new DefaultIndexedColorMap())); readOnlyStyle.setFont(font); - int rowCounter = 1; + Sheet hiddenSheet = workbook.createSheet("hidden"); + Name digestionMethodArea = createOptionArea(hiddenSheet, "Digestion Method", + DigestionMethod.getOptions()); + + Sheet sheet = workbook.createSheet("Proteomics Measurement Metadata"); + + Row header = getOrCreateRow(sheet, 0); + + for (ProteomicsMeasurementColumns measurementColumn : ProteomicsMeasurementColumns.values()) { + var cell = getOrCreateCell(header, measurementColumn.columnIndex()); + cell.setCellValue(measurementColumn.headerName()); + if (measurementColumn.readOnly()) { + cell.setCellStyle(readOnlyHeaderStyle); + } else { + cell.setCellStyle(boldStyle); + } + } + + var startIndex = 1; // start in row number 2 with index 1 skipping the header in the first row + var rowIndex = startIndex; for (ProteomicsMeasurementEntry pxpEntry : measurements) { - Row entry = sheet.createRow(rowCounter); + Row entry = getOrCreateRow(sheet, rowIndex); createMeasurementEntry(pxpEntry, entry, readOnlyStyle); - rowCounter++; + rowIndex++; } + var generatedRowCount = rowIndex - startIndex; + assert generatedRowCount == measurements.size() : "all measurements have a corresponding row"; + + addDataValidation(sheet, + ProteomicsMeasurementColumns.DIGESTION_METHOD.columnIndex(), startIndex, + ProteomicsMeasurementColumns.DIGESTION_METHOD.columnIndex(), + DEFAULT_GENERATED_ROW_COUNT - 1, + digestionMethodArea); setAutoWidth(sheet); + workbook.setSheetOrder(sheet.getSheetName(), 0); + workbook.setActiveSheet(0); + + lockSheet(hiddenSheet); + hideSheet(workbook, hiddenSheet); byteArrayOutputStream = new ByteArrayOutputStream(); workbook.write(byteArrayOutputStream); @@ -203,6 +178,52 @@ public byte[] getContent() { return byteArrayOutputStream.toByteArray(); } + enum ProteomicsMeasurementColumns { + + MEASUREMENT_ID("Measurement ID", 0, true), + SAMPLE_ID("QBiC Sample Id", 1, true), + SAMPLE_NAME( + "Sample Name", 2, true), + POOL_GROUP("Sample Pool Group", 3, true), + ORGANISATION_ID("Organisation ID", 4, false), + ORGANISATION_NAME("Organisation Name", 5, true), + FACILITY("Facility", 6, false), + MS_DEVICE("MS Device", 7, false), + MS_DEVICE_NAME("MS Device Name", 8, true), + CYCLE_FRACTION_NAME("Cycle/Fraction Name", 9, false), + DIGESTION_METHOD("Digestion Method", 10, false), + DIGESTION_ENZYME("Digestion Enzyme", 11, false), + ENRICHMENT_METHOD("Enrichment Method", 12, false), + INJECTION_VOLUME("Injection Volume (µL)", 13, false), + LC_COLUMN("LC Column", 14, false), + LCMS_METHOD("LCMS Method", 15, false), + LABELING_TYPE("Labeling Type", 16, false), + LABEL("Label", 17, false), + COMMENT("Comment", 18, false), + ; + private final String headerName; + private final int columnIndex; + private final boolean readOnly; + + ProteomicsMeasurementColumns(String headerName, int columnIndex, boolean readOnly) { + this.headerName = headerName; + this.columnIndex = columnIndex; + this.readOnly = readOnly; + } + + public String headerName() { + return headerName; + } + + public int columnIndex() { + return columnIndex; + } + + public boolean readOnly() { + return readOnly; + } + } + @Override public String getFileName() { return String.join("_", fileNamePrefix, FILE_NAME_SUFFIX); From f278e282b1d4c0749a6521301378079a26196b86 Mon Sep 17 00:00:00 2001 From: Tobias Koch Date: Mon, 16 Sep 2024 08:59:47 +0200 Subject: [PATCH 07/20] Make ngs row creation simpler Uses the enum and enhanced switch to make sure all columns are covered and removes complexity. --- .../NGSMeasurementContentProvider.java | 63 ++++++++++++++----- 1 file changed, 46 insertions(+), 17 deletions(-) diff --git a/user-interface/src/main/java/life/qbic/datamanager/views/projects/project/measurements/download/NGSMeasurementContentProvider.java b/user-interface/src/main/java/life/qbic/datamanager/views/projects/project/measurements/download/NGSMeasurementContentProvider.java index 540b2cfac..779e9a8b0 100644 --- a/user-interface/src/main/java/life/qbic/datamanager/views/projects/project/measurements/download/NGSMeasurementContentProvider.java +++ b/user-interface/src/main/java/life/qbic/datamanager/views/projects/project/measurements/download/NGSMeasurementContentProvider.java @@ -96,74 +96,103 @@ private static void setCellStyle(Cell cell, boolean isReadOnly) { } } - private static void writeMeasurementIntoRow(NGSMeasurementEntry ngsMeasurementEntry, Row entry) { - var measureCol = getOrCreateCell(entry, NGSMeasurementColumns.MEASUREMENT_ID.columnIndex()); + private static void writeMeasurementIntoRow(NGSMeasurementEntry ngsMeasurementEntry, + Row entryRow) { + var measureCol = getOrCreateCell(entryRow, NGSMeasurementColumns.MEASUREMENT_ID.columnIndex()); measureCol.setCellValue(ngsMeasurementEntry.measurementCode()); setCellStyle(measureCol, NGSMeasurementColumns.MEASUREMENT_ID.readOnly()); - var sampleIdCol = getOrCreateCell(entry, NGSMeasurementColumns.SAMPLE_ID.columnIndex()); + var sampleIdCol = getOrCreateCell(entryRow, NGSMeasurementColumns.SAMPLE_ID.columnIndex()); sampleIdCol.setCellValue(ngsMeasurementEntry.sampleInformation().sampleId()); setCellStyle(sampleIdCol, NGSMeasurementColumns.SAMPLE_ID.readOnly()); - var sampleNameCol = getOrCreateCell(entry, NGSMeasurementColumns.SAMPLE_NAME.columnIndex()); + var sampleNameCol = getOrCreateCell(entryRow, NGSMeasurementColumns.SAMPLE_NAME.columnIndex()); sampleNameCol.setCellValue(ngsMeasurementEntry.sampleInformation().sampleName()); setCellStyle(sampleNameCol, NGSMeasurementColumns.SAMPLE_NAME.readOnly()); - var orgIdCol = getOrCreateCell(entry, NGSMeasurementColumns.ORGANISATION_ID.columnIndex()); + var orgIdCol = getOrCreateCell(entryRow, NGSMeasurementColumns.ORGANISATION_ID.columnIndex()); orgIdCol.setCellValue(ngsMeasurementEntry.organisationId()); setCellStyle(orgIdCol, NGSMeasurementColumns.ORGANISATION_ID.readOnly()); - var organisationNameCol = getOrCreateCell(entry, + var organisationNameCol = getOrCreateCell(entryRow, NGSMeasurementColumns.ORGANISATION_NAME.columnIndex()); organisationNameCol.setCellValue(ngsMeasurementEntry.organisationName()); setCellStyle(organisationNameCol, NGSMeasurementColumns.ORGANISATION_NAME.readOnly()); - var facilityCol = getOrCreateCell(entry, NGSMeasurementColumns.FACILITY.columnIndex()); + var facilityCol = getOrCreateCell(entryRow, NGSMeasurementColumns.FACILITY.columnIndex()); facilityCol.setCellValue(ngsMeasurementEntry.facility()); setCellStyle(facilityCol, NGSMeasurementColumns.FACILITY.readOnly); - var instrumentCol = getOrCreateCell(entry, NGSMeasurementColumns.INSTRUMENT.columnIndex()); + var instrumentCol = getOrCreateCell(entryRow, NGSMeasurementColumns.INSTRUMENT.columnIndex()); instrumentCol.setCellValue(ngsMeasurementEntry.instrumentCURI()); setCellStyle(instrumentCol, NGSMeasurementColumns.INSTRUMENT.readOnly()); - var instrumentNameCol = getOrCreateCell(entry, + var instrumentNameCol = getOrCreateCell(entryRow, NGSMeasurementColumns.INSTRUMENT_NAME.columnIndex()); instrumentNameCol.setCellValue(ngsMeasurementEntry.instrumentName()); setCellStyle(instrumentNameCol, NGSMeasurementColumns.INSTRUMENT_NAME.readOnly()); - var readTypeCol = getOrCreateCell(entry, + var readTypeCol = getOrCreateCell(entryRow, NGSMeasurementColumns.SEQUENCING_READ_TYPE.columnIndex()); readTypeCol.setCellValue(ngsMeasurementEntry.readType()); setCellStyle(readTypeCol, NGSMeasurementColumns.SEQUENCING_READ_TYPE.readOnly()); - var libraryKitCol = getOrCreateCell(entry, NGSMeasurementColumns.LIBRARY_KIT.columnIndex()); + var libraryKitCol = getOrCreateCell(entryRow, NGSMeasurementColumns.LIBRARY_KIT.columnIndex()); libraryKitCol.setCellValue(ngsMeasurementEntry.libraryKit()); setCellStyle(libraryKitCol, NGSMeasurementColumns.LIBRARY_KIT.readOnly()); - var flowCellCol = getOrCreateCell(entry, NGSMeasurementColumns.FLOW_CELL.columnIndex()); + var flowCellCol = getOrCreateCell(entryRow, NGSMeasurementColumns.FLOW_CELL.columnIndex()); flowCellCol.setCellValue(ngsMeasurementEntry.flowCell()); setCellStyle(flowCellCol, NGSMeasurementColumns.FLOW_CELL.readOnly()); - var runProtocolCol = getOrCreateCell(entry, + var runProtocolCol = getOrCreateCell(entryRow, NGSMeasurementColumns.SEQUENCING_RUN_PROTOCOL.columnIndex()); runProtocolCol.setCellValue(ngsMeasurementEntry.runProtocol()); setCellStyle(runProtocolCol, NGSMeasurementColumns.SEQUENCING_RUN_PROTOCOL.readOnly()); - var poolGroupCol = getOrCreateCell(entry, NGSMeasurementColumns.POOL_GROUP.columnIndex()); + var poolGroupCol = getOrCreateCell(entryRow, NGSMeasurementColumns.POOL_GROUP.columnIndex()); poolGroupCol.setCellValue(ngsMeasurementEntry.samplePoolGroup()); setCellStyle(poolGroupCol, NGSMeasurementColumns.POOL_GROUP.readOnly()); - var indexI7Col = getOrCreateCell(entry, NGSMeasurementColumns.INDEX_I7.columnIndex()); + var indexI7Col = getOrCreateCell(entryRow, NGSMeasurementColumns.INDEX_I7.columnIndex()); indexI7Col.setCellValue(ngsMeasurementEntry.indexI7()); setCellStyle(indexI7Col, NGSMeasurementColumns.INDEX_I7.readOnly()); - var indexI5Col = getOrCreateCell(entry, NGSMeasurementColumns.INDEX_I5.columnIndex()); + var indexI5Col = getOrCreateCell(entryRow, NGSMeasurementColumns.INDEX_I5.columnIndex()); indexI5Col.setCellValue(ngsMeasurementEntry.indexI5()); setCellStyle(indexI5Col, NGSMeasurementColumns.INDEX_I5.readOnly()); - var commentCol = getOrCreateCell(entry, NGSMeasurementColumns.COMMENT.columnIndex()); + var commentCol = getOrCreateCell(entryRow, NGSMeasurementColumns.COMMENT.columnIndex()); commentCol.setCellValue(ngsMeasurementEntry.comment()); setCellStyle(commentCol, NGSMeasurementColumns.COMMENT.readOnly()); + + for (NGSMeasurementColumns measurementColumn : NGSMeasurementColumns.values()) { + var value = switch (measurementColumn) { + case MEASUREMENT_ID -> ngsMeasurementEntry.measurementCode(); + case SAMPLE_ID -> ngsMeasurementEntry.sampleInformation().sampleId(); + case SAMPLE_NAME -> ngsMeasurementEntry.sampleInformation().sampleName(); + case POOL_GROUP -> ngsMeasurementEntry.samplePoolGroup(); + case ORGANISATION_ID -> ngsMeasurementEntry.organisationId(); + case ORGANISATION_NAME -> ngsMeasurementEntry.organisationName(); + case FACILITY -> ngsMeasurementEntry.facility(); + case INSTRUMENT -> ngsMeasurementEntry.instrumentCURI(); + case INSTRUMENT_NAME -> ngsMeasurementEntry.instrumentName(); + case SEQUENCING_READ_TYPE -> ngsMeasurementEntry.readType(); + case LIBRARY_KIT -> ngsMeasurementEntry.libraryKit(); + case FLOW_CELL -> ngsMeasurementEntry.flowCell(); + case SEQUENCING_RUN_PROTOCOL -> ngsMeasurementEntry.runProtocol(); + case INDEX_I7 -> ngsMeasurementEntry.indexI7(); + case INDEX_I5 -> ngsMeasurementEntry.indexI5(); + case COMMENT -> ngsMeasurementEntry.comment(); + }; + var cell = getOrCreateCell(entryRow, measurementColumn.columnIndex()); + cell.setCellValue(value); + if (measurementColumn.readOnly()) { + cell.setCellStyle(readOnlyCellStyle); + } + } + + } public void setMeasurements(List measurements, String fileNamePrefix) { From 9ce0bc37250793dcbf0951ad4a9162f7e9d76ed7 Mon Sep 17 00:00:00 2001 From: Tobias Koch Date: Mon, 16 Sep 2024 09:00:21 +0200 Subject: [PATCH 08/20] remove duplicate row creation for ngs --- .../NGSMeasurementContentProvider.java | 67 ------------------- 1 file changed, 67 deletions(-) diff --git a/user-interface/src/main/java/life/qbic/datamanager/views/projects/project/measurements/download/NGSMeasurementContentProvider.java b/user-interface/src/main/java/life/qbic/datamanager/views/projects/project/measurements/download/NGSMeasurementContentProvider.java index 779e9a8b0..4cc6981af 100644 --- a/user-interface/src/main/java/life/qbic/datamanager/views/projects/project/measurements/download/NGSMeasurementContentProvider.java +++ b/user-interface/src/main/java/life/qbic/datamanager/views/projects/project/measurements/download/NGSMeasurementContentProvider.java @@ -98,73 +98,6 @@ private static void setCellStyle(Cell cell, boolean isReadOnly) { private static void writeMeasurementIntoRow(NGSMeasurementEntry ngsMeasurementEntry, Row entryRow) { - var measureCol = getOrCreateCell(entryRow, NGSMeasurementColumns.MEASUREMENT_ID.columnIndex()); - measureCol.setCellValue(ngsMeasurementEntry.measurementCode()); - setCellStyle(measureCol, NGSMeasurementColumns.MEASUREMENT_ID.readOnly()); - - var sampleIdCol = getOrCreateCell(entryRow, NGSMeasurementColumns.SAMPLE_ID.columnIndex()); - sampleIdCol.setCellValue(ngsMeasurementEntry.sampleInformation().sampleId()); - setCellStyle(sampleIdCol, NGSMeasurementColumns.SAMPLE_ID.readOnly()); - - var sampleNameCol = getOrCreateCell(entryRow, NGSMeasurementColumns.SAMPLE_NAME.columnIndex()); - sampleNameCol.setCellValue(ngsMeasurementEntry.sampleInformation().sampleName()); - setCellStyle(sampleNameCol, NGSMeasurementColumns.SAMPLE_NAME.readOnly()); - - var orgIdCol = getOrCreateCell(entryRow, NGSMeasurementColumns.ORGANISATION_ID.columnIndex()); - orgIdCol.setCellValue(ngsMeasurementEntry.organisationId()); - setCellStyle(orgIdCol, NGSMeasurementColumns.ORGANISATION_ID.readOnly()); - - var organisationNameCol = getOrCreateCell(entryRow, - NGSMeasurementColumns.ORGANISATION_NAME.columnIndex()); - - organisationNameCol.setCellValue(ngsMeasurementEntry.organisationName()); - setCellStyle(organisationNameCol, NGSMeasurementColumns.ORGANISATION_NAME.readOnly()); - - var facilityCol = getOrCreateCell(entryRow, NGSMeasurementColumns.FACILITY.columnIndex()); - facilityCol.setCellValue(ngsMeasurementEntry.facility()); - setCellStyle(facilityCol, NGSMeasurementColumns.FACILITY.readOnly); - var instrumentCol = getOrCreateCell(entryRow, NGSMeasurementColumns.INSTRUMENT.columnIndex()); - instrumentCol.setCellValue(ngsMeasurementEntry.instrumentCURI()); - setCellStyle(instrumentCol, NGSMeasurementColumns.INSTRUMENT.readOnly()); - - var instrumentNameCol = getOrCreateCell(entryRow, - NGSMeasurementColumns.INSTRUMENT_NAME.columnIndex()); - instrumentNameCol.setCellValue(ngsMeasurementEntry.instrumentName()); - setCellStyle(instrumentNameCol, NGSMeasurementColumns.INSTRUMENT_NAME.readOnly()); - - var readTypeCol = getOrCreateCell(entryRow, - NGSMeasurementColumns.SEQUENCING_READ_TYPE.columnIndex()); - readTypeCol.setCellValue(ngsMeasurementEntry.readType()); - setCellStyle(readTypeCol, NGSMeasurementColumns.SEQUENCING_READ_TYPE.readOnly()); - - var libraryKitCol = getOrCreateCell(entryRow, NGSMeasurementColumns.LIBRARY_KIT.columnIndex()); - libraryKitCol.setCellValue(ngsMeasurementEntry.libraryKit()); - setCellStyle(libraryKitCol, NGSMeasurementColumns.LIBRARY_KIT.readOnly()); - - var flowCellCol = getOrCreateCell(entryRow, NGSMeasurementColumns.FLOW_CELL.columnIndex()); - flowCellCol.setCellValue(ngsMeasurementEntry.flowCell()); - setCellStyle(flowCellCol, NGSMeasurementColumns.FLOW_CELL.readOnly()); - - var runProtocolCol = getOrCreateCell(entryRow, - NGSMeasurementColumns.SEQUENCING_RUN_PROTOCOL.columnIndex()); - runProtocolCol.setCellValue(ngsMeasurementEntry.runProtocol()); - setCellStyle(runProtocolCol, NGSMeasurementColumns.SEQUENCING_RUN_PROTOCOL.readOnly()); - - var poolGroupCol = getOrCreateCell(entryRow, NGSMeasurementColumns.POOL_GROUP.columnIndex()); - poolGroupCol.setCellValue(ngsMeasurementEntry.samplePoolGroup()); - setCellStyle(poolGroupCol, NGSMeasurementColumns.POOL_GROUP.readOnly()); - - var indexI7Col = getOrCreateCell(entryRow, NGSMeasurementColumns.INDEX_I7.columnIndex()); - indexI7Col.setCellValue(ngsMeasurementEntry.indexI7()); - setCellStyle(indexI7Col, NGSMeasurementColumns.INDEX_I7.readOnly()); - - var indexI5Col = getOrCreateCell(entryRow, NGSMeasurementColumns.INDEX_I5.columnIndex()); - indexI5Col.setCellValue(ngsMeasurementEntry.indexI5()); - setCellStyle(indexI5Col, NGSMeasurementColumns.INDEX_I5.readOnly()); - - var commentCol = getOrCreateCell(entryRow, NGSMeasurementColumns.COMMENT.columnIndex()); - commentCol.setCellValue(ngsMeasurementEntry.comment()); - setCellStyle(commentCol, NGSMeasurementColumns.COMMENT.readOnly()); for (NGSMeasurementColumns measurementColumn : NGSMeasurementColumns.values()) { var value = switch (measurementColumn) { From c5b7496d98879f156cc076e6396c1f1e02be9247 Mon Sep 17 00:00:00 2001 From: Tobias Koch Date: Mon, 16 Sep 2024 09:03:11 +0200 Subject: [PATCH 09/20] Rename `Instrument` to `MS Device` in template --- .../datamanager/parser/MetadataConverter.java | 10 +++++----- ...eomics_measurement_registration_sheet.xlsx | Bin 13366 -> 13301 bytes 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/user-interface/src/main/java/life/qbic/datamanager/parser/MetadataConverter.java b/user-interface/src/main/java/life/qbic/datamanager/parser/MetadataConverter.java index d69933cd5..e0f917cc4 100644 --- a/user-interface/src/main/java/life/qbic/datamanager/parser/MetadataConverter.java +++ b/user-interface/src/main/java/life/qbic/datamanager/parser/MetadataConverter.java @@ -7,12 +7,12 @@ import static life.qbic.datamanager.parser.MetadataConverter.ProteomicsMeasurementProperty.ENRICHMENT_METHOD; import static life.qbic.datamanager.parser.MetadataConverter.ProteomicsMeasurementProperty.FACILITY; import static life.qbic.datamanager.parser.MetadataConverter.ProteomicsMeasurementProperty.INJECTION_VOLUME; -import static life.qbic.datamanager.parser.MetadataConverter.ProteomicsMeasurementProperty.INSTRUMENT; import static life.qbic.datamanager.parser.MetadataConverter.ProteomicsMeasurementProperty.LABEL; import static life.qbic.datamanager.parser.MetadataConverter.ProteomicsMeasurementProperty.LABELING_TYPE; import static life.qbic.datamanager.parser.MetadataConverter.ProteomicsMeasurementProperty.LCMS_METHOD; import static life.qbic.datamanager.parser.MetadataConverter.ProteomicsMeasurementProperty.LC_COLUMN; import static life.qbic.datamanager.parser.MetadataConverter.ProteomicsMeasurementProperty.MEASUREMENT_ID; +import static life.qbic.datamanager.parser.MetadataConverter.ProteomicsMeasurementProperty.MS_DEVICE; import static life.qbic.datamanager.parser.MetadataConverter.ProteomicsMeasurementProperty.ORGANISATION_ID; import static life.qbic.datamanager.parser.MetadataConverter.ProteomicsMeasurementProperty.QBIC_SAMPLE_ID; import static life.qbic.datamanager.parser.MetadataConverter.ProteomicsMeasurementProperty.SAMPLE_POOL_GROUP; @@ -140,7 +140,7 @@ private List convertProteomicsMeasurement(ParsingResult par "")), safeListAccess(row.values(), keyIndices.getOrDefault(ORGANISATION_ID.propertyName(), -1), ""), - safeListAccess(row.values(), keyIndices.getOrDefault(INSTRUMENT.propertyName(), -1), ""), + safeListAccess(row.values(), keyIndices.getOrDefault(MS_DEVICE.propertyName(), -1), ""), safeListAccess(row.values(), keyIndices.getOrDefault(SAMPLE_POOL_GROUP.propertyName(), -1), ""), @@ -189,7 +189,7 @@ private List convertNGSMeasurement(ParsingResult parsingRes ""))), safeListAccess(row.values(), keyIndices.getOrDefault(ORGANISATION_ID.propertyName(), -1), ""), - safeListAccess(row.values(), keyIndices.getOrDefault(INSTRUMENT.propertyName(), -1), ""), + safeListAccess(row.values(), keyIndices.getOrDefault(MS_DEVICE.propertyName(), -1), ""), safeListAccess(row.values(), keyIndices.getOrDefault(FACILITY.propertyName(), -1), ""), safeListAccess(row.values(), keyIndices.getOrDefault( NGSMeasurementProperty.SEQUENCING_READ_TYPE.propertyName(), -1), ""), @@ -284,12 +284,12 @@ enum ProteomicsMeasurementProperty { SAMPLE_POOL_GROUP("sample pool group"), ORGANISATION_ID("organisation id"), FACILITY("facility"), - INSTRUMENT("instrument"), + MS_DEVICE("ms device"), CYCLE("cycle/fraction name"), DIGESTION_METHOD("digestion method"), DIGESTION_ENZYME("digestion enzyme"), ENRICHMENT_METHOD("enrichment method"), - INJECTION_VOLUME("injection volume (ul)"), + INJECTION_VOLUME("injection volume (µl)"), LC_COLUMN("lc column"), LCMS_METHOD("lcms method"), LABELING_TYPE("labeling type"), diff --git a/user-interface/src/main/resources/templates/proteomics_measurement_registration_sheet.xlsx b/user-interface/src/main/resources/templates/proteomics_measurement_registration_sheet.xlsx index fa8406e3bb1435662bae0a584e823c1bc3767dfc..c28d67eeffe3a17af99ddf27b3198510b0f5f2c1 100644 GIT binary patch delta 7029 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 J#`Rn5{{RZX^2z`J delta 7091 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 kAxeDAlz&eA;|(0%f7ppRAaQ&z5aJ Date: Mon, 16 Sep 2024 09:30:41 +0200 Subject: [PATCH 10/20] Fix column name --- ...eomics_measurement_registration_sheet.xlsx | Bin 13301 -> 13314 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/user-interface/src/main/resources/templates/proteomics_measurement_registration_sheet.xlsx b/user-interface/src/main/resources/templates/proteomics_measurement_registration_sheet.xlsx index c28d67eeffe3a17af99ddf27b3198510b0f5f2c1..70852eedca85a6d989df71c6500286588c0a3a34 100644 GIT binary patch delta 4439 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 From ea44f524e539a8b7b76fe3850de98fac22066d96 Mon Sep 17 00:00:00 2001 From: Tobias Koch Date: Mon, 16 Sep 2024 10:00:35 +0200 Subject: [PATCH 11/20] Fix sheet locking Excel protected the visible sheet. I do not know why and can only speculate that some reordering was not complete. Creating the hidden sheet second solves the issue. --- .../download/NGSMeasurementContentProvider.java | 11 +++++------ .../ProteomicsMeasurementContentProvider.java | 12 +++++------- 2 files changed, 10 insertions(+), 13 deletions(-) diff --git a/user-interface/src/main/java/life/qbic/datamanager/views/projects/project/measurements/download/NGSMeasurementContentProvider.java b/user-interface/src/main/java/life/qbic/datamanager/views/projects/project/measurements/download/NGSMeasurementContentProvider.java index 4cc6981af..493a83448 100644 --- a/user-interface/src/main/java/life/qbic/datamanager/views/projects/project/measurements/download/NGSMeasurementContentProvider.java +++ b/user-interface/src/main/java/life/qbic/datamanager/views/projects/project/measurements/download/NGSMeasurementContentProvider.java @@ -176,11 +176,6 @@ public byte[] getContent() { defineReadOnlyCellStyle(workbook); defineBoldStyle(workbook); - Sheet hiddenSheet = workbook.createSheet("hidden"); - - Name sequencingReadTypeArea = createOptionArea(hiddenSheet, - "Sequencing read type", SequencingReadType.getOptions()); - Sheet sheet = workbook.createSheet("NGS Measurement Metadata"); Row header = getOrCreateRow(sheet, 0); @@ -201,6 +196,11 @@ public byte[] getContent() { var generatedRowCount = rowIndex - startIndex; assert generatedRowCount == measurements.size() : "all measurements have a corresponding row"; + // make sure to create the visible sheet first + Sheet hiddenSheet = workbook.createSheet("hidden"); + Name sequencingReadTypeArea = createOptionArea(hiddenSheet, + "Sequencing read type", SequencingReadType.getOptions()); + XLSXTemplateHelper.addDataValidation(sheet, NGSMeasurementColumns.SEQUENCING_READ_TYPE.columnIndex(), startIndex, @@ -209,7 +209,6 @@ public byte[] getContent() { sequencingReadTypeArea); setAutoWidth(sheet); - workbook.setSheetOrder(sheet.getSheetName(), 0); workbook.setActiveSheet(0); lockSheet(hiddenSheet); diff --git a/user-interface/src/main/java/life/qbic/datamanager/views/projects/project/measurements/download/ProteomicsMeasurementContentProvider.java b/user-interface/src/main/java/life/qbic/datamanager/views/projects/project/measurements/download/ProteomicsMeasurementContentProvider.java index 90c29b92e..7512f8113 100644 --- a/user-interface/src/main/java/life/qbic/datamanager/views/projects/project/measurements/download/ProteomicsMeasurementContentProvider.java +++ b/user-interface/src/main/java/life/qbic/datamanager/views/projects/project/measurements/download/ProteomicsMeasurementContentProvider.java @@ -126,14 +126,8 @@ public byte[] getContent() { font.setColor(new XSSFColor(DARK_GREY, new DefaultIndexedColorMap())); readOnlyStyle.setFont(font); - Sheet hiddenSheet = workbook.createSheet("hidden"); - Name digestionMethodArea = createOptionArea(hiddenSheet, "Digestion Method", - DigestionMethod.getOptions()); - Sheet sheet = workbook.createSheet("Proteomics Measurement Metadata"); - Row header = getOrCreateRow(sheet, 0); - for (ProteomicsMeasurementColumns measurementColumn : ProteomicsMeasurementColumns.values()) { var cell = getOrCreateCell(header, measurementColumn.columnIndex()); cell.setCellValue(measurementColumn.headerName()); @@ -155,6 +149,11 @@ public byte[] getContent() { var generatedRowCount = rowIndex - startIndex; assert generatedRowCount == measurements.size() : "all measurements have a corresponding row"; + // make sure to create the visible sheet first + Sheet hiddenSheet = workbook.createSheet("hidden"); + Name digestionMethodArea = createOptionArea(hiddenSheet, "Digestion Method", + DigestionMethod.getOptions()); + addDataValidation(sheet, ProteomicsMeasurementColumns.DIGESTION_METHOD.columnIndex(), startIndex, ProteomicsMeasurementColumns.DIGESTION_METHOD.columnIndex(), @@ -162,7 +161,6 @@ public byte[] getContent() { digestionMethodArea); setAutoWidth(sheet); - workbook.setSheetOrder(sheet.getSheetName(), 0); workbook.setActiveSheet(0); lockSheet(hiddenSheet); From 5965ca5a075ae2e8b58ea57a421c20caefc4c0f9 Mon Sep 17 00:00:00 2001 From: Tobias Koch Date: Mon, 16 Sep 2024 10:32:52 +0200 Subject: [PATCH 12/20] replace instrument by ms device for proteomics --- .../measurement/MeasurementService.java | 18 +++++++-------- .../ProteomicsMeasurementMetadata.java | 22 +------------------ .../MeasurementProteomicsValidator.java | 22 +++++++++---------- .../model/measurement/NGSMeasurement.java | 6 ++--- .../measurement/ProteomicsMeasurement.java | 14 ++++++------ .../measurement/ProteomicsMethodMetadata.java | 2 +- .../MeasurementProteomicsValidatorSpec.groovy | 14 ++++++------ .../MeasurementDetailsComponent.java | 6 ++--- .../measurements/MeasurementPresenter.java | 4 ++-- .../ProteomicsMeasurementEntry.java | 4 ++-- .../ProteomicsMeasurementContentProvider.java | 4 ++-- 11 files changed, 48 insertions(+), 68 deletions(-) diff --git a/project-management/src/main/java/life/qbic/projectmanagement/application/measurement/MeasurementService.java b/project-management/src/main/java/life/qbic/projectmanagement/application/measurement/MeasurementService.java index 8850df4ac..916e7ae68 100644 --- a/project-management/src/main/java/life/qbic/projectmanagement/application/measurement/MeasurementService.java +++ b/project-management/src/main/java/life/qbic/projectmanagement/application/measurement/MeasurementService.java @@ -389,12 +389,12 @@ private Map> buildPxP( throw new MeasurementRegistrationException(ErrorCode.UNKNOWN_ORGANISATION_ROR_ID); } - var instrumentQuery = resolveOntologyCURI(firstMetadataEntry.instrumentCURI()); - if (instrumentQuery.isEmpty()) { + var msDeviceQuery = resolveOntologyCURI(firstMetadataEntry.msDeviceCURIE()); + if (msDeviceQuery.isEmpty()) { throw new MeasurementRegistrationException(ErrorCode.UNKNOWN_ONTOLOGY_TERM); } - var method = new ProteomicsMethodMetadata(instrumentQuery.get(), firstMetadataEntry.facility(), + var method = new ProteomicsMethodMetadata(msDeviceQuery.get(), firstMetadataEntry.facility(), firstMetadataEntry.digestionMethod(), firstMetadataEntry.digestionEnzyme(), firstMetadataEntry.enrichmentMethod(), firstMetadataEntry.lcColumn(), firstMetadataEntry.lcmsMethod(), readInjectionVolume(firstMetadataEntry.injectionVolume()), @@ -611,12 +611,12 @@ private List> updateAllPxP( throw new MeasurementRegistrationException(ErrorCode.UNKNOWN_ORGANISATION_ROR_ID); } - var instrumentQuery = resolveOntologyCURI(measurementMetadata.instrumentCURI()); - if (instrumentQuery.isEmpty()) { + var msDeviceQuery = resolveOntologyCURI(measurementMetadata.msDeviceCURIE()); + if (msDeviceQuery.isEmpty()) { throw new MeasurementRegistrationException(ErrorCode.UNKNOWN_ONTOLOGY_TERM); } - var method = new ProteomicsMethodMetadata(instrumentQuery.get(), + var method = new ProteomicsMethodMetadata(msDeviceQuery.get(), measurementMetadata.facility(), measurementMetadata.digestionMethod(), measurementMetadata.digestionEnzyme(), measurementMetadata.enrichmentMethod(), measurementMetadata.lcColumn(), @@ -642,12 +642,12 @@ private List> updateAllPxP( throw new MeasurementRegistrationException(ErrorCode.UNKNOWN_ORGANISATION_ROR_ID); } - var instrumentQuery = resolveOntologyCURI(firstEntry.instrumentCURI()); - if (instrumentQuery.isEmpty()) { + var msDeviceQuery = resolveOntologyCURI(firstEntry.msDeviceCURIE()); + if (msDeviceQuery.isEmpty()) { throw new MeasurementRegistrationException(ErrorCode.UNKNOWN_ONTOLOGY_TERM); } - var method = new ProteomicsMethodMetadata(instrumentQuery.get(), firstEntry.facility(), + var method = new ProteomicsMethodMetadata(msDeviceQuery.get(), firstEntry.facility(), firstEntry.digestionMethod(), firstEntry.digestionEnzyme(), firstEntry.enrichmentMethod(), firstEntry.lcColumn(), firstEntry.lcmsMethod(), readInjectionVolume(firstEntry.injectionVolume()), diff --git a/project-management/src/main/java/life/qbic/projectmanagement/application/measurement/ProteomicsMeasurementMetadata.java b/project-management/src/main/java/life/qbic/projectmanagement/application/measurement/ProteomicsMeasurementMetadata.java index 690c434b5..06dc450fc 100644 --- a/project-management/src/main/java/life/qbic/projectmanagement/application/measurement/ProteomicsMeasurementMetadata.java +++ b/project-management/src/main/java/life/qbic/projectmanagement/application/measurement/ProteomicsMeasurementMetadata.java @@ -12,7 +12,7 @@ */ public record ProteomicsMeasurementMetadata(String measurementId, SampleCode sampleCode, - String organisationId, String instrumentCURI, + String organisationId, String msDeviceCURIE, String samplePoolGroup, String facility, String fractionName, String digestionEnzyme, @@ -22,26 +22,6 @@ public record ProteomicsMeasurementMetadata(String measurementId, String comment) implements MeasurementMetadata { - public static ProteomicsMeasurementMetadata copyWithNewProperties(SampleCode associatedSample, - Labeling labeling, - ProteomicsMeasurementMetadata metadata) { - return new ProteomicsMeasurementMetadata(metadata.measurementId(), - associatedSample, - metadata.organisationId(), - metadata.instrumentCURI(), - metadata.samplePoolGroup(), - metadata.facility(), - metadata.fractionName(), - metadata.digestionEnzyme(), - metadata.digestionMethod(), - metadata.enrichmentMethod(), - metadata.injectionVolume(), - metadata.lcColumn(), - metadata.lcmsMethod(), - labeling, - metadata.comment()); - } - @Override public Optional assignedSamplePoolGroup() { return Optional.ofNullable(samplePoolGroup.isBlank() ? null : samplePoolGroup); diff --git a/project-management/src/main/java/life/qbic/projectmanagement/application/measurement/validation/MeasurementProteomicsValidator.java b/project-management/src/main/java/life/qbic/projectmanagement/application/measurement/validation/MeasurementProteomicsValidator.java index 696054645..5b1798db0 100644 --- a/project-management/src/main/java/life/qbic/projectmanagement/application/measurement/validation/MeasurementProteomicsValidator.java +++ b/project-management/src/main/java/life/qbic/projectmanagement/application/measurement/validation/MeasurementProteomicsValidator.java @@ -102,7 +102,7 @@ public ValidationResult validate(ProteomicsMeasurementMetadata measurementMetada return validationPolicy.validateSampleId(measurementMetadata.sampleCode()) .combine(validationPolicy.validateMandatoryDataProvided(measurementMetadata)) .combine(validationPolicy.validateOrganisation(measurementMetadata.organisationId()) - .combine(validationPolicy.validateInstrument(measurementMetadata.instrumentCURI()))); + .combine(validationPolicy.validateMsDevice(measurementMetadata.msDeviceCURIE()))); } /** @@ -121,7 +121,7 @@ public ValidationResult validateUpdate(ProteomicsMeasurementMetadata metadata, .combine(validationPolicy.validateMeasurementCode(metadata.measurementIdentifier().orElse("")) .combine(validationPolicy.validateMandatoryDataForUpdate(metadata)) .combine(validationPolicy.validateOrganisation(metadata.organisationId()) - .combine(validationPolicy.validateInstrument(metadata.instrumentCURI()) + .combine(validationPolicy.validateMsDevice(metadata.msDeviceCURIE()) .combine( validationPolicy.validateDigestionMethod(metadata.digestionMethod()))))); } @@ -131,7 +131,7 @@ public enum PROTEOMICS_PROPERTY { SAMPLE_LABEL("sample name"), ORGANISATION_ID("organisation id"), FACILITY("facility"), - INSTRUMENT("instrument"), + MS_DEVICE("msDevice"), SAMPLE_POOL_GROUP("sample pool group"), CYCLE_FRACTION_NAME("cycle/fraction name"), DIGESTION_METHOD("digestion method"), @@ -192,7 +192,7 @@ private class ValidationPolicy { private static final String UNKNOWN_ORGANISATION_ID_MESSAGE = "The organisation ID does not seem to be a ROR ID: \"%s\""; - private static final String UNKNOWN_INSTRUMENT_ID = "Unknown instrument id: \"%s\""; + private static final String UNKNOWN_MS_DEVICE_ID = "Unknown ms device id: \"%s\""; private static final String UNKNOWN_DIGESTION_METHOD = "Unknown digestion method: \"%s\""; @@ -246,13 +246,13 @@ ValidationResult validateMeasurementCode(String measurementCode) { List.of("Measurement Code: Unknown measurement for id '%s'".formatted(measurementCode)))); } - ValidationResult validateInstrument(String instrument) { - var result = terminologyService.findByCurie(instrument); + ValidationResult validateMsDevice(String msDevice) { + var result = terminologyService.findByCurie(msDevice); if (result.isPresent()) { return ValidationResult.successful(1); } return ValidationResult.withFailures(1, - List.of(UNKNOWN_INSTRUMENT_ID.formatted(instrument))); + List.of(UNKNOWN_MS_DEVICE_ID.formatted(msDevice))); } ValidationResult validateDigestionMethod(String digestionMethod) { @@ -277,9 +277,9 @@ ValidationResult validateMandatoryDataForUpdate(ProteomicsMeasurementMetadata me } else { validation = validation.combine(ValidationResult.successful(1)); } - if (metadata.instrumentCURI().isBlank()) { + if (metadata.msDeviceCURIE().isBlank()) { validation = validation.combine( - ValidationResult.withFailures(1, List.of("Instrument: missing mandatory metadata"))); + ValidationResult.withFailures(1, List.of("MS Device: missing mandatory metadata"))); } else { validation = validation.combine(ValidationResult.successful(1)); } @@ -327,10 +327,10 @@ ValidationResult validateMandatoryDataProvided( } else { validation = validation.combine(ValidationResult.successful(1)); } - if (metadata.instrumentCURI().isBlank()) { + if (metadata.msDeviceCURIE().isBlank()) { validation = validation.combine( ValidationResult.withFailures(1, - List.of("Instrument: missing mandatory metadata"))); + List.of("MS Device: missing mandatory metadata"))); } else { validation = validation.combine(ValidationResult.successful(1)); } diff --git a/project-management/src/main/java/life/qbic/projectmanagement/domain/model/measurement/NGSMeasurement.java b/project-management/src/main/java/life/qbic/projectmanagement/domain/model/measurement/NGSMeasurement.java index dc6f1deb8..2c8f7455d 100644 --- a/project-management/src/main/java/life/qbic/projectmanagement/domain/model/measurement/NGSMeasurement.java +++ b/project-management/src/main/java/life/qbic/projectmanagement/domain/model/measurement/NGSMeasurement.java @@ -18,8 +18,8 @@ import java.util.List; import java.util.Objects; import java.util.Optional; -import life.qbic.domain.concepts.LocalDomainEventDispatcher; import java.util.Set; +import life.qbic.domain.concepts.LocalDomainEventDispatcher; import life.qbic.projectmanagement.domain.Organisation; import life.qbic.projectmanagement.domain.model.OntologyTerm; import life.qbic.projectmanagement.domain.model.measurement.event.MeasurementCreatedEvent; @@ -93,7 +93,7 @@ private NGSMeasurement(MeasurementId measurementId, ProjectId projectId, this.measurementId = measurementId; this.projectId = requireNonNull(projectId, "projectId must not be null"); this.organisation = requireNonNull(organisation, "organisation must not be null"); - this.instrument = requireNonNull(method.instrument(), "instrument must not be null"); + this.instrument = requireNonNull(method.instrument(), "msDevice must not be null"); this.measurementCode = requireNonNull(measurementCode, "measurement code must not be null"); this.facility = requireNonNull(method.facility(), "facility must not be null"); this.sequencingReadType = requireNonNull(method.sequencingReadType(), @@ -136,7 +136,7 @@ public static NGSMeasurement createWithPool(ProjectId projectId, String samplePo throws IllegalArgumentException { requireNonNull(measurementCode, "measurement Code must not be null"); requireNonNull(method, "method must not be null"); - requireNonNull(method.instrument(), "instrument must not be null"); + requireNonNull(method.instrument(), "msDevice must not be null"); if (samplePool.isBlank()) { throw new IllegalArgumentException("Sample Pool: no value provided"); } diff --git a/project-management/src/main/java/life/qbic/projectmanagement/domain/model/measurement/ProteomicsMeasurement.java b/project-management/src/main/java/life/qbic/projectmanagement/domain/model/measurement/ProteomicsMeasurement.java index dc8470580..1b8f68265 100644 --- a/project-management/src/main/java/life/qbic/projectmanagement/domain/model/measurement/ProteomicsMeasurement.java +++ b/project-management/src/main/java/life/qbic/projectmanagement/domain/model/measurement/ProteomicsMeasurement.java @@ -57,7 +57,7 @@ public class ProteomicsMeasurement { private MeasurementId measurementId; @Column(name = "instrument", columnDefinition = "longtext CHECK (json_valid(`instrument`))") - private OntologyTerm instrument; + private OntologyTerm msDevice; @Column(name = "samplePool") private String samplePool = ""; @@ -127,8 +127,8 @@ private static void evaluateMandatorySpecificMetadata( private static void evaluateMandatoryMetadata(ProteomicsMethodMetadata method) throws IllegalArgumentException { - if (method.instrument() == null) { - throw new IllegalArgumentException("Instrument: Missing metadata."); + if (method.msDevice() == null) { + throw new IllegalArgumentException("MS Device: Missing metadata."); } if (method.facility().isBlank()) { throw new IllegalArgumentException("Facility: Missing metadata"); @@ -157,7 +157,7 @@ public static ProteomicsMeasurement create(ProjectId projectId, MeasurementCode measurementCode, Organisation organisation, ProteomicsMethodMetadata method, Collection proteomicsSpecificMeasurementMetadata) throws IllegalArgumentException { - requireNonNull(method.instrument()); + requireNonNull(method.msDevice()); requireNonNull(measurementCode); requireNonNull(proteomicsSpecificMeasurementMetadata); if (!measurementCode.isMSDomain()) { @@ -215,8 +215,8 @@ public Collection measuredSamples() { .toList(); } - public OntologyTerm instrument() { - return instrument; + public OntologyTerm msDevice() { + return msDevice; } public Organisation organisation() { @@ -257,7 +257,7 @@ public void updateMethod(ProteomicsMethodMetadata method) { } private void setMethodMetadata(ProteomicsMethodMetadata methodMetadata) { - this.instrument = methodMetadata.instrument(); + this.msDevice = methodMetadata.msDevice(); this.facility = methodMetadata.facility(); this.digestionMethod = methodMetadata.digestionMethod(); this.digestionEnzyme = methodMetadata.digestionEnzyme(); diff --git a/project-management/src/main/java/life/qbic/projectmanagement/domain/model/measurement/ProteomicsMethodMetadata.java b/project-management/src/main/java/life/qbic/projectmanagement/domain/model/measurement/ProteomicsMethodMetadata.java index c841db076..b0586c33f 100644 --- a/project-management/src/main/java/life/qbic/projectmanagement/domain/model/measurement/ProteomicsMethodMetadata.java +++ b/project-management/src/main/java/life/qbic/projectmanagement/domain/model/measurement/ProteomicsMethodMetadata.java @@ -9,7 +9,7 @@ * * @since 1.0.0 */ -public record ProteomicsMethodMetadata(OntologyTerm instrument, String facility, +public record ProteomicsMethodMetadata(OntologyTerm msDevice, String facility, String digestionMethod, String digestionEnzyme, String enrichmentMethod, String lcColumn, String lcmsMethod, int injectionVolume, diff --git a/project-management/src/test/groovy/life/qbic/projectmanagement/application/measurement/validation/MeasurementProteomicsValidatorSpec.groovy b/project-management/src/test/groovy/life/qbic/projectmanagement/application/measurement/validation/MeasurementProteomicsValidatorSpec.groovy index cc2787c58..cb2748b2b 100644 --- a/project-management/src/test/groovy/life/qbic/projectmanagement/application/measurement/validation/MeasurementProteomicsValidatorSpec.groovy +++ b/project-management/src/test/groovy/life/qbic/projectmanagement/application/measurement/validation/MeasurementProteomicsValidatorSpec.groovy @@ -40,14 +40,14 @@ class MeasurementMeasurementProteomicsValidatorSpec extends Specification { "http://www.ebi.ac.uk/efo/EFO_0004205" ) final TerminologyService terminologyService = Mock(TerminologyService.class, { - findByCurie(validMetadata.instrumentCURI()) >> Optional.of(illuminaMiSeq) + findByCurie(validMetadata.msDeviceCURIE()) >> Optional.of(illuminaMiSeq) }) final MeasurementService measurementService = Mock(MeasurementService.class) final ProjectInformationService projectInformationService = Mock(ProjectInformationService.class) - final static List validPXPProperties = Collections.unmodifiableList(["qbic sample id", "sample name", "organisation id", "facility", "instrument", + final static List validPXPProperties = Collections.unmodifiableList(["qbic sample id", "sample name", "organisation id", "facility", "ms device", "sample pool group", "cycle/fraction name", "digestion method", "digestion enzyme", "enrichment method", "injection volume (uL)", "lc column", "lcms method", "labeling type", "label", "comment"]) @@ -336,7 +336,7 @@ class MeasurementMeasurementProteomicsValidatorSpec extends Specification { } - def "If no instrument Curie for the instrument information is provided, the validation must fail"() { + def "If no MS device Curie for the MS device information is provided, the validation must fail"() { given: SampleCode validSampleCode = SampleCode.create("QTEST001AE") ProteomicsMeasurementMetadata invalidMetadata = new ProteomicsMeasurementMetadata("", validSampleCode, @@ -372,15 +372,15 @@ class MeasurementMeasurementProteomicsValidatorSpec extends Specification { !result.containsWarnings() result.containsFailures() result.failedEntries() == 1 - result.failures()[0] == "Instrument: missing mandatory metadata" + result.failures()[0] == "MS Device: missing mandatory metadata" } - def "If a valid instrument curie for the instrument information is provided, the validation must pass"() { + def "If a valid ms device curie for the ms device information is provided, the validation must pass"() { given: SampleCode validSampleCode = SampleCode.create("QTEST001AE") ProteomicsMeasurementMetadata invalidMetadata = new ProteomicsMeasurementMetadata("", validSampleCode, "https://ror.org/03a1kwz48", //Universität Tübingen, - validInstrumentCurie, //Illumina MiSeq + validMsDeviceCurie, //Illumina MiSeq "1", "The geniuses of ITSS", "4 Nations lived in harmony", @@ -412,7 +412,7 @@ class MeasurementMeasurementProteomicsValidatorSpec extends Specification { !result.containsFailures() where: - validInstrumentCurie << [ + validMsDeviceCurie << [ "EFO:0004205", // Illumina MiSeq ] } diff --git a/user-interface/src/main/java/life/qbic/datamanager/views/projects/project/measurements/MeasurementDetailsComponent.java b/user-interface/src/main/java/life/qbic/datamanager/views/projects/project/measurements/MeasurementDetailsComponent.java index ecc86b529..3814a98e9 100644 --- a/user-interface/src/main/java/life/qbic/datamanager/views/projects/project/measurements/MeasurementDetailsComponent.java +++ b/user-interface/src/main/java/life/qbic/datamanager/views/projects/project/measurements/MeasurementDetailsComponent.java @@ -302,10 +302,10 @@ private void createProteomicsGrid() { .setAutoWidth(true); proteomicsMeasurementGrid.addComponentColumn( proteomicsMeasurement -> renderInstrument().createComponent( - proteomicsMeasurement.instrument())) - .setHeader("Instrument") + proteomicsMeasurement.msDevice())) + .setHeader("MS Device") .setTooltipGenerator( - proteomicsMeasurement -> proteomicsMeasurement.instrument().formatted()) + proteomicsMeasurement -> proteomicsMeasurement.msDevice().formatted()) .setAutoWidth(true); proteomicsMeasurementGrid.addColumn(ProteomicsMeasurement::digestionEnzyme) .setHeader("Digestion Enzyme").setTooltipGenerator( diff --git a/user-interface/src/main/java/life/qbic/datamanager/views/projects/project/measurements/MeasurementPresenter.java b/user-interface/src/main/java/life/qbic/datamanager/views/projects/project/measurements/MeasurementPresenter.java index 672c183ad..1ac431368 100644 --- a/user-interface/src/main/java/life/qbic/datamanager/views/projects/project/measurements/MeasurementPresenter.java +++ b/user-interface/src/main/java/life/qbic/datamanager/views/projects/project/measurements/MeasurementPresenter.java @@ -35,8 +35,8 @@ private static ProteomicsMeasurementEntry convertProteomicsMeasurement( ProteomicsSpecificMeasurementMetadata specificMeasurementMetadata) { return new ProteomicsMeasurementEntry(measurement.measurementCode().value(), sampleInfo, measurement.organisation().IRI(), measurement.organisation().label(), - measurement.instrument().getOboId().replace("_", ":"), - measurement.instrument().getLabel(), + measurement.msDevice().getOboId().replace("_", ":"), + measurement.msDevice().getLabel(), measurement.samplePoolGroup().orElse(""), measurement.facility(), specificMeasurementMetadata.fractionName(), measurement.digestionEnzyme(), measurement.digestionMethod(), diff --git a/user-interface/src/main/java/life/qbic/datamanager/views/projects/project/measurements/ProteomicsMeasurementEntry.java b/user-interface/src/main/java/life/qbic/datamanager/views/projects/project/measurements/ProteomicsMeasurementEntry.java index 4c8aa5ac2..beef70e19 100644 --- a/user-interface/src/main/java/life/qbic/datamanager/views/projects/project/measurements/ProteomicsMeasurementEntry.java +++ b/user-interface/src/main/java/life/qbic/datamanager/views/projects/project/measurements/ProteomicsMeasurementEntry.java @@ -11,8 +11,8 @@ public record ProteomicsMeasurementEntry(String measurementCode, SampleInformation sampleInformation, String organisationId, String organisationName, - String instrumentCURI, - String instrumentName, + String msDeviceCURIE, + String msDeviceName, String samplePoolGroup, String facility, String fractionName, diff --git a/user-interface/src/main/java/life/qbic/datamanager/views/projects/project/measurements/download/ProteomicsMeasurementContentProvider.java b/user-interface/src/main/java/life/qbic/datamanager/views/projects/project/measurements/download/ProteomicsMeasurementContentProvider.java index 7512f8113..f03867a53 100644 --- a/user-interface/src/main/java/life/qbic/datamanager/views/projects/project/measurements/download/ProteomicsMeasurementContentProvider.java +++ b/user-interface/src/main/java/life/qbic/datamanager/views/projects/project/measurements/download/ProteomicsMeasurementContentProvider.java @@ -68,8 +68,8 @@ private static void createMeasurementEntry(ProteomicsMeasurementEntry pxpEntry, case ORGANISATION_ID -> pxpEntry.organisationId(); case ORGANISATION_NAME -> pxpEntry.organisationName(); case FACILITY -> pxpEntry.facility(); - case MS_DEVICE -> pxpEntry.instrumentCURI(); - case MS_DEVICE_NAME -> pxpEntry.instrumentName(); + case MS_DEVICE -> pxpEntry.msDeviceCURIE(); + case MS_DEVICE_NAME -> pxpEntry.msDeviceName(); case CYCLE_FRACTION_NAME -> pxpEntry.fractionName(); case DIGESTION_METHOD -> pxpEntry.digestionMethod(); case DIGESTION_ENZYME -> pxpEntry.digestionEnzyme(); From 19568fcb505dbaa52923f1b26ec9bec71ac46d51 Mon Sep 17 00:00:00 2001 From: Tobias Koch Date: Mon, 16 Sep 2024 10:35:42 +0200 Subject: [PATCH 13/20] correct spelling --- .../measurement/validation/MeasurementProteomicsValidator.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project-management/src/main/java/life/qbic/projectmanagement/application/measurement/validation/MeasurementProteomicsValidator.java b/project-management/src/main/java/life/qbic/projectmanagement/application/measurement/validation/MeasurementProteomicsValidator.java index 5b1798db0..fbe37e6d2 100644 --- a/project-management/src/main/java/life/qbic/projectmanagement/application/measurement/validation/MeasurementProteomicsValidator.java +++ b/project-management/src/main/java/life/qbic/projectmanagement/application/measurement/validation/MeasurementProteomicsValidator.java @@ -131,7 +131,7 @@ public enum PROTEOMICS_PROPERTY { SAMPLE_LABEL("sample name"), ORGANISATION_ID("organisation id"), FACILITY("facility"), - MS_DEVICE("msDevice"), + MS_DEVICE("ms device"), SAMPLE_POOL_GROUP("sample pool group"), CYCLE_FRACTION_NAME("cycle/fraction name"), DIGESTION_METHOD("digestion method"), From df82459bab06861e0c193532ac94db9d27009017 Mon Sep 17 00:00:00 2001 From: Tobias Koch Date: Mon, 16 Sep 2024 11:14:53 +0200 Subject: [PATCH 14/20] fix jpa access --- .../measurement/MeasurementLookupImplementation.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/project-management-infrastructure/src/main/java/life/qbic/projectmanagement/infrastructure/experiment/measurement/MeasurementLookupImplementation.java b/project-management-infrastructure/src/main/java/life/qbic/projectmanagement/infrastructure/experiment/measurement/MeasurementLookupImplementation.java index d937c12c6..c84a67969 100644 --- a/project-management-infrastructure/src/main/java/life/qbic/projectmanagement/infrastructure/experiment/measurement/MeasurementLookupImplementation.java +++ b/project-management-infrastructure/src/main/java/life/qbic/projectmanagement/infrastructure/experiment/measurement/MeasurementLookupImplementation.java @@ -252,7 +252,7 @@ public static Specification isOrganisationLabel(String fi public static Specification isOntologyTermName(String filter) { return (root, query, builder) -> { Expression function = builder.function("JSON_EXTRACT", String.class, - root.get("instrument"), + root.get("msDevice"), builder.literal("$.name")); return builder.like(function, "%" + filter + "%"); @@ -263,7 +263,7 @@ public static Specification isOntologyTermLabel(String fi return (root, query, builder) -> { Expression function = builder.function("JSON_EXTRACT", String.class, - root.get("instrument"), builder.literal("$.label")); + root.get("msDevice"), builder.literal("$.label")); return builder.like(function, "%" + filter + "%"); }; From 4c5bccd572a14ba08e6805db7d44984e5663bd8b Mon Sep 17 00:00:00 2001 From: Tobias Koch Date: Mon, 16 Sep 2024 11:34:50 +0200 Subject: [PATCH 15/20] solves #747 add technical replicate --- .../measurement/MeasurementService.java | 27 ++++++++++----- .../ProteomicsMeasurementMetadata.java | 29 ++++++++++------ .../MeasurementProteomicsValidator.java | 1 + .../measurement/ProteomicsMeasurement.java | 8 +++++ .../measurement/ProteomicsMethodMetadata.java | 17 +++++++--- .../MeasurementProteomicsValidatorSpec.groovy | 2 +- .../datamanager/parser/MetadataConverter.java | 4 +++ .../measurements/MeasurementPresenter.java | 21 ++++++++---- .../ProteomicsMeasurementEntry.java | 3 +- .../ProteomicsMeasurementContentProvider.java | 32 ++++++++++-------- ...eomics_measurement_registration_sheet.xlsx | Bin 13314 -> 13402 bytes 11 files changed, 97 insertions(+), 47 deletions(-) diff --git a/project-management/src/main/java/life/qbic/projectmanagement/application/measurement/MeasurementService.java b/project-management/src/main/java/life/qbic/projectmanagement/application/measurement/MeasurementService.java index 916e7ae68..966d1d2ca 100644 --- a/project-management/src/main/java/life/qbic/projectmanagement/application/measurement/MeasurementService.java +++ b/project-management/src/main/java/life/qbic/projectmanagement/application/measurement/MeasurementService.java @@ -394,10 +394,15 @@ private Map> buildPxP( throw new MeasurementRegistrationException(ErrorCode.UNKNOWN_ONTOLOGY_TERM); } - var method = new ProteomicsMethodMetadata(msDeviceQuery.get(), firstMetadataEntry.facility(), - firstMetadataEntry.digestionMethod(), firstMetadataEntry.digestionEnzyme(), - firstMetadataEntry.enrichmentMethod(), firstMetadataEntry.lcColumn(), - firstMetadataEntry.lcmsMethod(), readInjectionVolume(firstMetadataEntry.injectionVolume()), + var method = new ProteomicsMethodMetadata(msDeviceQuery.get(), + firstMetadataEntry.technicalReplicateName(), + firstMetadataEntry.facility(), + firstMetadataEntry.digestionMethod(), + firstMetadataEntry.digestionEnzyme(), + firstMetadataEntry.enrichmentMethod(), + firstMetadataEntry.lcColumn(), + firstMetadataEntry.lcmsMethod(), + readInjectionVolume(firstMetadataEntry.injectionVolume()), firstMetadataEntry.labeling() .labelType()); @@ -617,6 +622,7 @@ private List> updateAllPxP( } var method = new ProteomicsMethodMetadata(msDeviceQuery.get(), + measurementMetadata.technicalReplicateName(), measurementMetadata.facility(), measurementMetadata.digestionMethod(), measurementMetadata.digestionEnzyme(), measurementMetadata.enrichmentMethod(), measurementMetadata.lcColumn(), @@ -647,10 +653,15 @@ private List> updateAllPxP( throw new MeasurementRegistrationException(ErrorCode.UNKNOWN_ONTOLOGY_TERM); } - var method = new ProteomicsMethodMetadata(msDeviceQuery.get(), firstEntry.facility(), - firstEntry.digestionMethod(), firstEntry.digestionEnzyme(), - firstEntry.enrichmentMethod(), firstEntry.lcColumn(), - firstEntry.lcmsMethod(), readInjectionVolume(firstEntry.injectionVolume()), + var method = new ProteomicsMethodMetadata(msDeviceQuery.get(), + firstEntry.technicalReplicateName(), + firstEntry.facility(), + firstEntry.digestionMethod(), + firstEntry.digestionEnzyme(), + firstEntry.enrichmentMethod(), + firstEntry.lcColumn(), + firstEntry.lcmsMethod(), + readInjectionVolume(firstEntry.injectionVolume()), firstEntry.labeling() .labelType()); diff --git a/project-management/src/main/java/life/qbic/projectmanagement/application/measurement/ProteomicsMeasurementMetadata.java b/project-management/src/main/java/life/qbic/projectmanagement/application/measurement/ProteomicsMeasurementMetadata.java index 06dc450fc..fd5811dcc 100644 --- a/project-management/src/main/java/life/qbic/projectmanagement/application/measurement/ProteomicsMeasurementMetadata.java +++ b/project-management/src/main/java/life/qbic/projectmanagement/application/measurement/ProteomicsMeasurementMetadata.java @@ -10,16 +10,24 @@ * * @since 1.0.0 */ -public record ProteomicsMeasurementMetadata(String measurementId, - SampleCode sampleCode, - String organisationId, String msDeviceCURIE, - String samplePoolGroup, String facility, - String fractionName, - String digestionEnzyme, - String digestionMethod, String enrichmentMethod, - String injectionVolume, String lcColumn, - String lcmsMethod, Labeling labeling, - String comment) implements MeasurementMetadata { +public record ProteomicsMeasurementMetadata( + String measurementId, + SampleCode sampleCode, + String technicalReplicateName, + String organisationId, + String msDeviceCURIE, + String samplePoolGroup, + String facility, + String fractionName, + String digestionEnzyme, + String digestionMethod, + String enrichmentMethod, + String injectionVolume, + String lcColumn, + String lcmsMethod, + Labeling labeling, + String comment +) implements MeasurementMetadata { @Override @@ -52,4 +60,5 @@ public boolean equals(Object o) { public int hashCode() { return measurementId.hashCode(); } + } diff --git a/project-management/src/main/java/life/qbic/projectmanagement/application/measurement/validation/MeasurementProteomicsValidator.java b/project-management/src/main/java/life/qbic/projectmanagement/application/measurement/validation/MeasurementProteomicsValidator.java index fbe37e6d2..fd0d065ee 100644 --- a/project-management/src/main/java/life/qbic/projectmanagement/application/measurement/validation/MeasurementProteomicsValidator.java +++ b/project-management/src/main/java/life/qbic/projectmanagement/application/measurement/validation/MeasurementProteomicsValidator.java @@ -129,6 +129,7 @@ public ValidationResult validateUpdate(ProteomicsMeasurementMetadata metadata, public enum PROTEOMICS_PROPERTY { QBIC_SAMPLE_ID("qbic sample id"), SAMPLE_LABEL("sample name"), + TECHNICAL_REPLICATE_NAME("technical replicate"), ORGANISATION_ID("organisation id"), FACILITY("facility"), MS_DEVICE("ms device"), diff --git a/project-management/src/main/java/life/qbic/projectmanagement/domain/model/measurement/ProteomicsMeasurement.java b/project-management/src/main/java/life/qbic/projectmanagement/domain/model/measurement/ProteomicsMeasurement.java index 1b8f68265..eefc2a1fc 100644 --- a/project-management/src/main/java/life/qbic/projectmanagement/domain/model/measurement/ProteomicsMeasurement.java +++ b/project-management/src/main/java/life/qbic/projectmanagement/domain/model/measurement/ProteomicsMeasurement.java @@ -59,6 +59,9 @@ public class ProteomicsMeasurement { @Column(name = "instrument", columnDefinition = "longtext CHECK (json_valid(`instrument`))") private OntologyTerm msDevice; + @Column(name = "technicalReplicateName") + private String technicalReplicateName; + @Column(name = "samplePool") private String samplePool = ""; @@ -258,6 +261,7 @@ public void updateMethod(ProteomicsMethodMetadata method) { private void setMethodMetadata(ProteomicsMethodMetadata methodMetadata) { this.msDevice = methodMetadata.msDevice(); + this.technicalReplicateName = methodMetadata.technicalReplicate(); this.facility = methodMetadata.facility(); this.digestionMethod = methodMetadata.digestionMethod(); this.digestionEnzyme = methodMetadata.digestionEnzyme(); @@ -319,4 +323,8 @@ public int hashCode() { public Optional comment() { return Optional.empty(); } + + public Optional technicalReplicateName() { + return Optional.ofNullable(technicalReplicateName); + } } diff --git a/project-management/src/main/java/life/qbic/projectmanagement/domain/model/measurement/ProteomicsMethodMetadata.java b/project-management/src/main/java/life/qbic/projectmanagement/domain/model/measurement/ProteomicsMethodMetadata.java index b0586c33f..c8ecef142 100644 --- a/project-management/src/main/java/life/qbic/projectmanagement/domain/model/measurement/ProteomicsMethodMetadata.java +++ b/project-management/src/main/java/life/qbic/projectmanagement/domain/model/measurement/ProteomicsMethodMetadata.java @@ -9,10 +9,17 @@ * * @since 1.0.0 */ -public record ProteomicsMethodMetadata(OntologyTerm msDevice, String facility, - String digestionMethod, - String digestionEnzyme, String enrichmentMethod, - String lcColumn, String lcmsMethod, int injectionVolume, - String labelType) { +public record ProteomicsMethodMetadata( + OntologyTerm msDevice, + String technicalReplicate, + String facility, + String digestionMethod, + String digestionEnzyme, + String enrichmentMethod, + String lcColumn, + String lcmsMethod, + int injectionVolume, + String labelType +) { } diff --git a/project-management/src/test/groovy/life/qbic/projectmanagement/application/measurement/validation/MeasurementProteomicsValidatorSpec.groovy b/project-management/src/test/groovy/life/qbic/projectmanagement/application/measurement/validation/MeasurementProteomicsValidatorSpec.groovy index cb2748b2b..d4a533ca9 100644 --- a/project-management/src/test/groovy/life/qbic/projectmanagement/application/measurement/validation/MeasurementProteomicsValidatorSpec.groovy +++ b/project-management/src/test/groovy/life/qbic/projectmanagement/application/measurement/validation/MeasurementProteomicsValidatorSpec.groovy @@ -47,7 +47,7 @@ class MeasurementMeasurementProteomicsValidatorSpec extends Specification { final ProjectInformationService projectInformationService = Mock(ProjectInformationService.class) - final static List validPXPProperties = Collections.unmodifiableList(["qbic sample id", "sample name", "organisation id", "facility", "ms device", + final static List validPXPProperties = Collections.unmodifiableList(["qbic sample id", "sample name", "technical replicate", "organisation id", "facility", "ms device", "sample pool group", "cycle/fraction name", "digestion method", "digestion enzyme", "enrichment method", "injection volume (uL)", "lc column", "lcms method", "labeling type", "label", "comment"]) diff --git a/user-interface/src/main/java/life/qbic/datamanager/parser/MetadataConverter.java b/user-interface/src/main/java/life/qbic/datamanager/parser/MetadataConverter.java index e0f917cc4..a4963cb2e 100644 --- a/user-interface/src/main/java/life/qbic/datamanager/parser/MetadataConverter.java +++ b/user-interface/src/main/java/life/qbic/datamanager/parser/MetadataConverter.java @@ -16,6 +16,7 @@ import static life.qbic.datamanager.parser.MetadataConverter.ProteomicsMeasurementProperty.ORGANISATION_ID; import static life.qbic.datamanager.parser.MetadataConverter.ProteomicsMeasurementProperty.QBIC_SAMPLE_ID; import static life.qbic.datamanager.parser.MetadataConverter.ProteomicsMeasurementProperty.SAMPLE_POOL_GROUP; +import static life.qbic.datamanager.parser.MetadataConverter.ProteomicsMeasurementProperty.TECHNICAL_REPLICATE_NAME; import static life.qbic.logging.service.LoggerFactory.logger; import java.util.ArrayList; @@ -138,6 +139,8 @@ private List convertProteomicsMeasurement(ParsingResult par safeListAccess(row.values(), keyIndices.getOrDefault(QBIC_SAMPLE_ID.propertyName(), -1), "")), + safeListAccess(row.values(), + keyIndices.getOrDefault(TECHNICAL_REPLICATE_NAME.propertyName(), -1), ""), safeListAccess(row.values(), keyIndices.getOrDefault(ORGANISATION_ID.propertyName(), -1), ""), safeListAccess(row.values(), keyIndices.getOrDefault(MS_DEVICE.propertyName(), -1), ""), @@ -280,6 +283,7 @@ private boolean looksLikeProteomicsMeasurement(Collection properties, bo enum ProteomicsMeasurementProperty { MEASUREMENT_ID("measurement id"), + TECHNICAL_REPLICATE_NAME("technical replicate"), QBIC_SAMPLE_ID("qbic sample id"), SAMPLE_POOL_GROUP("sample pool group"), ORGANISATION_ID("organisation id"), diff --git a/user-interface/src/main/java/life/qbic/datamanager/views/projects/project/measurements/MeasurementPresenter.java b/user-interface/src/main/java/life/qbic/datamanager/views/projects/project/measurements/MeasurementPresenter.java index 1ac431368..15b33bd1a 100644 --- a/user-interface/src/main/java/life/qbic/datamanager/views/projects/project/measurements/MeasurementPresenter.java +++ b/user-interface/src/main/java/life/qbic/datamanager/views/projects/project/measurements/MeasurementPresenter.java @@ -34,15 +34,24 @@ private static ProteomicsMeasurementEntry convertProteomicsMeasurement( SampleInformation sampleInfo, ProteomicsSpecificMeasurementMetadata specificMeasurementMetadata) { return new ProteomicsMeasurementEntry(measurement.measurementCode().value(), - sampleInfo, measurement.organisation().IRI(), measurement.organisation().label(), + sampleInfo, + measurement.technicalReplicateName().orElse(""), + measurement.organisation().IRI(), + measurement.organisation().label(), measurement.msDevice().getOboId().replace("_", ":"), measurement.msDevice().getLabel(), - measurement.samplePoolGroup().orElse(""), measurement.facility(), + measurement.samplePoolGroup().orElse(""), + measurement.facility(), specificMeasurementMetadata.fractionName(), - measurement.digestionEnzyme(), measurement.digestionMethod(), - measurement.enrichmentMethod(), String.valueOf(measurement.injectionVolume()), - measurement.lcColumn(), measurement.lcmsMethod(), measurement.labelType(), - specificMeasurementMetadata.label(), ""); + measurement.digestionEnzyme(), + measurement.digestionMethod(), + measurement.enrichmentMethod(), + String.valueOf(measurement.injectionVolume()), + measurement.lcColumn(), + measurement.lcmsMethod(), + measurement.labelType(), + specificMeasurementMetadata.label(), + ""); } private static NGSMeasurementEntry convertNGSMeasurement(NGSMeasurement measurement, diff --git a/user-interface/src/main/java/life/qbic/datamanager/views/projects/project/measurements/ProteomicsMeasurementEntry.java b/user-interface/src/main/java/life/qbic/datamanager/views/projects/project/measurements/ProteomicsMeasurementEntry.java index beef70e19..752e1fe9a 100644 --- a/user-interface/src/main/java/life/qbic/datamanager/views/projects/project/measurements/ProteomicsMeasurementEntry.java +++ b/user-interface/src/main/java/life/qbic/datamanager/views/projects/project/measurements/ProteomicsMeasurementEntry.java @@ -9,6 +9,7 @@ */ public record ProteomicsMeasurementEntry(String measurementCode, SampleInformation sampleInformation, + String technicalReplicateName, String organisationId, String organisationName, String msDeviceCURIE, @@ -25,6 +26,4 @@ public record ProteomicsMeasurementEntry(String measurementCode, String labelingType, String label, String comment) { - - } diff --git a/user-interface/src/main/java/life/qbic/datamanager/views/projects/project/measurements/download/ProteomicsMeasurementContentProvider.java b/user-interface/src/main/java/life/qbic/datamanager/views/projects/project/measurements/download/ProteomicsMeasurementContentProvider.java index f03867a53..05de7d2b4 100644 --- a/user-interface/src/main/java/life/qbic/datamanager/views/projects/project/measurements/download/ProteomicsMeasurementContentProvider.java +++ b/user-interface/src/main/java/life/qbic/datamanager/views/projects/project/measurements/download/ProteomicsMeasurementContentProvider.java @@ -65,6 +65,7 @@ private static void createMeasurementEntry(ProteomicsMeasurementEntry pxpEntry, case SAMPLE_ID -> pxpEntry.sampleInformation().sampleId(); case SAMPLE_NAME -> pxpEntry.sampleInformation().sampleName(); case POOL_GROUP -> pxpEntry.samplePoolGroup(); + case TECHNICAL_REPLICATE_NAME -> pxpEntry.technicalReplicateName(); case ORGANISATION_ID -> pxpEntry.organisationId(); case ORGANISATION_NAME -> pxpEntry.organisationName(); case FACILITY -> pxpEntry.facility(); @@ -183,21 +184,22 @@ enum ProteomicsMeasurementColumns { SAMPLE_NAME( "Sample Name", 2, true), POOL_GROUP("Sample Pool Group", 3, true), - ORGANISATION_ID("Organisation ID", 4, false), - ORGANISATION_NAME("Organisation Name", 5, true), - FACILITY("Facility", 6, false), - MS_DEVICE("MS Device", 7, false), - MS_DEVICE_NAME("MS Device Name", 8, true), - CYCLE_FRACTION_NAME("Cycle/Fraction Name", 9, false), - DIGESTION_METHOD("Digestion Method", 10, false), - DIGESTION_ENZYME("Digestion Enzyme", 11, false), - ENRICHMENT_METHOD("Enrichment Method", 12, false), - INJECTION_VOLUME("Injection Volume (µL)", 13, false), - LC_COLUMN("LC Column", 14, false), - LCMS_METHOD("LCMS Method", 15, false), - LABELING_TYPE("Labeling Type", 16, false), - LABEL("Label", 17, false), - COMMENT("Comment", 18, false), + TECHNICAL_REPLICATE_NAME("Technical Replicate", 4, false), + ORGANISATION_ID("Organisation ID", 5, false), + ORGANISATION_NAME("Organisation Name", 6, true), + FACILITY("Facility", 7, false), + MS_DEVICE("MS Device", 8, false), + MS_DEVICE_NAME("MS Device Name", 9, true), + CYCLE_FRACTION_NAME("Cycle/Fraction Name", 10, false), + DIGESTION_METHOD("Digestion Method", 11, false), + DIGESTION_ENZYME("Digestion Enzyme", 12, false), + ENRICHMENT_METHOD("Enrichment Method", 13, false), + INJECTION_VOLUME("Injection Volume (µL)", 14, false), + LC_COLUMN("LC Column", 15, false), + LCMS_METHOD("LCMS Method", 16, false), + LABELING_TYPE("Labeling Type", 17, false), + LABEL("Label", 18, false), + COMMENT("Comment", 19, false), ; private final String headerName; private final int columnIndex; diff --git a/user-interface/src/main/resources/templates/proteomics_measurement_registration_sheet.xlsx b/user-interface/src/main/resources/templates/proteomics_measurement_registration_sheet.xlsx index 70852eedca85a6d989df71c6500286588c0a3a34..644b9ae866cc15a34d2e2dd1b03a0d2360fcc211 100644 GIT binary patch delta 5014 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 Juh_3g{{oWI1MUC- From aec0f86cdbecc53450d88ebd3403b50e413433de Mon Sep 17 00:00:00 2001 From: Tobias Koch Date: Mon, 16 Sep 2024 11:43:18 +0200 Subject: [PATCH 16/20] fix constructors in tests --- .../MeasurementProteomicsValidatorSpec.groovy | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/project-management/src/test/groovy/life/qbic/projectmanagement/application/measurement/validation/MeasurementProteomicsValidatorSpec.groovy b/project-management/src/test/groovy/life/qbic/projectmanagement/application/measurement/validation/MeasurementProteomicsValidatorSpec.groovy index d4a533ca9..12b4bff77 100644 --- a/project-management/src/test/groovy/life/qbic/projectmanagement/application/measurement/validation/MeasurementProteomicsValidatorSpec.groovy +++ b/project-management/src/test/groovy/life/qbic/projectmanagement/application/measurement/validation/MeasurementProteomicsValidatorSpec.groovy @@ -16,6 +16,7 @@ import java.util.stream.Collectors class MeasurementMeasurementProteomicsValidatorSpec extends Specification { final static ProteomicsMeasurementMetadata validMetadata = new ProteomicsMeasurementMetadata("", SampleCode.create("QTEST001AE"), + "", "https://ror.org/03a1kwz48", //Universität Tübingen, "EFO:0004205", //Illumina MiSeq "1", @@ -131,6 +132,7 @@ class MeasurementMeasurementProteomicsValidatorSpec extends Specification { def "An unknown sample code in a proteomics measurement metadata object must return a failed validation "() { given: def invalidMeasurementEntry = new ProteomicsMeasurementMetadata("", SampleCode.create("QNKWN001AE"), + "", "https://ror.org/03a1kwz48", //Universität Tübingen, "EFO:0004205", //Illumina MiSeq "1", @@ -168,6 +170,7 @@ class MeasurementMeasurementProteomicsValidatorSpec extends Specification { def "If no sample code is provided, the validation must fail"() { given: def invalidMeasurementEntry = new ProteomicsMeasurementMetadata("", null, + "", "https://ror.org/03a1kwz48", //Universität Tübingen, "EFO:0004205", //Illumina MiSeq "1", @@ -210,6 +213,7 @@ class MeasurementMeasurementProteomicsValidatorSpec extends Specification { given: SampleCode validSampleCode = SampleCode.create("QTEST001AE") ProteomicsMeasurementMetadata invalidMetadata = new ProteomicsMeasurementMetadata("", validSampleCode, + "", invalidRorId, //Universität Tübingen, "EFO:0004205", //Illumina MiSeq "1", @@ -257,6 +261,7 @@ class MeasurementMeasurementProteomicsValidatorSpec extends Specification { given: SampleCode validSampleCode = SampleCode.create("QTEST001AE") ProteomicsMeasurementMetadata invalidMetadata = new ProteomicsMeasurementMetadata("", validSampleCode, + "", "", // missing entry "EFO:0004205", //Illumina MiSeq "1", @@ -296,6 +301,7 @@ class MeasurementMeasurementProteomicsValidatorSpec extends Specification { given: SampleCode validSampleCode = SampleCode.create("QTEST001AE") ProteomicsMeasurementMetadata validMetadata = new ProteomicsMeasurementMetadata("", validSampleCode, + "", validRorId, //Universität Tübingen, "EFO:0004205", //Illumina MiSeq "1", @@ -340,6 +346,7 @@ class MeasurementMeasurementProteomicsValidatorSpec extends Specification { given: SampleCode validSampleCode = SampleCode.create("QTEST001AE") ProteomicsMeasurementMetadata invalidMetadata = new ProteomicsMeasurementMetadata("", validSampleCode, + "", "https://ror.org/03a1kwz48", //Universität Tübingen, "", //Illumina MiSeq "1", @@ -379,6 +386,7 @@ class MeasurementMeasurementProteomicsValidatorSpec extends Specification { given: SampleCode validSampleCode = SampleCode.create("QTEST001AE") ProteomicsMeasurementMetadata invalidMetadata = new ProteomicsMeasurementMetadata("", validSampleCode, + "", "https://ror.org/03a1kwz48", //Universität Tübingen, validMsDeviceCurie, //Illumina MiSeq "1", @@ -422,6 +430,7 @@ class MeasurementMeasurementProteomicsValidatorSpec extends Specification { given: SampleCode validSampleCode = SampleCode.create("QTEST001AE") ProteomicsMeasurementMetadata invalidMetadata = new ProteomicsMeasurementMetadata("", validSampleCode, + "", "https://ror.org/03a1kwz48", //Universität Tübingen, "EFO:0004205", //Illumina MiSeq "1", @@ -461,6 +470,7 @@ class MeasurementMeasurementProteomicsValidatorSpec extends Specification { given: SampleCode validSampleCode = SampleCode.create("QTEST001AE") ProteomicsMeasurementMetadata invalidMetadata = new ProteomicsMeasurementMetadata("", validSampleCode, + "", "https://ror.org/03a1kwz48", //Universität Tübingen, "EFO:0004205", //Illumina MiSeq "1", @@ -498,6 +508,7 @@ class MeasurementMeasurementProteomicsValidatorSpec extends Specification { given: SampleCode validSampleCode = SampleCode.create("QTEST001AE") ProteomicsMeasurementMetadata invalidMetadata = new ProteomicsMeasurementMetadata("", validSampleCode, + "", "https://ror.org/03a1kwz48", //Universität Tübingen, "EFO:0004205", //Illumina MiSeq "1", @@ -537,6 +548,7 @@ class MeasurementMeasurementProteomicsValidatorSpec extends Specification { given: SampleCode validSampleCode = SampleCode.create("QTEST001AE") ProteomicsMeasurementMetadata invalidMetadata = new ProteomicsMeasurementMetadata("", validSampleCode, + "", "https://ror.org/03a1kwz48", //Universität Tübingen, "EFO:0004205", //Illumina MiSeq "1", @@ -576,6 +588,7 @@ class MeasurementMeasurementProteomicsValidatorSpec extends Specification { given: SampleCode validSampleCode = SampleCode.create("QTEST001AE") ProteomicsMeasurementMetadata invalidMetadata = new ProteomicsMeasurementMetadata("", validSampleCode, + "", "https://ror.org/03a1kwz48", //Universität Tübingen, "EFO:0004205", //Illumina MiSeq "1", @@ -614,6 +627,7 @@ class MeasurementMeasurementProteomicsValidatorSpec extends Specification { given: SampleCode validSampleCode = SampleCode.create("QTEST001AE") ProteomicsMeasurementMetadata invalidMetadata = new ProteomicsMeasurementMetadata("", validSampleCode, + "", "https://ror.org/03a1kwz48", //Universität Tübingen, "EFO:0004205", //Illumina MiSeq "1", From 5cdefa35f803b3a204fd8fbe75d1c31e1f75be41 Mon Sep 17 00:00:00 2001 From: Tobias Koch Date: Tue, 17 Sep 2024 09:10:12 +0200 Subject: [PATCH 17/20] use correct methods --- .../download/NGSMeasurementContentProvider.java | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/user-interface/src/main/java/life/qbic/datamanager/views/projects/project/measurements/download/NGSMeasurementContentProvider.java b/user-interface/src/main/java/life/qbic/datamanager/views/projects/project/measurements/download/NGSMeasurementContentProvider.java index 493a83448..6254294d5 100644 --- a/user-interface/src/main/java/life/qbic/datamanager/views/projects/project/measurements/download/NGSMeasurementContentProvider.java +++ b/user-interface/src/main/java/life/qbic/datamanager/views/projects/project/measurements/download/NGSMeasurementContentProvider.java @@ -1,6 +1,8 @@ package life.qbic.datamanager.views.projects.project.measurements.download; import static life.qbic.datamanager.spreadsheet.XLSXTemplateHelper.createOptionArea; +import static life.qbic.datamanager.spreadsheet.XLSXTemplateHelper.getOrCreateCell; +import static life.qbic.datamanager.spreadsheet.XLSXTemplateHelper.getOrCreateRow; import static life.qbic.datamanager.spreadsheet.XLSXTemplateHelper.hideSheet; import static life.qbic.datamanager.spreadsheet.XLSXTemplateHelper.lockSheet; import static life.qbic.logging.service.LoggerFactory.logger; @@ -10,7 +12,6 @@ import java.util.Arrays; import java.util.LinkedList; import java.util.List; -import java.util.Optional; import life.qbic.application.commons.ApplicationException; import life.qbic.application.commons.ApplicationException.ErrorCode; import life.qbic.datamanager.spreadsheet.XLSXTemplateHelper; @@ -25,7 +26,6 @@ import org.apache.poi.ss.usermodel.Font; import org.apache.poi.ss.usermodel.Name; import org.apache.poi.ss.usermodel.Row; -import org.apache.poi.ss.usermodel.Row.MissingCellPolicy; import org.apache.poi.ss.usermodel.Sheet; import org.apache.poi.ss.usermodel.Workbook; import org.apache.poi.xssf.usermodel.DefaultIndexedColorMap; @@ -180,7 +180,7 @@ public byte[] getContent() { Row header = getOrCreateRow(sheet, 0); for (NGSMeasurementColumns value : NGSMeasurementColumns.values()) { - var cell = header.createCell(value.columnIndex()); + var cell = getOrCreateCell(header, value.columnIndex()); cell.setCellValue(value.headerName()); setHeaderStyle(cell, value.readOnly()); } @@ -224,16 +224,6 @@ public byte[] getContent() { return byteArrayOutputStream.toByteArray(); } - private static Row getOrCreateRow(Sheet sheet, int index) { - return Optional.ofNullable(sheet.getRow(index)) - .orElse(sheet.createRow(index)); - } - - private static Cell getOrCreateCell(Row row, int colIndex) { - return Optional.ofNullable(row.getCell(colIndex, MissingCellPolicy.RETURN_BLANK_AS_NULL)) - .orElse(row.createCell(colIndex)); - } - @Override public String getFileName() { return String.join("_", fileNamePrefix, FILE_NAME_SUFFIX); From 42949457916a72448477a511212b64a9834e3a30 Mon Sep 17 00:00:00 2001 From: Tobias Koch Date: Tue, 17 Sep 2024 15:33:32 +0200 Subject: [PATCH 18/20] Fix wrong imports Co-authored-by: steffengreiner --- .../datamanager/parser/MetadataConverter.java | 106 ++++++++++-------- 1 file changed, 61 insertions(+), 45 deletions(-) diff --git a/user-interface/src/main/java/life/qbic/datamanager/parser/MetadataConverter.java b/user-interface/src/main/java/life/qbic/datamanager/parser/MetadataConverter.java index a4963cb2e..3df6e642f 100644 --- a/user-interface/src/main/java/life/qbic/datamanager/parser/MetadataConverter.java +++ b/user-interface/src/main/java/life/qbic/datamanager/parser/MetadataConverter.java @@ -1,22 +1,5 @@ package life.qbic.datamanager.parser; -import static life.qbic.datamanager.parser.MetadataConverter.ProteomicsMeasurementProperty.COMMENT; -import static life.qbic.datamanager.parser.MetadataConverter.ProteomicsMeasurementProperty.CYCLE; -import static life.qbic.datamanager.parser.MetadataConverter.ProteomicsMeasurementProperty.DIGESTION_ENZYME; -import static life.qbic.datamanager.parser.MetadataConverter.ProteomicsMeasurementProperty.DIGESTION_METHOD; -import static life.qbic.datamanager.parser.MetadataConverter.ProteomicsMeasurementProperty.ENRICHMENT_METHOD; -import static life.qbic.datamanager.parser.MetadataConverter.ProteomicsMeasurementProperty.FACILITY; -import static life.qbic.datamanager.parser.MetadataConverter.ProteomicsMeasurementProperty.INJECTION_VOLUME; -import static life.qbic.datamanager.parser.MetadataConverter.ProteomicsMeasurementProperty.LABEL; -import static life.qbic.datamanager.parser.MetadataConverter.ProteomicsMeasurementProperty.LABELING_TYPE; -import static life.qbic.datamanager.parser.MetadataConverter.ProteomicsMeasurementProperty.LCMS_METHOD; -import static life.qbic.datamanager.parser.MetadataConverter.ProteomicsMeasurementProperty.LC_COLUMN; -import static life.qbic.datamanager.parser.MetadataConverter.ProteomicsMeasurementProperty.MEASUREMENT_ID; -import static life.qbic.datamanager.parser.MetadataConverter.ProteomicsMeasurementProperty.MS_DEVICE; -import static life.qbic.datamanager.parser.MetadataConverter.ProteomicsMeasurementProperty.ORGANISATION_ID; -import static life.qbic.datamanager.parser.MetadataConverter.ProteomicsMeasurementProperty.QBIC_SAMPLE_ID; -import static life.qbic.datamanager.parser.MetadataConverter.ProteomicsMeasurementProperty.SAMPLE_POOL_GROUP; -import static life.qbic.datamanager.parser.MetadataConverter.ProteomicsMeasurementProperty.TECHNICAL_REPLICATE_NAME; import static life.qbic.logging.service.LoggerFactory.logger; import java.util.ArrayList; @@ -133,39 +116,66 @@ private List convertProteomicsMeasurement(ParsingResult par for (ParsingResult.Row row : parsingResult.rows()) { // we us -1 as default value if a property cannot be accessed, thus ending up in an empty String var pxpMetaDatum = new ProteomicsMeasurementMetadata( - safeListAccess(row.values(), keyIndices.getOrDefault(MEASUREMENT_ID.propertyName(), -1), + safeListAccess(row.values(), + keyIndices.getOrDefault(ProteomicsMeasurementProperty.MEASUREMENT_ID.propertyName(), + -1), ""), SampleCode.create( safeListAccess(row.values(), - keyIndices.getOrDefault(QBIC_SAMPLE_ID.propertyName(), -1), + keyIndices.getOrDefault( + ProteomicsMeasurementProperty.QBIC_SAMPLE_ID.propertyName(), -1), "")), safeListAccess(row.values(), - keyIndices.getOrDefault(TECHNICAL_REPLICATE_NAME.propertyName(), -1), ""), - safeListAccess(row.values(), keyIndices.getOrDefault(ORGANISATION_ID.propertyName(), -1), + keyIndices.getOrDefault( + ProteomicsMeasurementProperty.TECHNICAL_REPLICATE_NAME.propertyName(), -1), ""), + safeListAccess(row.values(), + keyIndices.getOrDefault(ProteomicsMeasurementProperty.ORGANISATION_ID.propertyName(), + -1), + ""), + safeListAccess(row.values(), + keyIndices.getOrDefault(ProteomicsMeasurementProperty.MS_DEVICE.propertyName(), -1), + ""), + safeListAccess(row.values(), + keyIndices.getOrDefault( + ProteomicsMeasurementProperty.SAMPLE_POOL_GROUP.propertyName(), -1), + ""), + safeListAccess(row.values(), + keyIndices.getOrDefault(ProteomicsMeasurementProperty.FACILITY.propertyName(), -1), + ""), + safeListAccess(row.values(), + keyIndices.getOrDefault(ProteomicsMeasurementProperty.CYCLE.propertyName(), -1), ""), + safeListAccess(row.values(), + keyIndices.getOrDefault(ProteomicsMeasurementProperty.DIGESTION_ENZYME.propertyName(), + -1), ""), - safeListAccess(row.values(), keyIndices.getOrDefault(MS_DEVICE.propertyName(), -1), ""), safeListAccess(row.values(), - keyIndices.getOrDefault(SAMPLE_POOL_GROUP.propertyName(), -1), + keyIndices.getOrDefault(ProteomicsMeasurementProperty.DIGESTION_METHOD.propertyName(), + -1), ""), - safeListAccess(row.values(), keyIndices.getOrDefault(FACILITY.propertyName(), -1), ""), - safeListAccess(row.values(), keyIndices.getOrDefault(CYCLE.propertyName(), -1), ""), - safeListAccess(row.values(), keyIndices.getOrDefault(DIGESTION_ENZYME.propertyName(), -1), + safeListAccess(row.values(), + keyIndices.getOrDefault( + ProteomicsMeasurementProperty.ENRICHMENT_METHOD.propertyName(), -1), ""), - safeListAccess(row.values(), keyIndices.getOrDefault(DIGESTION_METHOD.propertyName(), -1), + safeListAccess(row.values(), + keyIndices.getOrDefault(ProteomicsMeasurementProperty.INJECTION_VOLUME.propertyName(), + -1), ""), safeListAccess(row.values(), - keyIndices.getOrDefault(ENRICHMENT_METHOD.propertyName(), -1), + keyIndices.getOrDefault(ProteomicsMeasurementProperty.LC_COLUMN.propertyName(), -1), ""), - safeListAccess(row.values(), keyIndices.getOrDefault(INJECTION_VOLUME.propertyName(), -1), + safeListAccess(row.values(), + keyIndices.getOrDefault(ProteomicsMeasurementProperty.LCMS_METHOD.propertyName(), -1), ""), - safeListAccess(row.values(), keyIndices.getOrDefault(LC_COLUMN.propertyName(), -1), ""), - safeListAccess(row.values(), keyIndices.getOrDefault(LCMS_METHOD.propertyName(), -1), ""), new Labeling( safeListAccess(row.values(), - keyIndices.getOrDefault(LABELING_TYPE.propertyName(), -1), + keyIndices.getOrDefault( + ProteomicsMeasurementProperty.LABELING_TYPE.propertyName(), -1), ""), - safeListAccess(row.values(), keyIndices.getOrDefault(LABEL.propertyName(), -1), "")), - safeListAccess(row.values(), keyIndices.getOrDefault(COMMENT.propertyName(), -1), "") + safeListAccess(row.values(), + keyIndices.getOrDefault(ProteomicsMeasurementProperty.LABEL.propertyName(), -1), + "")), + safeListAccess(row.values(), + keyIndices.getOrDefault(ProteomicsMeasurementProperty.COMMENT.propertyName(), -1), "") ); result.add(pxpMetaDatum); } @@ -184,16 +194,20 @@ private List convertNGSMeasurement(ParsingResult parsingRes var keyIndices = parsingResult.keys(); for (Row row : parsingResult.rows()) { var ngsMeasurementMetadata = new NGSMeasurementMetadata( - safeListAccess(row.values(), keyIndices.getOrDefault(MEASUREMENT_ID.propertyName(), -1), + safeListAccess(row.values(), + keyIndices.getOrDefault(NGSMeasurementProperty.MEASUREMENT_ID.propertyName(), -1), ""), List.of(SampleCode.create( safeListAccess(row.values(), - keyIndices.getOrDefault(QBIC_SAMPLE_ID.propertyName(), -1), + keyIndices.getOrDefault(NGSMeasurementProperty.SAMPLE_CODE.propertyName(), -1), ""))), - safeListAccess(row.values(), keyIndices.getOrDefault(ORGANISATION_ID.propertyName(), -1), + safeListAccess(row.values(), + keyIndices.getOrDefault(NGSMeasurementProperty.ORGANISATION_ID.propertyName(), -1), ""), - safeListAccess(row.values(), keyIndices.getOrDefault(MS_DEVICE.propertyName(), -1), ""), - safeListAccess(row.values(), keyIndices.getOrDefault(FACILITY.propertyName(), -1), ""), + safeListAccess(row.values(), + keyIndices.getOrDefault(NGSMeasurementProperty.INSTRUMENT.propertyName(), -1), ""), + safeListAccess(row.values(), + keyIndices.getOrDefault(NGSMeasurementProperty.FACILITY.propertyName(), -1), ""), safeListAccess(row.values(), keyIndices.getOrDefault( NGSMeasurementProperty.SEQUENCING_READ_TYPE.propertyName(), -1), ""), safeListAccess(row.values(), @@ -205,7 +219,7 @@ private List convertNGSMeasurement(ParsingResult parsingRes NGSMeasurementProperty.SEQUENCING_RUN_PROTOCOL.propertyName(), -1), ""), safeListAccess(row.values(), - keyIndices.getOrDefault(SAMPLE_POOL_GROUP.propertyName(), -1), + keyIndices.getOrDefault(NGSMeasurementProperty.SAMPLE_POOL_GROUP.propertyName(), -1), ""), safeListAccess(row.values(), keyIndices.getOrDefault(NGSMeasurementProperty.INDEX_I7.propertyName(), -1), @@ -213,7 +227,8 @@ private List convertNGSMeasurement(ParsingResult parsingRes safeListAccess(row.values(), keyIndices.getOrDefault(NGSMeasurementProperty.INDEX_I5.propertyName(), -1), ""), - safeListAccess(row.values(), keyIndices.getOrDefault(COMMENT.propertyName(), -1), "") + safeListAccess(row.values(), + keyIndices.getOrDefault(NGSMeasurementProperty.COMMENT.propertyName(), -1), "") ); result.add(ngsMeasurementMetadata); } @@ -225,11 +240,11 @@ private boolean looksLikeNgsMeasurement(Collection properties, boolean i .collect(Collectors.toList()); Map hitMap; if (ignoreID) { - formattedProperties.remove(MEASUREMENT_ID.propertyName()); + formattedProperties.remove(NGSMeasurementProperty.MEASUREMENT_ID.propertyName()); hitMap = countHits(formattedProperties, Arrays.stream(NGSMeasurementProperty.values()) .map(NGSMeasurementProperty::propertyName).collect( - Collectors.toSet()), MEASUREMENT_ID.propertyName()); + Collectors.toSet()), NGSMeasurementProperty.MEASUREMENT_ID.propertyName()); } else { hitMap = countHits(formattedProperties, Arrays.stream(NGSMeasurementProperty.values()) @@ -255,11 +270,11 @@ private boolean looksLikeProteomicsMeasurement(Collection properties, bo .collect(Collectors.toList()); Map hitMap; if (ignoreID) { - formattedProperties.remove(MEASUREMENT_ID.propertyName()); + formattedProperties.remove(ProteomicsMeasurementProperty.MEASUREMENT_ID.propertyName()); hitMap = countHits(formattedProperties, Arrays.stream(ProteomicsMeasurementProperty.values()) .map(ProteomicsMeasurementProperty::propertyName).collect( - Collectors.toSet()), MEASUREMENT_ID.propertyName()); + Collectors.toSet()), ProteomicsMeasurementProperty.MEASUREMENT_ID.propertyName()); } else { hitMap = countHits(formattedProperties, Arrays.stream(ProteomicsMeasurementProperty.values()) @@ -327,6 +342,7 @@ public String propertyName() { enum NGSMeasurementProperty { MEASUREMENT_ID("measurement id"), + SAMPLE_CODE("sample code"), ORGANISATION_ID("organisation id"), SAMPLE_POOL_GROUP("sample pool group"), FACILITY("facility"), From bbdfa72dc687ca690f168ece2ba79256108f74d0 Mon Sep 17 00:00:00 2001 From: Tobias Koch Date: Tue, 17 Sep 2024 15:36:28 +0200 Subject: [PATCH 19/20] move test Co-authored-by: steffengreiner --- .../NGSMeasurementContentProviderTest.groovy | 30 ------------------- .../spreadsheet/XLSXTemplateHelperTest.groovy | 17 +++++++++++ 2 files changed, 17 insertions(+), 30 deletions(-) delete mode 100644 user-interface/src/test/groovy/life/qbic/datamanager/views/projects/project/measurements/download/NGSMeasurementContentProviderTest.groovy diff --git a/user-interface/src/test/groovy/life/qbic/datamanager/views/projects/project/measurements/download/NGSMeasurementContentProviderTest.groovy b/user-interface/src/test/groovy/life/qbic/datamanager/views/projects/project/measurements/download/NGSMeasurementContentProviderTest.groovy deleted file mode 100644 index efbfe33cc..000000000 --- a/user-interface/src/test/groovy/life/qbic/datamanager/views/projects/project/measurements/download/NGSMeasurementContentProviderTest.groovy +++ /dev/null @@ -1,30 +0,0 @@ -package life.qbic.datamanager.views.projects.project.measurements.download - -import org.apache.poi.ss.usermodel.Workbook -import org.apache.poi.ss.usermodel.WorkbookFactory -import spock.lang.Specification - -/** - * TODO! - * short description - * - *

detailed description

- * - * @since - */ -class NGSMeasurementContentProviderTest extends Specification { - - def "test that column reference works"() { - given: - Workbook workbook = WorkbookFactory.create(true) - def sheet = workbook.createSheet("My sheet") - when: - var result = NGSMeasurementContentProvider.addValueListWithName("namedArea", sheet, List.of("test1", "test2", "aböüß"), "test values") - then: - result.getRefersToFormula() == "'My sheet'!\$A\$1:\$A\$4" - result.getNameName() == "namedArea" - workbook.getName("namedArea") != null - - - } -} diff --git a/user-interface/src/test/java/life/qbic/datamanager/spreadsheet/XLSXTemplateHelperTest.groovy b/user-interface/src/test/java/life/qbic/datamanager/spreadsheet/XLSXTemplateHelperTest.groovy index 8fabc001f..2e7ef2920 100644 --- a/user-interface/src/test/java/life/qbic/datamanager/spreadsheet/XLSXTemplateHelperTest.groovy +++ b/user-interface/src/test/java/life/qbic/datamanager/spreadsheet/XLSXTemplateHelperTest.groovy @@ -1,5 +1,8 @@ package life.qbic.datamanager.spreadsheet + +import org.apache.poi.ss.usermodel.Workbook +import org.apache.poi.ss.usermodel.WorkbookFactory import spock.lang.Specification class XLSXTemplateHelperTest extends Specification { @@ -19,4 +22,18 @@ class XLSXTemplateHelperTest extends Specification { "this is a test*" | "thisIsATest" } + + def "test that column reference works"() { + given: + Workbook workbook = WorkbookFactory.create(true) + def sheet = workbook.createSheet("My sheet") + when: + var result = XLSXTemplateHelper.createOptionArea(sheet, + "test values", + List.of("test1", "test2", "aböüß")) + then: + result.getRefersToFormula() == "'My sheet'!\$A\$1:\$A\$4" + result.getNameName() == "testValues" + workbook.getName("testValues") != null + } } From b81cef4e4009c8388886fd72cb92bed30824f64f Mon Sep 17 00:00:00 2001 From: Tobias Koch Date: Tue, 17 Sep 2024 15:55:18 +0200 Subject: [PATCH 20/20] Add QBIC_SAMPLE_ID Co-authored-by: steffengreiner --- .../java/life/qbic/datamanager/parser/MetadataConverter.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/user-interface/src/main/java/life/qbic/datamanager/parser/MetadataConverter.java b/user-interface/src/main/java/life/qbic/datamanager/parser/MetadataConverter.java index 3df6e642f..2bda75faf 100644 --- a/user-interface/src/main/java/life/qbic/datamanager/parser/MetadataConverter.java +++ b/user-interface/src/main/java/life/qbic/datamanager/parser/MetadataConverter.java @@ -199,7 +199,7 @@ private List convertNGSMeasurement(ParsingResult parsingRes ""), List.of(SampleCode.create( safeListAccess(row.values(), - keyIndices.getOrDefault(NGSMeasurementProperty.SAMPLE_CODE.propertyName(), -1), + keyIndices.getOrDefault(NGSMeasurementProperty.QBIC_SAMPLE_ID.propertyName(), -1), ""))), safeListAccess(row.values(), keyIndices.getOrDefault(NGSMeasurementProperty.ORGANISATION_ID.propertyName(), -1), @@ -342,7 +342,7 @@ public String propertyName() { enum NGSMeasurementProperty { MEASUREMENT_ID("measurement id"), - SAMPLE_CODE("sample code"), + QBIC_SAMPLE_ID("qbic sample id"), ORGANISATION_ID("organisation id"), SAMPLE_POOL_GROUP("sample pool group"), FACILITY("facility"),