diff --git a/project-management/src/main/java/life/qbic/projectmanagement/domain/model/experiment/repository/jpa/OntologyClassAttributeConverter.java b/project-management/src/main/java/life/qbic/projectmanagement/domain/model/experiment/repository/jpa/OntologyClassAttributeConverter.java index 9398fa6b5..cd8f50692 100644 --- a/project-management/src/main/java/life/qbic/projectmanagement/domain/model/experiment/repository/jpa/OntologyClassAttributeConverter.java +++ b/project-management/src/main/java/life/qbic/projectmanagement/domain/model/experiment/repository/jpa/OntologyClassAttributeConverter.java @@ -1,6 +1,7 @@ package life.qbic.projectmanagement.domain.model.experiment.repository.jpa; import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; import jakarta.persistence.AttributeConverter; import jakarta.persistence.Converter; @@ -11,7 +12,8 @@ public class OntologyClassAttributeConverter implements AttributeConverter { - private static final ObjectMapper objectMapper = new ObjectMapper(); + private static final ObjectMapper objectMapper = new ObjectMapper().configure( + DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); @Override public String convertToDatabaseColumn(OntologyTerm attribute) { diff --git a/user-interface/src/main/bundles/dev.bundle b/user-interface/src/main/bundles/dev.bundle index a4d4c8af6..72f81be38 100644 Binary files a/user-interface/src/main/bundles/dev.bundle and b/user-interface/src/main/bundles/dev.bundle differ diff --git a/user-interface/src/main/java/life/qbic/datamanager/views/projects/project/measurements/MeasurementMain.java b/user-interface/src/main/java/life/qbic/datamanager/views/projects/project/measurements/MeasurementMain.java index 6091d501a..8f296b3db 100644 --- a/user-interface/src/main/java/life/qbic/datamanager/views/projects/project/measurements/MeasurementMain.java +++ b/user-interface/src/main/java/life/qbic/datamanager/views/projects/project/measurements/MeasurementMain.java @@ -45,6 +45,7 @@ import life.qbic.datamanager.views.projects.project.measurements.download.ProteomicsMeasurementContentProvider; import life.qbic.logging.api.Logger; import life.qbic.logging.service.LoggerFactory; +import life.qbic.projectmanagement.application.ProjectInformationService; import life.qbic.projectmanagement.application.measurement.MeasurementMetadata; import life.qbic.projectmanagement.application.measurement.MeasurementService; import life.qbic.projectmanagement.application.measurement.MeasurementService.MeasurementDeletionException; @@ -97,6 +98,7 @@ public class MeasurementMain extends Main implements BeforeEnterObserver { private final NGSMeasurementContentProvider ngsMeasurementContentProvider; private final DownloadProvider ngsDownloadProvider; private final DownloadProvider proteomicsDownloadProvider; + private final ProjectInformationService projectInformationService; private transient Context context; public MeasurementMain( @@ -105,7 +107,8 @@ public MeasurementMain( @Autowired SampleInformationService sampleInformationService, @Autowired MeasurementService measurementService, @Autowired MeasurementPresenter measurementPresenter, - @Autowired MeasurementValidationService measurementValidationService) { + @Autowired MeasurementValidationService measurementValidationService, + ProjectInformationService projectInformationService) { Objects.requireNonNull(measurementTemplateListComponent); Objects.requireNonNull(measurementDetailsComponent); Objects.requireNonNull(measurementService); @@ -133,7 +136,8 @@ public MeasurementMain( add(measurementDetailsComponent); measurementDetailsComponent.addListener( - selectionChangedEvent -> setSelectedMeasurementsInfo(selectionChangedEvent.getSource().getNumberOfSelectedMeasurements())); + selectionChangedEvent -> setSelectedMeasurementsInfo( + selectionChangedEvent.getSource().getNumberOfSelectedMeasurements())); add(ngsDownloadProvider); add(proteomicsDownloadProvider); @@ -143,6 +147,7 @@ public MeasurementMain( getClass().getSimpleName(), System.identityHashCode(this), measurementTemplateListComponent.getClass().getSimpleName(), System.identityHashCode(measurementTemplateListComponent))); + this.projectInformationService = projectInformationService; } private static String convertErrorCodeToMessage(MeasurementService.ErrorCode errorCode) { @@ -203,24 +208,25 @@ private void initSearchFieldAndButtonBar() { private void onDeleteMeasurementsClicked() { Optional tabLabel = measurementDetailsComponent.getSelectedTabName(); - if(tabLabel.isEmpty()) { + if (tabLabel.isEmpty()) { return; } String label = tabLabel.get(); - if(label.equals("Proteomics")) { + if (label.equals("Proteomics")) { handlePtxDeletionRequest(measurementDetailsComponent.getSelectedProteomicsMeasurements()); } - if(label.equals("Genomics")) { + if (label.equals("Genomics")) { handleNGSDeletionRequest(measurementDetailsComponent.getSelectedNGSMeasurements()); } } private void handlePtxDeletionRequest(Set measurements) { - if(measurements.isEmpty()) { + if (measurements.isEmpty()) { return; } MeasurementDeletionConfirmationNotification notification = - new MeasurementDeletionConfirmationNotification("Selected proteomics measurements will be deleted", measurements.size()); + new MeasurementDeletionConfirmationNotification( + "Selected proteomics measurements will be deleted", measurements.size()); notification.open(); notification.addConfirmListener(event -> { deletePtxMeasurements(measurements); @@ -230,11 +236,12 @@ private void handlePtxDeletionRequest(Set measurements) { } private void handleNGSDeletionRequest(Set measurements) { - if(measurements.isEmpty()) { + if (measurements.isEmpty()) { return; } MeasurementDeletionConfirmationNotification notification = - new MeasurementDeletionConfirmationNotification("Selected genomics measurements will be deleted", measurements.size()); + new MeasurementDeletionConfirmationNotification( + "Selected genomics measurements will be deleted", measurements.size()); notification.open(); notification.addConfirmListener(event -> { deleteNGSMeasurements(measurements); @@ -245,7 +252,7 @@ private void handleNGSDeletionRequest(Set measurements) { private void deleteNGSMeasurements(Set measurements) { Result result = measurementService.deleteNGSMeasurements( - context.projectId().orElseThrow(), measurements); + context.projectId().orElseThrow(), measurements); handleDeletionResults(result); } @@ -326,7 +333,7 @@ private void openEditMeasurementDialog() { private void downloadMetadataForSelectedTab() { Optional tabLabel = measurementDetailsComponent.getSelectedTabName(); - if(tabLabel.isEmpty()) { + if (tabLabel.isEmpty()) { return; } switch (tabLabel.get()) { @@ -356,7 +363,9 @@ private void downloadProteomicsMetadata() { .flatMap(Collection::stream) .sorted(Comparator.comparing(ProteomicsMeasurementEntry::measurementCode, natOrder) .thenComparing(ptx -> ptx.sampleInformation().sampleId(), natOrder)).toList(); - proteomicsMeasurementContentProvider.setMeasurements(result); + proteomicsMeasurementContentProvider.setMeasurements(result, + projectInformationService.find(context.projectId().orElseThrow()).orElseThrow() + .getProjectCode().value()); proteomicsDownloadProvider.trigger(); } @@ -373,7 +382,9 @@ private void downloadNGSMetadata() { // sort by measurement codes first, then by sample codes .sorted(Comparator.comparing(NGSMeasurementEntry::measurementCode, natOrder) .thenComparing(ngs -> ngs.sampleInformation().sampleId(), natOrder)).toList(); - ngsMeasurementContentProvider.setMeasurements(result); + ngsMeasurementContentProvider.setMeasurements(result, + projectInformationService.find(context.projectId().orElseThrow()).orElseThrow() + .getProjectCode().value()); ngsDownloadProvider.trigger(); } @@ -535,13 +546,13 @@ private void routeToRawData(ComponentEvent componentEvent) { } private void setSelectedMeasurementsInfo(int selectedMeasurements) { - String text = "%s measurements are currently selected.".formatted( - String.valueOf(selectedMeasurements)); + String text = "%s measurements are currently selected.".formatted( + String.valueOf(selectedMeasurements)); if (selectedMeasurements > 0) { measurementsSelectedInfoBox.getStyle().setVisibility(Visibility.INITIAL); } else { measurementsSelectedInfoBox.getStyle().setVisibility(Visibility.HIDDEN); } - measurementsSelectedInfoBox.setText(text); + measurementsSelectedInfoBox.setText(text); } } 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 f46f751bd..05d96a4ae 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 @@ -32,7 +32,7 @@ */ public class NGSMeasurementContentProvider implements DownloadContentProvider { - private static final String FILE_NAME = "ngs_measurements.xlsx"; + private static final String FILE_NAME_SUFFIX = "ngs_measurements.xlsx"; private static final Logger log = logger(NGSMeasurementContentProvider.class); private static final byte[] DARK_GREY = {119, 119, 119}; private static final byte[] LIGHT_GREY = {(byte) 220, (byte) 220, (byte) 220}; @@ -40,6 +40,8 @@ public class NGSMeasurementContentProvider implements DownloadContentProvider { private static CellStyle readOnlyHeaderStyle; 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 static void setAutoWidth(Sheet sheet) { for (int col = 0; col <= NGSMeasurementColumns.values().length; col++) { @@ -135,9 +137,10 @@ private static void createMeasurementEntry(NGSMeasurementEntry ngsMeasurementEnt setCellStyle(commentCol, NGSMeasurementColumns.COMMENT.readOnly()); } - public void setMeasurements(List measurements) { + public void setMeasurements(List measurements, String fileNamePrefix) { this.measurements.clear(); this.measurements.addAll(measurements); + this.fileNamePrefix = fileNamePrefix.trim(); } private void defineBoldStyle(Workbook workbook) { @@ -207,7 +210,7 @@ public byte[] getContent() { @Override public String getFileName() { - return FILE_NAME; + return String.join("_" , fileNamePrefix, FILE_NAME_SUFFIX); } /** 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 1f6878d79..285023830 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 @@ -31,11 +31,13 @@ */ public class ProteomicsMeasurementContentProvider implements DownloadContentProvider { - private static final String FILE_NAME = "proteomics_measurements.xlsx"; + private static final String FILE_NAME_SUFFIX = "proteomics_measurements.xlsx"; private static final Logger log = logger(ProteomicsMeasurementContentProvider.class); private static final byte[] DARK_GREY = {119, 119, 119}; private static final byte[] LIGHT_GREY = {(byte) 220, (byte) 220, (byte) 220}; private final List measurements = new LinkedList<>(); + private static final String DEFAULT_FILE_NAME_PREFIX = "QBiC"; + private String fileNamePrefix = DEFAULT_FILE_NAME_PREFIX; private static void setAutoWidth(Sheet sheet) { for (int col = 0; col <= 18; col++) { @@ -139,9 +141,10 @@ private static void createMeasurementEntry(ProteomicsMeasurementEntry pxpEntry, entry.createCell(18).setCellValue(pxpEntry.comment()); } - public void setMeasurements(List measurements) { + public void setMeasurements(List measurements, String fileNamePrefix) { this.measurements.clear(); this.measurements.addAll(measurements); + this.fileNamePrefix = fileNamePrefix; } @Override @@ -202,6 +205,6 @@ public byte[] getContent() { @Override public String getFileName() { - return FILE_NAME; + return String.join("_", fileNamePrefix, FILE_NAME_SUFFIX); } } diff --git a/user-interface/src/main/java/life/qbic/datamanager/views/projects/project/rawdata/RawDataMain.java b/user-interface/src/main/java/life/qbic/datamanager/views/projects/project/rawdata/RawDataMain.java index a6a03c67b..64ca228ce 100644 --- a/user-interface/src/main/java/life/qbic/datamanager/views/projects/project/rawdata/RawDataMain.java +++ b/user-interface/src/main/java/life/qbic/datamanager/views/projects/project/rawdata/RawDataMain.java @@ -30,6 +30,7 @@ import life.qbic.datamanager.views.projects.project.experiments.ExperimentMainLayout; import life.qbic.logging.api.Logger; import life.qbic.logging.service.LoggerFactory; +import life.qbic.projectmanagement.application.ProjectInformationService; import life.qbic.projectmanagement.application.dataset.RawDataService; import life.qbic.projectmanagement.application.experiment.ExperimentInformationService; import life.qbic.projectmanagement.application.measurement.MeasurementMetadata; @@ -74,6 +75,7 @@ public class RawDataMain extends Main implements BeforeEnterObserver { private final Disclaimer noRawDataRegisteredDisclaimer; private final String rawDataSourceURL; private final String documentationUrl; + private final ProjectInformationService projectInformationService; private transient Context context; public RawDataMain(@Autowired RawDataDetailsComponent rawDataDetailsComponent, @@ -82,7 +84,9 @@ public RawDataMain(@Autowired RawDataDetailsComponent rawDataDetailsComponent, @Autowired MeasurementService measurementService, @Autowired RawDataService rawDataService, @Value("${server.download.api.measurement.url}") String dataSourceURL, - @Value("${qbic.communication.documentation.url}") String documentationUrl) { + @Value("${qbic.communication.documentation.url}") String documentationUrl, + @Autowired ProjectInformationService projectInformationService) { + this.projectInformationService = Objects.requireNonNull(projectInformationService); this.rawdataDetailsComponent = Objects.requireNonNull(rawDataDetailsComponent); this.rawDataDownloadInformationComponent = Objects.requireNonNull( rawDataDownloadInformationComponent); @@ -160,7 +164,10 @@ private void handleUrlDownload(ComponentEvent event) { var currentExperiment = experimentInformationService.find( context.projectId().orElseThrow().value(), context.experimentId().orElseThrow()) .orElseThrow(); - urlDownloadFormatter.updateContext(currentExperiment, downloadUrls); + var currentProjectCode = projectInformationService.find(context.projectId().orElseThrow()) + .orElseThrow().getProjectCode().value(); + urlDownloadFormatter.updateContext(downloadUrls, + String.join("_", currentProjectCode, currentExperiment.getName().replace(" ", "_"))); urlDownload.trigger(); } diff --git a/user-interface/src/main/java/life/qbic/datamanager/views/projects/project/rawdata/RawDataURLContentProvider.java b/user-interface/src/main/java/life/qbic/datamanager/views/projects/project/rawdata/RawDataURLContentProvider.java index 0c9732db2..53dda15ba 100644 --- a/user-interface/src/main/java/life/qbic/datamanager/views/projects/project/rawdata/RawDataURLContentProvider.java +++ b/user-interface/src/main/java/life/qbic/datamanager/views/projects/project/rawdata/RawDataURLContentProvider.java @@ -17,6 +17,8 @@ public class RawDataURLContentProvider implements DownloadContentProvider { private Experiment experiment; private List rawDataUrls; private static final String FILE_SUFFIX = "rawdata_urls.txt"; + private static final String DEFAULT_FILE_PREFIX = "QBiC"; + private String fileNamePrefix = DEFAULT_FILE_PREFIX; @Override public byte[] getContent() { @@ -28,22 +30,14 @@ public byte[] getContent() { return textFileBuilder.getRowString().getBytes(StandardCharsets.UTF_8); } - public void updateContext(Experiment experiment, List rawDataUrls) { - this.experiment = experiment; + public void updateContext(List rawDataUrls, String fileNamePrefix) { + this.fileNamePrefix = fileNamePrefix; this.rawDataUrls = rawDataUrls; } - /** - * Provides the file name in the following format: CURRENTTIMESTAMP.EXPERIMENTNAME.FILESUFFIX. - * CURRENTTIMESTAMP is provided in the format "yyyy-MM-dd" - * EXPERIMENTNAME is provided with at most 15 characters and replacing whitespace values with underscore - * FILESUFFIX is set to "rawdata_urls.txt" - */ @Override public String getFileName() { - return String.format("%s.%s.%s", - LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd")), - fileNamePrefixFromExperimentName(experiment.getName()), FILE_SUFFIX); + return String.join("_", fileNamePrefix, FILE_SUFFIX); } private String fileNamePrefixFromExperimentName(String experimentName) { diff --git a/user-interface/src/main/java/life/qbic/datamanager/views/projects/project/samples/SampleInformationMain.java b/user-interface/src/main/java/life/qbic/datamanager/views/projects/project/samples/SampleInformationMain.java index d8dd51c6e..f04e5384f 100644 --- a/user-interface/src/main/java/life/qbic/datamanager/views/projects/project/samples/SampleInformationMain.java +++ b/user-interface/src/main/java/life/qbic/datamanager/views/projects/project/samples/SampleInformationMain.java @@ -41,6 +41,7 @@ import life.qbic.logging.api.Logger; import life.qbic.logging.service.LoggerFactory; import life.qbic.projectmanagement.application.DeletionService; +import life.qbic.projectmanagement.application.ProjectInformationService; import life.qbic.projectmanagement.application.batch.BatchRegistrationService; import life.qbic.projectmanagement.application.batch.SampleUpdateRequest; import life.qbic.projectmanagement.application.batch.SampleUpdateRequest.SampleInformation; @@ -94,6 +95,7 @@ public class SampleInformationMain extends Main implements BeforeEnterObserver { private final TextField searchField = new TextField(); private final Disclaimer noGroupsDefinedDisclaimer; private final Disclaimer noSamplesRegisteredDisclaimer; + private final ProjectInformationService projectInformationService; private transient Context context; public SampleInformationMain(@Autowired ExperimentInformationService experimentInformationService, @@ -102,7 +104,8 @@ public SampleInformationMain(@Autowired ExperimentInformationService experimentI @Autowired SampleRegistrationService sampleRegistrationService, @Autowired SampleInformationService sampleInformationService, @Autowired SampleDetailsComponent sampleDetailsComponent, - @Autowired BatchDetailsComponent batchDetailsComponent) { + @Autowired BatchDetailsComponent batchDetailsComponent, + ProjectInformationService projectInformationService) { this.experimentInformationService = Objects.requireNonNull(experimentInformationService, "ExperimentInformationService cannot be null"); this.batchRegistrationService = Objects.requireNonNull(batchRegistrationService, @@ -144,6 +147,7 @@ public SampleInformationMain(@Autowired ExperimentInformationService experimentI System.identityHashCode(batchDetailsComponent), sampleDetailsComponent.getClass().getSimpleName(), System.identityHashCode(sampleDetailsComponent))); + this.projectInformationService = projectInformationService; } private static boolean noExperimentGroupsInExperiment(Experiment experiment) { @@ -188,7 +192,9 @@ private void downloadSampleMetadata() { // sort by measurement codes first, then by sample codes .sorted(Comparator.comparing(SamplePreview::sampleCode, natOrder) .thenComparing(SamplePreview::sampleName, natOrder)).toList(); - sampleInformationXLSXProvider.setSamples(result); + sampleInformationXLSXProvider.setSamples(result, + projectInformationService.find(context.projectId().orElseThrow()).orElseThrow() + .getProjectCode().value()); metadataDownload.trigger(); } diff --git a/user-interface/src/main/java/life/qbic/datamanager/views/projects/project/samples/download/SampleInformationXLSXProvider.java b/user-interface/src/main/java/life/qbic/datamanager/views/projects/project/samples/download/SampleInformationXLSXProvider.java index 68b209a0c..f65f46d74 100644 --- a/user-interface/src/main/java/life/qbic/datamanager/views/projects/project/samples/download/SampleInformationXLSXProvider.java +++ b/user-interface/src/main/java/life/qbic/datamanager/views/projects/project/samples/download/SampleInformationXLSXProvider.java @@ -33,9 +33,11 @@ */ public class SampleInformationXLSXProvider implements DownloadContentProvider { - private static final String FILE_NAME = "sample_information.xlsx"; + private static final String FILE_NAME_SUFFIX = "sample_information.xlsx"; private static final Logger log = logger(SampleInformationXLSXProvider.class); private final List samples = new ArrayList<>(); + private static final String DEFAULT_FILE_PREFIX = "QBiC"; + private String fileNamePrefix = DEFAULT_FILE_PREFIX; private static void setAutoWidth(Sheet sheet) { @@ -147,15 +149,16 @@ private void createSampleInfoEntry(SamplePreview sample, Row sampleRow, } } - public void setSamples(List samplePreviews) { + public void setSamples(List samplePreviews, String fileNamePrefix) { this.samples.clear(); this.samples.addAll(samplePreviews); this.samples.sort(Comparator.comparing(SamplePreview::sampleCode)); + this.fileNamePrefix = fileNamePrefix; } @Override public String getFileName() { - return FILE_NAME; + return String.join("_", fileNamePrefix, FILE_NAME_SUFFIX); } enum SamplePreviewColumn {