diff --git a/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/AbstractImageDatasetUploader.java b/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/AbstractImageDatasetUploader.java index a182385cdbf765ba6bedb33206d813c65973b138..02d17e343f119cb5b98b6016b2f71ba27c2b5155 100644 --- a/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/AbstractImageDatasetUploader.java +++ b/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/AbstractImageDatasetUploader.java @@ -285,7 +285,8 @@ abstract class AbstractImageDatasetUploader private ImgImageZoomLevelDTO convert(long imageContainerDatasetId, ImageZoomLevel imageZoomLevel) { return new ImgImageZoomLevelDTO(imageZoomLevel.getPhysicalDatasetPermId(), - imageZoomLevel.isOriginal(), imageContainerDatasetId); + imageZoomLevel.isOriginal(), imageZoomLevel.getRootPath(), + imageZoomLevel.getWidth(), imageZoomLevel.getHeight(), imageContainerDatasetId); } private static ImgImageDatasetDTO createImageDatasetDTO(String datasetPermId, diff --git a/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/AbstractImageFileExtractor.java b/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/AbstractImageFileExtractor.java index 9a6c7c0e54bde62f200adccd64c56325bfbd1c94..243858f2ffc9a28b8c74d231f46777c71296625a 100644 --- a/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/AbstractImageFileExtractor.java +++ b/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/AbstractImageFileExtractor.java @@ -41,7 +41,7 @@ import ch.systemsx.cisd.common.logging.LogCategory; import ch.systemsx.cisd.common.logging.LogFactory; import ch.systemsx.cisd.common.utilities.PropertyUtils; import ch.systemsx.cisd.openbis.dss.etl.dto.RelativeImageFile; -import ch.systemsx.cisd.openbis.dss.etl.dto.api.impl.ThumbnailFilePaths; +import ch.systemsx.cisd.openbis.dss.etl.dto.api.impl.ThumbnailsInfo; import ch.systemsx.cisd.openbis.dss.etl.dto.api.v1.Channel; import ch.systemsx.cisd.openbis.dss.etl.dto.api.v1.ImageFileInfo; import ch.systemsx.cisd.openbis.dss.etl.dto.api.v1.ImageIdentifier; @@ -457,7 +457,7 @@ abstract public class AbstractImageFileExtractor implements IImageFileExtractor } public final static List<AcquiredSingleImage> createImagesWithNoColorComponent( - ImageFileInfo imageInfo, ThumbnailFilePaths thumbnailFilePathsOrNull) + ImageFileInfo imageInfo, ThumbnailsInfo thumbnailFilePathsOrNull) { List<AcquiredSingleImage> images = new ArrayList<AcquiredSingleImage>(); images.add(createImage(imageInfo, imageInfo.getChannelCode(), null, @@ -467,7 +467,7 @@ abstract public class AbstractImageFileExtractor implements IImageFileExtractor public final static AcquiredSingleImage createImage(ImageFileInfo imageInfo, String channelCode, ColorComponent colorComponentOrNull, - ThumbnailFilePaths thumbnailFilePathsOrNull) + ThumbnailsInfo thumbnailFilePathsOrNull) { RelativeImageReference relativeImageRef = new RelativeImageReference(imageInfo.getImageRelativePath(), diff --git a/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/AbstractImageStorageProcessor.java b/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/AbstractImageStorageProcessor.java index 76bc4d5bf69712f2b22d7bf94ceec92e31ac28eb..b829a8713af74a64a430e9a4a20ce473876d0f6e 100644 --- a/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/AbstractImageStorageProcessor.java +++ b/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/AbstractImageStorageProcessor.java @@ -63,7 +63,7 @@ import ch.systemsx.cisd.openbis.dss.etl.dataaccess.IImagingQueryDAO; import ch.systemsx.cisd.openbis.dss.etl.dto.ImageSeriesPoint; import ch.systemsx.cisd.openbis.dss.etl.dto.api.impl.ImageDataSetInformation; import ch.systemsx.cisd.openbis.dss.etl.dto.api.impl.ImageDataSetStructure; -import ch.systemsx.cisd.openbis.dss.etl.dto.api.impl.ThumbnailFilePaths; +import ch.systemsx.cisd.openbis.dss.etl.dto.api.impl.ThumbnailsInfo; import ch.systemsx.cisd.openbis.dss.etl.dto.api.v1.Channel; import ch.systemsx.cisd.openbis.dss.etl.dto.api.v1.ChannelColorComponent; import ch.systemsx.cisd.openbis.dss.etl.dto.api.v1.ImageFileInfo; @@ -674,8 +674,9 @@ abstract class AbstractImageStorageProcessor extends AbstractStorageProcessor im new Geometry(imageDataSetStructure.getTileRowsNumber(), imageDataSetStructure.getTileColumnsNumber()); - ThumbnailFilePaths thumbnailFilePaths = dataSetInformation.tryGetThumbnailFilePaths(); - List<AcquiredSingleImage> images = convertImages(imageDataSetStructure, thumbnailFilePaths); + ThumbnailsInfo thumbnailsInfo = dataSetInformation.getThumbnailsInfos(); + + List<AcquiredSingleImage> images = convertImages(imageDataSetStructure, thumbnailsInfo); List<File> invalidFiles = new ArrayList<File>(); // handles in an earlier phase ImageStorageConfiguraton imageStorageConfiguraton = @@ -694,18 +695,15 @@ abstract class AbstractImageStorageProcessor extends AbstractStorageProcessor im imageStorageConfiguraton.getStoreChannelsOnExperimentLevel(), imageStorageConfiguraton.tryGetImageLibrary()); - String thumbnailDatasetPermIdOrNull = - (thumbnailFilePaths == null) ? null : thumbnailFilePaths - .getThumbnailPhysicalDatasetPermId(); ImageDatasetOwnerInformation imageDatasetOwner = ImageDatasetOwnerInformation.create(dataSetInformation.getContainerDatasetPermId(), - dataSetInformation, thumbnailDatasetPermIdOrNull); + dataSetInformation, thumbnailsInfo); return new ImageFileExtractionWithConfig(imageDatasetOwner, extractionResult, imageStorageConfiguraton); } private static List<AcquiredSingleImage> convertImages( - ImageDataSetStructure imageDataSetStructure, ThumbnailFilePaths thumbnailFilePathsOrNull) + ImageDataSetStructure imageDataSetStructure, ThumbnailsInfo thumbnailFilePathsOrNull) { List<ImageFileInfo> imageInfos = imageDataSetStructure.getImages(); List<ChannelColorComponent> channelColorComponentsOrNull = diff --git a/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/Hdf5ThumbnailGenerator.java b/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/Hdf5ThumbnailGenerator.java index f3145f15c3af77f014aa659e739b142817c038a7..a77b2e34b0f864092c2b8d56c1c17cc0b04e54c8 100644 --- a/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/Hdf5ThumbnailGenerator.java +++ b/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/Hdf5ThumbnailGenerator.java @@ -46,7 +46,10 @@ import ch.systemsx.cisd.common.process.ProcessIOStrategy; import ch.systemsx.cisd.common.process.ProcessResult; import ch.systemsx.cisd.openbis.dss.etl.dto.ImageLibraryInfo; import ch.systemsx.cisd.openbis.dss.etl.dto.RelativeImageFile; -import ch.systemsx.cisd.openbis.dss.etl.dto.api.impl.ThumbnailFilePaths; +import ch.systemsx.cisd.openbis.dss.etl.dto.api.impl.ImageDataSetStructure; +import ch.systemsx.cisd.openbis.dss.etl.dto.api.impl.ThumbnailsInfo; +import ch.systemsx.cisd.openbis.dss.etl.dto.api.v1.ImageFileInfo; +import ch.systemsx.cisd.openbis.dss.etl.dto.api.v1.ImageIdentifier; import ch.systemsx.cisd.openbis.dss.etl.dto.api.v1.ImageStorageConfiguraton; import ch.systemsx.cisd.openbis.dss.etl.dto.api.v1.ThumbnailsStorageFormat; import ch.systemsx.cisd.openbis.dss.generic.shared.utils.ImageUtil; @@ -58,41 +61,52 @@ import ch.systemsx.cisd.openbis.dss.generic.shared.utils.ImageUtil; */ public class Hdf5ThumbnailGenerator implements IHDF5WriterClient { + private static class ThumbnailData + { + private final byte[] data; + + private final int width; + + private final int height; + + private ThumbnailData(byte[] data, int width, int height) + { + this.data = data; + this.width = width; + this.height = height; + } + } + /** * Generates thumbnails of specified images whose paths are relative to the specified image * parent directory. * - * @param images path to the images relative to the imagesParentDirectory + * @param imageDataSetStructure the images dataset structure, including paths relative to the + * imagesParentDirectory * @param thumbnailFilePath absolute path to the file where thumbnails will be saved * @param imageStorageConfiguraton describes how the thumbnails should be generated - * @return null if thumbnails generation was not requested */ - public static ThumbnailFilePaths tryGenerateThumbnails(List<RelativeImageFile> images, + public static void tryGenerateThumbnails(ImageDataSetStructure imageDataSetStructure, File imagesParentDirectory, String thumbnailFilePath, ImageStorageConfiguraton imageStorageConfiguraton, String thumbnailPhysicalDatasetPermId, - ThumbnailsStorageFormat thumbnailsStorageFormatOrNull) + ThumbnailsStorageFormat thumbnailsStorageFormatOrNull, ThumbnailsInfo thumbnailPaths) { if (thumbnailsStorageFormatOrNull != null) { - ThumbnailFilePaths thumbnailPaths = - new ThumbnailFilePaths(thumbnailPhysicalDatasetPermId); - + thumbnailPaths.putDataSet(thumbnailPhysicalDatasetPermId, + thumbnailsStorageFormatOrNull.getThumbnailsFileName()); File thumbnailsFile = new File(thumbnailFilePath); final String relativeThumbnailFilePath = thumbnailsFile.getName(); HDF5Container container = new HDF5Container(thumbnailsFile); ImageLibraryInfo imageLibrary = imageStorageConfiguraton.tryGetImageLibrary(); Hdf5ThumbnailGenerator thumbnailsGenerator = - new Hdf5ThumbnailGenerator(images, imagesParentDirectory, - thumbnailsStorageFormatOrNull, imageLibrary, relativeThumbnailFilePath, - thumbnailPaths, operationLog); + new Hdf5ThumbnailGenerator(imageDataSetStructure, imagesParentDirectory, + thumbnailPhysicalDatasetPermId, thumbnailsStorageFormatOrNull, + imageLibrary, relativeThumbnailFilePath, thumbnailPaths, operationLog); container.runWriterClient(thumbnailsStorageFormatOrNull.isStoreCompressed(), thumbnailsGenerator); - return thumbnailPaths; - } else - { - return null; } } @@ -106,27 +120,31 @@ public class Hdf5ThumbnailGenerator implements IHDF5WriterClient private static final int MAX_RETRY_OF_FAILED_GENERATION = 3; - private final List<RelativeImageFile> images; + private final ImageDataSetStructure imageDataSetStructure; private final File imagesParentDirectory; + private String thumbnailPhysicalDatasetPermId; + private final ThumbnailsStorageFormat thumbnailsStorageFormat; private final ImageLibraryInfo imageLibraryOrNull; private final String relativeThumbnailFilePath; - private final ThumbnailFilePaths thumbnailPathCollector; + private final ThumbnailsInfo thumbnailPathCollector; private final Logger logger; - private Hdf5ThumbnailGenerator(List<RelativeImageFile> images, File imagesParentDirectory, + private Hdf5ThumbnailGenerator(ImageDataSetStructure imageDataSetStructure, + File imagesParentDirectory, String thumbnailPhysicalDatasetPermId, ThumbnailsStorageFormat thumbnailsStorageFormat, ImageLibraryInfo imageLibraryOrNull, - String relativeThumbnailFilePath, ThumbnailFilePaths thumbnailPathCollector, + String relativeThumbnailFilePath, ThumbnailsInfo thumbnailPathCollector, Logger operationLog) { - this.images = images; + this.imageDataSetStructure = imageDataSetStructure; this.imagesParentDirectory = imagesParentDirectory; + this.thumbnailPhysicalDatasetPermId = thumbnailPhysicalDatasetPermId; this.thumbnailsStorageFormat = thumbnailsStorageFormat; this.imageLibraryOrNull = imageLibraryOrNull; this.relativeThumbnailFilePath = relativeThumbnailFilePath; @@ -139,7 +157,7 @@ public class Hdf5ThumbnailGenerator implements IHDF5WriterClient * the thumbnail. Using it allows not to allocate memory each time when a thumbnail * is generated. */ - private Status generateThumbnail(IHDF5ContainerWriter writer, RelativeImageFile image, + private Status generateThumbnail(IHDF5ContainerWriter writer, ImageFileInfo image, ByteArrayOutputStream bufferOutputStream) { String imagePath = image.getImageRelativePath(); @@ -149,24 +167,23 @@ public class Hdf5ThumbnailGenerator implements IHDF5WriterClient try { long start = System.currentTimeMillis(); - String imageIdOrNull = image.tryGetImageID(); - byte[] byteArray = generateThumbnail(bufferOutputStream, img, imageIdOrNull); - String path = - relativeThumbnailFilePath + AbstractImageStorageProcessor.ARCHIVE_DELIMITER - + thumbnailPath; - thumbnailPathCollector.saveThumbnailPath(image, path); + String imageIdOrNull = tryExtractImageID(image); + ThumbnailData thumbnailData = generateThumbnail(bufferOutputStream, img, imageIdOrNull); + thumbnailPathCollector.saveThumbnailPath(thumbnailPhysicalDatasetPermId, + RelativeImageFile.create(image), thumbnailPath, thumbnailData.width, + thumbnailData.height); if (logger.isDebugEnabled()) { long now = System.currentTimeMillis(); logger.debug(Thread.currentThread().getName() + " thumbnail " + thumbnailPath - + " (" + byteArray.length + " bytes) generated in " + (now - start) - + " msec"); + + " (" + thumbnailData.data.length + " bytes) generated in " + + (now - start) + " msec"); } synchronized (writer) { - writer.writeToHDF5Container(thumbnailPath, new ByteArrayInputStream(byteArray), - byteArray.length); + writer.writeToHDF5Container(thumbnailPath, new ByteArrayInputStream( + thumbnailData.data), thumbnailData.data.length); } } catch (IOException ex) { @@ -175,7 +192,7 @@ public class Hdf5ThumbnailGenerator implements IHDF5WriterClient return Status.OK; } - private String createThumbnailPath(RelativeImageFile plateImage) + private String createThumbnailPath(ImageFileInfo plateImage) { String imagePath = plateImage.getImageRelativePath(); String newImagePath = imagePath; @@ -184,7 +201,8 @@ public class Hdf5ThumbnailGenerator implements IHDF5WriterClient { newImagePath = imagePath.substring(0, lastIndex); } - String imageIdOrNull = plateImage.tryGetImageID(); + + String imageIdOrNull = tryExtractImageID(plateImage); if (imageIdOrNull != null) { newImagePath += "_" + imageIdOrNull; @@ -193,37 +211,39 @@ public class Hdf5ThumbnailGenerator implements IHDF5WriterClient return newImagePath; } - private byte[] generateThumbnail(ByteArrayOutputStream bufferOutputStream, File img, + private static String tryExtractImageID(ImageFileInfo image) + { + ImageIdentifier imageIdentifier = image.tryGetImageIdentifier(); + return imageIdentifier == null ? null : imageIdentifier.getUniqueStringIdentifier(); + } + + private ThumbnailData generateThumbnail(ByteArrayOutputStream bufferOutputStream, File img, String imageIdOrNull) throws IOException { - byte[] byteArray; + ThumbnailData thumbnailData; if (thumbnailsStorageFormat.isGenerateWithImageMagic()) { - byteArray = generateThumbnailWithImageMagic(img); + thumbnailData = generateThumbnailWithImageMagic(img); } else { - byteArray = generateThumbnailInternally(img, imageIdOrNull, bufferOutputStream); + thumbnailData = generateThumbnailInternally(img, imageIdOrNull, bufferOutputStream); } - return byteArray; + return thumbnailData; } - private byte[] generateThumbnailWithImageMagic(File imageFile) throws IOException + private ThumbnailData generateThumbnailWithImageMagic(File imageFile) throws IOException { - String size = - thumbnailsStorageFormat.getMaxWidth() + "x" - + thumbnailsStorageFormat.getMaxHeight(); + int width = thumbnailsStorageFormat.getMaxWidth(); + int height = thumbnailsStorageFormat.getMaxHeight(); if (thumbnailsStorageFormat.getZoomLevel() != null) { Dimension originalSize = loadUnchangedImageDimension(imageFile, null); - long x = - (int) Math.round(thumbnailsStorageFormat.getZoomLevel() - * originalSize.getWidth()); - long y = - (int) Math.round(thumbnailsStorageFormat.getZoomLevel() - * originalSize.getHeight()); - size = x + "x" + y; + double zoomLevel = thumbnailsStorageFormat.getZoomLevel(); + width = (int) Math.round(zoomLevel * originalSize.getWidth()); + height = (int) Math.round(zoomLevel * originalSize.getHeight()); } + String size = width + "x" + height; String imageFilePath = imageFile.getPath(); List<String> params = new ArrayList<String>(); @@ -245,28 +265,29 @@ public class Hdf5ThumbnailGenerator implements IHDF5WriterClient imageFilePath, result.getExitValue(), result.getProcessIOResult().getStatus())); } else { - return result.getBinaryOutput(); + return new ThumbnailData(result.getBinaryOutput(), width, height); } } - private byte[] generateThumbnailInternally(File imageFile, String imageIdOrNull, + private ThumbnailData generateThumbnailInternally(File imageFile, String imageIdOrNull, ByteArrayOutputStream bufferOutputStream) throws IOException { BufferedImage image = loadUnchangedImage(imageFile, imageIdOrNull); - long x = thumbnailsStorageFormat.getMaxWidth(); - long y = thumbnailsStorageFormat.getMaxHeight(); + int widht = thumbnailsStorageFormat.getMaxWidth(); + int height = thumbnailsStorageFormat.getMaxHeight(); if (thumbnailsStorageFormat.getZoomLevel() != null) { - x = Math.round(thumbnailsStorageFormat.getZoomLevel() * image.getWidth()); - y = Math.round(thumbnailsStorageFormat.getZoomLevel() * image.getHeight()); + widht = (int) Math.round(thumbnailsStorageFormat.getZoomLevel() * image.getWidth()); + height = (int) Math.round(thumbnailsStorageFormat.getZoomLevel() * image.getHeight()); } BufferedImage thumbnail = - ImageUtil.rescale(image, (int) x, (int) y, false, + ImageUtil.rescale(image, widht, height, false, thumbnailsStorageFormat.isHighQuality()); ImageUtil.writeImageToPng(thumbnail, bufferOutputStream); - return bufferOutputStream.toByteArray(); + return new ThumbnailData(bufferOutputStream.toByteArray(), thumbnail.getWidth(), + thumbnail.getHeight()); } private BufferedImage loadUnchangedImage(File imageFile, String imageIdOrNull) @@ -288,10 +309,9 @@ public class Hdf5ThumbnailGenerator implements IHDF5WriterClient thumbnailPath, ex.getMessage())); } - private ITaskExecutor<RelativeImageFile> createThumbnailGenerator( - final IHDF5ContainerWriter writer) + private ITaskExecutor<ImageFileInfo> createThumbnailGenerator(final IHDF5ContainerWriter writer) { - return new ITaskExecutor<RelativeImageFile>() + return new ITaskExecutor<ImageFileInfo>() { private ThreadLocal<ByteArrayOutputStream> outputStreamBuffers = new ThreadLocal<ByteArrayOutputStream>() @@ -303,7 +323,7 @@ public class Hdf5ThumbnailGenerator implements IHDF5WriterClient } }; - public Status execute(RelativeImageFile image) + public Status execute(ImageFileInfo image) { // each thread will get its own buffer to avoid allocating memory for the // internal array each time @@ -316,8 +336,9 @@ public class Hdf5ThumbnailGenerator implements IHDF5WriterClient public void runWithSimpleWriter(IHDF5ContainerWriter writer) { - Collection<FailureRecord<RelativeImageFile>> errors = - ParallelizedExecutor.process(images, createThumbnailGenerator(writer), + Collection<FailureRecord<ImageFileInfo>> errors = + ParallelizedExecutor.process(imageDataSetStructure.getImages(), + createThumbnailGenerator(writer), thumbnailsStorageFormat.getAllowedMachineLoadDuringGeneration(), 100, "Thumbnails generation", MAX_RETRY_OF_FAILED_GENERATION, true); if (errors.size() > 0) diff --git a/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/IImagingDatasetLoader.java b/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/IImagingDatasetLoader.java index 79d1a850af5d97206d1753bfd7bdc9886075fb51..637641bf03caec39cf50925a98f19003d133af11 100644 --- a/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/IImagingDatasetLoader.java +++ b/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/IImagingDatasetLoader.java @@ -72,9 +72,10 @@ public interface IImagingDatasetLoader extends IImageDatasetLoader * @param channelCode channel code for which representative image is requested * @param wellLocationOrNull if not null the returned images are restricted to one well. * Otherwise the dataset is assumed to have no container and spots. + * @param imageSize */ AbsoluteImageReference tryGetRepresentativeThumbnail(String channelCode, - Location wellLocationOrNull); + Location wellLocationOrNull, RequestedImageSize imageSize); /** * Returns the stored thumbnail for the given parameters, or <code>null</code>, if no thumbnail @@ -85,8 +86,9 @@ public interface IImagingDatasetLoader extends IImageDatasetLoader * * @param channelCode The code of the channel to get the thumbnail for. * @param channelStackReference Specifies well and tile of the thumbnail. + * @param imageSize */ AbsoluteImageReference tryGetThumbnail(String channelCode, - ImageChannelStackReference channelStackReference); + ImageChannelStackReference channelStackReference, RequestedImageSize imageSize); } \ No newline at end of file diff --git a/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/ImagingDatabaseVersionHolder.java b/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/ImagingDatabaseVersionHolder.java index f8d8f792c7fc7f75a3e543d90b5a4e0b0680b61a..7cc8191234f34f72c5cbc1c1c0d7a540447cbb95 100644 --- a/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/ImagingDatabaseVersionHolder.java +++ b/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/ImagingDatabaseVersionHolder.java @@ -28,7 +28,7 @@ public class ImagingDatabaseVersionHolder implements IDatabaseVersionHolder public String getDatabaseVersion() { - return "018"; // changed in S119 + return "019"; // changed in S121 } } diff --git a/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/ImagingLoaderStrategyFactory.java b/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/ImagingLoaderStrategyFactory.java index c7da4d9422045778dcec4a03ca04742a5e7b4aac..bdcd247549df44d3defb06f790a7ebdc9b01388c 100644 --- a/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/ImagingLoaderStrategyFactory.java +++ b/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/ImagingLoaderStrategyFactory.java @@ -59,14 +59,15 @@ public class ImagingLoaderStrategyFactory ImageChannelStackReference channelStackReference, RequestedImageSize imageSize) { - return this.imageAccessor.tryGetThumbnail(channelCode, channelStackReference); + return this.imageAccessor.tryGetThumbnail(channelCode, channelStackReference, + imageSize); } public AbsoluteImageReference tryGetRepresentativeImage(String channelCode, Location wellLocationOrNull, RequestedImageSize imageSize) { return imageAccessor.tryGetRepresentativeThumbnail(channelCode, - wellLocationOrNull); + wellLocationOrNull, imageSize); } }; diff --git a/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/PlateStorageProcessor.java b/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/PlateStorageProcessor.java index 5fd8d4e23a79fcdd7a77db17cc3b59d3d31798c5..1056eb91ddf65366f951b6ed05110729032f14c3 100644 --- a/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/PlateStorageProcessor.java +++ b/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/PlateStorageProcessor.java @@ -23,13 +23,16 @@ import java.util.Properties; import ch.systemsx.cisd.bds.hcs.Geometry; import ch.systemsx.cisd.common.mail.IMailClient; +import ch.systemsx.cisd.common.shared.basic.utils.StringUtils; import ch.systemsx.cisd.common.utilities.AbstractHashable; import ch.systemsx.cisd.common.utilities.PropertyUtils; import ch.systemsx.cisd.openbis.dss.etl.dataaccess.IImagingQueryDAO; import ch.systemsx.cisd.openbis.dss.etl.dto.ImageDatasetInfo; import ch.systemsx.cisd.openbis.dss.etl.dto.ImageLibraryInfo; import ch.systemsx.cisd.openbis.dss.etl.dto.ImageZoomLevel; +import ch.systemsx.cisd.openbis.dss.etl.dto.api.impl.ThumbnailsInfo; import ch.systemsx.cisd.openbis.dss.generic.shared.dto.DataSetInformation; +import ch.systemsx.cisd.openbis.dss.generic.shared.dto.Size; import ch.systemsx.cisd.openbis.generic.shared.basic.dto.Experiment; import ch.systemsx.cisd.openbis.generic.shared.basic.dto.IEntityProperty; import ch.systemsx.cisd.openbis.generic.shared.basic.dto.Sample; @@ -146,33 +149,47 @@ public final class PlateStorageProcessor extends AbstractImageStorageProcessor public static class ImageDatasetOwnerInformation extends DatasetOwnerInformation { public static ImageDatasetOwnerInformation create(String containerDatasetPermId, - DataSetInformation originalDataset, String thumbnailDatasetPermIdOrNull) + DataSetInformation originalDataset, ThumbnailsInfo thumbnailsInfosOrNull) { return new ImageDatasetOwnerInformation(containerDatasetPermId, originalDataset, - thumbnailDatasetPermIdOrNull); + thumbnailsInfosOrNull); } private final List<ImageZoomLevel> imageZoomLevels; private ImageDatasetOwnerInformation(String containerDatasetPermId, - DataSetInformation originalDataset, String thumbnailDatasetPermIdOrNull) + DataSetInformation originalDataset, ThumbnailsInfo thumbnailsInfosOrNull) { super(containerDatasetPermId, originalDataset); - this.imageZoomLevels = createZoomLevels(originalDataset, thumbnailDatasetPermIdOrNull); + this.imageZoomLevels = createZoomLevels(originalDataset, thumbnailsInfosOrNull); } private static List<ImageZoomLevel> createZoomLevels(DataSetInformation originalDataset, - String thumbnailDatasetPermIdOrNull) + ThumbnailsInfo thumbnailsInfosOrNull) { List<ImageZoomLevel> zoomLevels = new ArrayList<ImageZoomLevel>(); + ImageZoomLevel originalZoomLevel = - new ImageZoomLevel(originalDataset.getDataSetCode(), true); + new ImageZoomLevel(originalDataset.getDataSetCode(), true, + StringUtils.EMPTY_STRING, null, null); zoomLevels.add(originalZoomLevel); - if (thumbnailDatasetPermIdOrNull != null) + if (thumbnailsInfosOrNull != null) { - ImageZoomLevel thumbnailZoomLevel = - new ImageZoomLevel(thumbnailDatasetPermIdOrNull, false); - zoomLevels.add(thumbnailZoomLevel); + for (String permId : thumbnailsInfosOrNull.getThumbnailPhysicalDatasetsPermIds()) + { + Integer width = null, height = null; + Size dimension = thumbnailsInfosOrNull.tryGetDimension(permId); + if (dimension != null) + { + width = dimension.getWidth(); + height = dimension.getHeight(); + } + String rootPath = thumbnailsInfosOrNull.getRootPath(permId); + + ImageZoomLevel thumbnailZoomLevel = + new ImageZoomLevel(permId, false, rootPath, width, height); + zoomLevels.add(thumbnailZoomLevel); + } } return zoomLevels; } @@ -193,7 +210,7 @@ public final class PlateStorageProcessor extends AbstractImageStorageProcessor extractionResult, operationLog, notificationLog, notifyIfPlateIncomplete); return validator.validateImages(); } - + private void checkDataSetInformation(final DatasetOwnerInformation dataSetInformation) { assert dataSetInformation != null : "Unspecified data set information"; diff --git a/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/PrefixedImage.java b/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/PrefixedImage.java new file mode 100644 index 0000000000000000000000000000000000000000..47e56572bf4c96c88c36fa303b6b0d9375a175e8 --- /dev/null +++ b/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/PrefixedImage.java @@ -0,0 +1,63 @@ +/* + * Copyright 2011 ETH Zuerich, CISD + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package ch.systemsx.cisd.openbis.dss.etl; + +import ch.systemsx.cisd.base.image.IImageTransformerFactory; +import ch.systemsx.cisd.common.shared.basic.utils.StringUtils; +import ch.systemsx.cisd.openbis.plugin.screening.shared.imaging.dataaccess.ColorComponent; +import ch.systemsx.cisd.openbis.plugin.screening.shared.imaging.dataaccess.ImgImageDTO; + +/** + * @author Pawel Glyzewski + */ +public class PrefixedImage +{ + private final String pathPrefix; + + private final ImgImageDTO image; + + public PrefixedImage(String pathPrefix, ImgImageDTO image) + { + this.pathPrefix = pathPrefix; + this.image = image; + } + + public String getFilePath() + { + if (StringUtils.isBlank(pathPrefix)) + { + return image.getFilePath(); + } + return pathPrefix + "/" + image.getFilePath(); + } + + public ColorComponent getColorComponent() + { + return image.getColorComponent(); + } + + public String getImageID() + { + return image.getImageID(); + } + + public IImageTransformerFactory tryGetImageTransformerFactory() + { + return image.tryGetImageTransformerFactory(); + } + +} diff --git a/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/dataaccess/IImagingQueryDAO.java b/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/dataaccess/IImagingQueryDAO.java index 74f282d01400ab3e7564d2fb3a875a805684edf9..6861f592a41279730db71f26fa7f819032452f1c 100644 --- a/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/dataaccess/IImagingQueryDAO.java +++ b/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/dataaccess/IImagingQueryDAO.java @@ -90,8 +90,8 @@ public interface IImagingQueryDAO extends TransactionQuery, IImagingReadonlyQuer + "?{1.imageLibraryName}, ?{1.imageReaderName}) returning ID") public long addImageDataset(ImgImageDatasetDTO dataset); - @Select("insert into image_zoom_levels (physical_dataset_perm_id, is_original, container_dataset_id) " - + "values(?{1.physicalDatasetPermId}, ?{1.isOriginal}, ?{1.containerDatasetId}) returning ID") + @Select("insert into image_zoom_levels (physical_dataset_perm_id, is_original, container_dataset_id, path, width, height) " + + "values(?{1.physicalDatasetPermId}, ?{1.isOriginal}, ?{1.containerDatasetId}, ?{1.rootPath}, ?{1.width}, ?{1.height}) returning ID") public long addImageZoomLevel(ImgImageZoomLevelDTO dataset); @Select("insert into ANALYSIS_DATA_SETS (PERM_ID, CONT_ID) " diff --git a/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/dto/ImageZoomLevel.java b/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/dto/ImageZoomLevel.java index 46844c2b06428307642191af3336ed5d9dee0f59..ff13e2ac6e252b7d785046f584365a8f20012662 100644 --- a/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/dto/ImageZoomLevel.java +++ b/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/dto/ImageZoomLevel.java @@ -27,10 +27,20 @@ public class ImageZoomLevel private final boolean isOriginal; - public ImageZoomLevel(String physicalDatasetPermId, boolean isOriginal) + private final String rootPath; + + private final Integer width; + + private final Integer height; + + public ImageZoomLevel(String physicalDatasetPermId, boolean isOriginal, String rootPath, + Integer width, Integer height) { this.physicalDatasetPermId = physicalDatasetPermId; this.isOriginal = isOriginal; + this.rootPath = rootPath; + this.width = width; + this.height = height; } public String getPhysicalDatasetPermId() @@ -43,4 +53,18 @@ public class ImageZoomLevel return isOriginal; } + public Integer getWidth() + { + return width; + } + + public Integer getHeight() + { + return height; + } + + public String getRootPath() + { + return rootPath; + } } diff --git a/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/dto/api/impl/ImageDataSetInformation.java b/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/dto/api/impl/ImageDataSetInformation.java index 26514bce22ade9d2f97433585640063e31605989..3505d444113fa804968d8d89733d4bd8748b4cf3 100644 --- a/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/dto/api/impl/ImageDataSetInformation.java +++ b/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/dto/api/impl/ImageDataSetInformation.java @@ -42,7 +42,7 @@ public class ImageDataSetInformation extends BasicDataSetInformation private ImageDataSetStructure imageDataSetStructure; - private ThumbnailFilePaths thumbnailFilePathsOrNull; + private ThumbnailsInfo thumbnailsInfos; private String containerDatasetPermId; @@ -57,9 +57,9 @@ public class ImageDataSetInformation extends BasicDataSetInformation } /** @returns null if no thumbnails will be stored */ - public ThumbnailFilePaths tryGetThumbnailFilePaths() + public ThumbnailsInfo getThumbnailsInfos() { - return thumbnailFilePathsOrNull; + return thumbnailsInfos; } public ImageDataSetStructure getImageDataSetStructure() @@ -88,9 +88,9 @@ public class ImageDataSetInformation extends BasicDataSetInformation this.containerDatasetPermId = containerDatasetPermId; } - public void setThumbnailFilePaths(ThumbnailFilePaths thumbnailFilePathsOrNull) + public void setThumbnailsInfo(ThumbnailsInfo thumbnailsInfo) { - this.thumbnailFilePathsOrNull = thumbnailFilePathsOrNull; + this.thumbnailsInfos = thumbnailsInfo; } public void setImageDataSetStructure(ImageDataSetStructure imageDataSetStructure) @@ -107,9 +107,9 @@ public class ImageDataSetInformation extends BasicDataSetInformation appendNameAndObject(buffer, "images structure", imageDataSetStructure); appendNameAndObject(buffer, "container dataset", containerDatasetPermId); appendNameAndObject(buffer, "original dataset", this.getDataSetCode()); - if (this.tryGetThumbnailFilePaths() != null) + if (getThumbnailsInfos() != null) { - appendNameAndObject(buffer, "thumbnail", this.tryGetThumbnailFilePaths()); + appendNameAndObject(buffer, "thumbnail", getThumbnailsInfos()); } else { appendNameAndObject(buffer, "thumbnail", "none"); diff --git a/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/dto/api/impl/ThumbnailFilePaths.java b/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/dto/api/impl/ThumbnailFilePaths.java deleted file mode 100644 index 1cb5d037e62a9600bd4b46bdbfc8ff5a80b768dc..0000000000000000000000000000000000000000 --- a/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/dto/api/impl/ThumbnailFilePaths.java +++ /dev/null @@ -1,56 +0,0 @@ -package ch.systemsx.cisd.openbis.dss.etl.dto.api.impl; - -import java.util.HashMap; -import java.util.Map; - -import ch.systemsx.cisd.openbis.dss.etl.dto.RelativeImageFile; -import ch.systemsx.cisd.openbis.dss.generic.shared.utils.ToStringUtil; - -/** - * Stores the mapping between image and their thumbnails paths. Thread-safe class. - * - * @author Tomasz Pylak - */ -public class ThumbnailFilePaths -{ - private final Map<RelativeImageFile, String> imageToThumbnailPathMap; - - private final String thumbnailPhysicalDatasetPermId; - - public ThumbnailFilePaths(String thumbnailPhysicalDatasetPermId) - { - assert thumbnailPhysicalDatasetPermId != null : "thumbnailPhysicalDatasetPermId is null"; - - this.thumbnailPhysicalDatasetPermId = thumbnailPhysicalDatasetPermId; - this.imageToThumbnailPathMap = new HashMap<RelativeImageFile, String>(); - } - - /** - * Saves the path to the thumbnail for the specified image. The thumbnail path is relative and - * starts with the name of the thumbnail file. - */ - public synchronized void saveThumbnailPath(RelativeImageFile image, String thumbnailRelativePath) - { - imageToThumbnailPathMap.put(image, thumbnailRelativePath); - } - - public synchronized String getThumbnailPath(RelativeImageFile image) - { - return imageToThumbnailPathMap.get(image); - } - - public String getThumbnailPhysicalDatasetPermId() - { - return thumbnailPhysicalDatasetPermId; - } - - @Override - public String toString() - { - final StringBuilder buffer = new StringBuilder(); - ToStringUtil.appendNameAndObject(buffer, "number of thumbnails", - imageToThumbnailPathMap.size()); - ToStringUtil.appendNameAndObject(buffer, "dataset perm id", thumbnailPhysicalDatasetPermId); - return buffer.toString(); - } -} \ No newline at end of file diff --git a/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/dto/api/impl/ThumbnailsInfo.java b/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/dto/api/impl/ThumbnailsInfo.java new file mode 100644 index 0000000000000000000000000000000000000000..d72cb466a7622e4d99752a24daa577007c77565c --- /dev/null +++ b/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/dto/api/impl/ThumbnailsInfo.java @@ -0,0 +1,117 @@ +package ch.systemsx.cisd.openbis.dss.etl.dto.api.impl; + +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +import ch.systemsx.cisd.openbis.dss.etl.dto.RelativeImageFile; +import ch.systemsx.cisd.openbis.dss.generic.shared.dto.Size; +import ch.systemsx.cisd.openbis.dss.generic.shared.utils.ToStringUtil; + +/** + * Stores the mapping between image and their thumbnails paths. Thread-safe class. + * + * @author Tomasz Pylak + */ +public class ThumbnailsInfo +{ + public static class PhysicalDatasetInfo + { + private final String rootPath; + + private int thumbnailsWidth; + + private int thumbnailsHeight; + + public PhysicalDatasetInfo(String rootPath) + { + this.rootPath = rootPath; + } + } + + private final Map<RelativeImageFile, String> imageToThumbnailPathMap; + + private final Map<String, PhysicalDatasetInfo> datasetInfos; + + public ThumbnailsInfo() + { + this.imageToThumbnailPathMap = new HashMap<RelativeImageFile, String>(); + this.datasetInfos = new HashMap<String, ThumbnailsInfo.PhysicalDatasetInfo>(); + } + + public void putDataSet(String permId, String rootPath) + { + datasetInfos.put(permId, new PhysicalDatasetInfo(rootPath)); + } + + /** + * Saves the path to the thumbnail for the specified image. The thumbnail path is relative and + * starts with the name of the thumbnail file. + */ + public synchronized void saveThumbnailPath(String permId, RelativeImageFile image, + String thumbnailRelativePath, int width, int height) + { + imageToThumbnailPathMap.put(image, thumbnailRelativePath); + + PhysicalDatasetInfo datasetInfo = datasetInfos.get(permId); + if (datasetInfo != null) + { + datasetInfo.thumbnailsWidth = Math.max(datasetInfo.thumbnailsWidth, width); + datasetInfo.thumbnailsHeight = Math.max(datasetInfo.thumbnailsHeight, height); + } + } + + public synchronized String getThumbnailPath(RelativeImageFile image) + { + return imageToThumbnailPathMap.get(image); + } + + public Set<String> getThumbnailPhysicalDatasetsPermIds() + { + return datasetInfos.keySet(); + } + + public Size tryGetDimension(String permId) + { + PhysicalDatasetInfo datasetInfo = datasetInfos.get(permId); + if (datasetInfo != null) + { + if (datasetInfo.thumbnailsWidth > 0 && datasetInfo.thumbnailsHeight > 0) + { + return new Size(datasetInfo.thumbnailsWidth, datasetInfo.thumbnailsHeight); + } + } + return null; + } + + @Override + public String toString() + { + final StringBuilder buffer = new StringBuilder(); + ToStringUtil.appendNameAndObject(buffer, "number of thumbnails", + imageToThumbnailPathMap.size()); + ToStringUtil.appendNameAndObject(buffer, "dataset perm ids: ", printPermIds()); + return buffer.toString(); + } + + private String printPermIds() + { + StringBuilder sb = new StringBuilder("["); + boolean notFirst = false; + for (String permId : datasetInfos.keySet()) + { + if (notFirst) + { + sb.append(";"); + notFirst = true; + } + sb.append(" ").append(permId); + } + return sb.append(" ]").toString(); + } + + public String getRootPath(String permId) + { + return datasetInfos.get(permId).rootPath; + } +} \ No newline at end of file diff --git a/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/jython/JythonPlateDataSetHandler.java b/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/jython/JythonPlateDataSetHandler.java index 64351d4752db5a855348b1dbc57bf3f05bdab8ff..36b75120b11706c345f9f4644a0b6e582c2fa9ec 100644 --- a/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/jython/JythonPlateDataSetHandler.java +++ b/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/jython/JythonPlateDataSetHandler.java @@ -30,14 +30,13 @@ import ch.systemsx.cisd.etlserver.registrator.api.v1.impl.DataSetRegistrationTra import ch.systemsx.cisd.openbis.dss.Constants; import ch.systemsx.cisd.openbis.dss.etl.Hdf5ThumbnailGenerator; import ch.systemsx.cisd.openbis.dss.etl.PlateGeometryOracle; -import ch.systemsx.cisd.openbis.dss.etl.dto.RelativeImageFile; import ch.systemsx.cisd.openbis.dss.etl.dto.api.impl.FeatureDefinition; import ch.systemsx.cisd.openbis.dss.etl.dto.api.impl.FeatureVectorDataSetInformation; import ch.systemsx.cisd.openbis.dss.etl.dto.api.impl.FeaturesBuilder; import ch.systemsx.cisd.openbis.dss.etl.dto.api.impl.ImageContainerDataSet; import ch.systemsx.cisd.openbis.dss.etl.dto.api.impl.ImageDataSetInformation; import ch.systemsx.cisd.openbis.dss.etl.dto.api.impl.ImageDataSetStructure; -import ch.systemsx.cisd.openbis.dss.etl.dto.api.impl.ThumbnailFilePaths; +import ch.systemsx.cisd.openbis.dss.etl.dto.api.impl.ThumbnailsInfo; import ch.systemsx.cisd.openbis.dss.etl.dto.api.v1.IFeaturesBuilder; import ch.systemsx.cisd.openbis.dss.etl.dto.api.v1.IImageDataSet; import ch.systemsx.cisd.openbis.dss.etl.dto.api.v1.IImagingDataSetRegistrationTransaction; @@ -429,17 +428,17 @@ public class JythonPlateDataSetHandler extends JythonTopLevelDataSetHandler<Data imageDataSetStructure.getImageStorageConfiguraton() .getThumbnailsStorageFormat(); + ThumbnailsInfo thumbnailsInfo = new ThumbnailsInfo(); for (ThumbnailsStorageFormat thumbnailsStorageFormat : thumbnailsStorageFormatList) { IDataSet thumbnailDataset = createThumbnailDataset(); thumbnailDatasets.add(thumbnailDataset); - ThumbnailFilePaths thumbnailPaths = - generateThumbnails(imageDataSetStructure, incomingDirectory, - thumbnailDataset, thumbnailsStorageFormat); - imageDataSetInformation.setThumbnailFilePaths(thumbnailPaths); + generateThumbnails(imageDataSetStructure, incomingDirectory, thumbnailDataset, + thumbnailsStorageFormat, thumbnailsInfo); containedDataSetCodes.add(thumbnailDataset.getDataSetCode()); } + imageDataSetInformation.setThumbnailsInfo(thumbnailsInfo); } // create main dataset (with original images) @SuppressWarnings("unchecked") @@ -470,9 +469,9 @@ public class JythonPlateDataSetHandler extends JythonTopLevelDataSetHandler<Data return new File(originalDirName + File.separator + directoryPath); } - private ThumbnailFilePaths generateThumbnails(ImageDataSetStructure imageDataSetStructure, + private void generateThumbnails(ImageDataSetStructure imageDataSetStructure, File incomingDirectory, IDataSet thumbnailDataset, - ThumbnailsStorageFormat thumbnailsStorageFormatOrNull) + ThumbnailsStorageFormat thumbnailsStorageFormatOrNull, ThumbnailsInfo thumbnailPaths) { String thumbnailFile; if (thumbnailsStorageFormatOrNull == null) @@ -487,22 +486,10 @@ public class JythonPlateDataSetHandler extends JythonTopLevelDataSetHandler<Data thumbnailsStorageFormatOrNull.getThumbnailsFileName()); } - List<RelativeImageFile> images = asRelativeImageFile(imageDataSetStructure.getImages()); - ThumbnailFilePaths thumbnailPaths = - Hdf5ThumbnailGenerator.tryGenerateThumbnails(images, incomingDirectory, - thumbnailFile, imageDataSetStructure.getImageStorageConfiguraton(), - thumbnailDataset.getDataSetCode(), thumbnailsStorageFormatOrNull); - return thumbnailPaths; - } - - private static List<RelativeImageFile> asRelativeImageFile(List<ImageFileInfo> images) - { - List<RelativeImageFile> imageFiles = new ArrayList<RelativeImageFile>(); - for (ImageFileInfo image : images) - { - imageFiles.add(RelativeImageFile.create(image)); - } - return imageFiles; + Hdf5ThumbnailGenerator.tryGenerateThumbnails(imageDataSetStructure, incomingDirectory, + thumbnailFile, imageDataSetStructure.getImageStorageConfiguraton(), + thumbnailDataset.getDataSetCode(), thumbnailsStorageFormatOrNull, + thumbnailPaths); } private IDataSet createThumbnailDataset() diff --git a/screening/source/java/ch/systemsx/cisd/openbis/dss/screening/server/DssServiceRpcScreening.java b/screening/source/java/ch/systemsx/cisd/openbis/dss/screening/server/DssServiceRpcScreening.java index 2ed895dd957f9a04b875622604eca2cd86e6f08f..3ce22ddfcdd909f6c5ddd089f13f9340e15dad64 100644 --- a/screening/source/java/ch/systemsx/cisd/openbis/dss/screening/server/DssServiceRpcScreening.java +++ b/screening/source/java/ch/systemsx/cisd/openbis/dss/screening/server/DssServiceRpcScreening.java @@ -346,7 +346,7 @@ public class DssServiceRpcScreening extends AbstractDssServiceRpc<IDssServiceRpc for (String channelCode : params.getChannelsCodes()) { AbsoluteImageReference image = - imageAccessor.tryGetRepresentativeThumbnail(channelCode, null); + imageAccessor.tryGetRepresentativeThumbnail(channelCode, null, null); if (image != null) { return image.getUnchangedImage(); diff --git a/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/shared/imaging/dataaccess/IImagingReadonlyQueryDAO.java b/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/shared/imaging/dataaccess/IImagingReadonlyQueryDAO.java index c54e10e35d4b597f36cf5624bd28e7e3a693036a..52efb64ba2862c435339f1deefbd75430dfcd1af 100644 --- a/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/shared/imaging/dataaccess/IImagingReadonlyQueryDAO.java +++ b/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/shared/imaging/dataaccess/IImagingReadonlyQueryDAO.java @@ -235,9 +235,8 @@ public interface IImagingReadonlyQueryDAO extends BaseQuery { StringArrayMapper.class }, fetchSize = FETCH_SIZE) public List<ImgImageDatasetDTO> listImageDatasetsByPermId(String... datasetPermIds); - @Select(sql = "select * from IMAGE_ZOOM_LEVELS zoom, IMAGE_DATA_SETS img_ds " - + "where zoom.CONTAINER_DATASET_ID = img_ds.id and img_ds.PERM_ID = ?{1}", fetchSize = FETCH_SIZE) - public List<ImgImageZoomLevelDTO> listImageZoomLevels(String containerDatasetPermId); + @Select(sql = "select * from IMAGE_ZOOM_LEVELS zoom where zoom.CONTAINER_DATASET_ID = ?{1}", fetchSize = FETCH_SIZE) + public List<ImgImageZoomLevelDTO> listImageZoomLevels(long datasetId); @Select(sql = "select * from ANALYSIS_DATA_SETS where PERM_ID = any(?{1})", parameterBindings = { StringArrayMapper.class }, fetchSize = FETCH_SIZE) diff --git a/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/shared/imaging/dataaccess/ImgImageZoomLevelDTO.java b/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/shared/imaging/dataaccess/ImgImageZoomLevelDTO.java index cb006c85e95ff04441e84251aa5f7b7ae1b47b15..ad1774d821f5b8df77b5c9bd306930b5e60eec28 100644 --- a/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/shared/imaging/dataaccess/ImgImageZoomLevelDTO.java +++ b/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/shared/imaging/dataaccess/ImgImageZoomLevelDTO.java @@ -34,18 +34,30 @@ public class ImgImageZoomLevelDTO extends AbstractImgIdentifiable @ResultColumn("container_dataset_id") private long containerDatasetId; + @ResultColumn("path") + private String rootPath; + + @ResultColumn("width") + private Integer width; + + @ResultColumn("height") + private Integer height; + @SuppressWarnings("unused") private ImgImageZoomLevelDTO() { // All Data-Object classes must have a default constructor. } - public ImgImageZoomLevelDTO(String physicalDatasetPermId, boolean isOriginal, - long containerDatasetId) + public ImgImageZoomLevelDTO(String physicalDatasetPermId, boolean isOriginal, String rootPath, + Integer width, Integer height, long containerDatasetId) { this.physicalDatasetPermId = physicalDatasetPermId; this.isOriginal = isOriginal; this.containerDatasetId = containerDatasetId; + this.rootPath = rootPath; + this.width = width; + this.height = height; } public String getPhysicalDatasetPermId() @@ -77,4 +89,34 @@ public class ImgImageZoomLevelDTO extends AbstractImgIdentifiable { this.containerDatasetId = containerDatasetId; } + + public String getRootPath() + { + return rootPath; + } + + public void setRootPath(String rootPath) + { + this.rootPath = rootPath; + } + + public Integer getWidth() + { + return width; + } + + public void setWidth(Integer width) + { + this.width = width; + } + + public Integer getHeight() + { + return height; + } + + public void setHeight(Integer height) + { + this.height = height; + } } diff --git a/screening/source/sql/imaging/postgresql/019/schema-019.sql b/screening/source/sql/imaging/postgresql/019/schema-019.sql new file mode 100644 index 0000000000000000000000000000000000000000..476e44e4a39043e60aae1f3c046e4ae4c8310bc5 --- /dev/null +++ b/screening/source/sql/imaging/postgresql/019/schema-019.sql @@ -0,0 +1,410 @@ + +/* ---------------------------------------------------------------------- */ +/* Domains */ +/* ---------------------------------------------------------------------- */ + +CREATE DOMAIN TECH_ID AS BIGINT; + +CREATE DOMAIN CODE AS VARCHAR(40); + +CREATE DOMAIN NAME AS VARCHAR(80); + +CREATE DOMAIN DESCRIPTION AS VARCHAR(200); + +CREATE DOMAIN FILE_PATH as VARCHAR(1000); + +CREATE DOMAIN COLOR_COMPONENT AS VARCHAR(40) CHECK (VALUE IN ('RED', 'GREEN', 'BLUE')); + +CREATE DOMAIN CHANNEL_COLOR AS VARCHAR(20) CHECK (VALUE IN ('BLUE', 'GREEN', 'RED', 'RED_GREEN', 'RED_BLUE', 'GREEN_BLUE')); + +CREATE DOMAIN BOOLEAN_CHAR AS BOOLEAN DEFAULT FALSE; + +/* ---------------------------------------------------------------------- */ +/* Tables */ +/* ---------------------------------------------------------------------- */ + +CREATE TABLE EXPERIMENTS ( + ID BIGSERIAL NOT NULL, + PERM_ID CODE NOT NULL, + IMAGE_TRANSFORMER_FACTORY BYTEA, + + PRIMARY KEY (ID), + UNIQUE (PERM_ID) +); + +CREATE TABLE CONTAINERS ( + ID BIGSERIAL NOT NULL, + PERM_ID CODE NOT NULL, + + SPOTS_WIDTH INTEGER, + SPOTS_HEIGHT INTEGER, + + EXPE_ID TECH_ID NOT NULL, + + PRIMARY KEY (ID), + UNIQUE (PERM_ID), + CONSTRAINT FK_SAMPLE_1 FOREIGN KEY (EXPE_ID) REFERENCES EXPERIMENTS (ID) ON DELETE CASCADE ON UPDATE CASCADE +); + +CREATE INDEX CONTAINERS_EXPE_IDX ON CONTAINERS(EXPE_ID); + +CREATE TABLE SPOTS ( + ID BIGSERIAL NOT NULL, + + -- position in the container, one-based + X INTEGER, + Y INTEGER, + CONT_ID TECH_ID NOT NULL, + + PRIMARY KEY (ID), + CONSTRAINT FK_SPOT_1 FOREIGN KEY (CONT_ID) REFERENCES CONTAINERS (ID) ON DELETE CASCADE ON UPDATE CASCADE +); + +CREATE INDEX SPOTS_CONT_IDX ON SPOTS(CONT_ID); +-- allows to select one spot of the container quicker +CREATE INDEX SPOTS_COORDS_IDX ON SPOTS(CONT_ID, X, Y); + +CREATE TABLE ANALYSIS_DATA_SETS ( + ID BIGSERIAL NOT NULL, + PERM_ID CODE NOT NULL, + + CONT_ID TECH_ID, + + PRIMARY KEY (ID), + UNIQUE (PERM_ID), + CONSTRAINT FK_ANALYSIS_DATA_SET_1 FOREIGN KEY (CONT_ID) REFERENCES CONTAINERS (ID) ON DELETE CASCADE ON UPDATE CASCADE +); + +CREATE INDEX ANALYSIS_DATA_SETS_CONT_IDX ON ANALYSIS_DATA_SETS(CONT_ID); + + +CREATE TABLE IMAGE_DATA_SETS ( + ID BIGSERIAL NOT NULL, + PERM_ID CODE NOT NULL, + + ---- image dataset specific fields (should be refactored) + FIELDS_WIDTH INTEGER, + FIELDS_HEIGHT INTEGER, + -- transformation for merged channels on the dataset level, overrides experiment level transformation + IMAGE_TRANSFORMER_FACTORY BYTEA, + -- a redundant information if there are timepoint or depth stack data for any spots in this dataset + IS_MULTIDIMENSIONAL BOOLEAN_CHAR NOT NULL, + + -- Which image library should be used to read the image? + -- If not specified, some heuristics are used, but it is slower and does not try with all the available libraries. + IMAGE_LIBRARY_NAME NAME, + -- Which reader in the library should be used? Valid only if the library name is specified. + -- Should be specified when library name is specified. + IMAGE_LIBRARY_READER_NAME NAME, + ---- END image dataset specific fields + + CONT_ID TECH_ID, + + PRIMARY KEY (ID), + UNIQUE (PERM_ID), + CONSTRAINT FK_IMAGE_DATA_SET_1 FOREIGN KEY (CONT_ID) REFERENCES CONTAINERS (ID) ON DELETE CASCADE ON UPDATE CASCADE +); + +CREATE INDEX IMAGE_DATA_SETS_CONT_IDX ON IMAGE_DATA_SETS(CONT_ID); + +CREATE TABLE IMAGE_ZOOM_LEVELS ( + ID BIGSERIAL NOT NULL, + + IS_ORIGINAL BOOLEAN_CHAR NOT NULL, + CONTAINER_DATASET_ID TECH_ID NOT NULL, + + -- Perm id of the 'physical' dataset which contains all images with this zoom. + -- Physical datasets are not stored in "image_data_sets" table, but we need the reference to them + -- when we delete or archive one zoom level. + PHYSICAL_DATASET_PERM_ID TEXT NOT NULL, + + PATH FILE_PATH, + WIDTH INTEGER, + HEIGHT INTEGER, + + PRIMARY KEY (ID), + UNIQUE (PHYSICAL_DATASET_PERM_ID), + CONSTRAINT FK_IMAGE_ZOOM_LEVELS_1 FOREIGN KEY (CONTAINER_DATASET_ID) REFERENCES IMAGE_DATA_SETS (ID) ON DELETE CASCADE ON UPDATE CASCADE +); + +CREATE INDEX IMAGE_ZOOM_LEVELS_PHYS_DS_IDX ON IMAGE_ZOOM_LEVELS (PHYSICAL_DATASET_PERM_ID); +CREATE INDEX IMAGE_ZOOM_LEVELS_CONT_FK_IDX ON IMAGE_ZOOM_LEVELS (CONTAINER_DATASET_ID); + +CREATE TABLE CHANNELS ( + ID BIGSERIAL NOT NULL, + + CODE NAME NOT NULL, + LABEL NAME NOT NULL, + DESCRIPTION DESCRIPTION, + WAVELENGTH INTEGER, + + -- RGB color components specify the color in which channel should be displayed + RED_CC INTEGER NOT NULL, + GREEN_CC INTEGER NOT NULL, + BLUE_CC INTEGER NOT NULL, + + DS_ID TECH_ID, + EXP_ID TECH_ID, + + PRIMARY KEY (ID), + CONSTRAINT FK_CHANNELS_1 FOREIGN KEY (DS_ID) REFERENCES IMAGE_DATA_SETS (ID) ON DELETE CASCADE ON UPDATE CASCADE, + CONSTRAINT FK_CHANNELS_2 FOREIGN KEY (EXP_ID) REFERENCES EXPERIMENTS (ID) ON DELETE CASCADE ON UPDATE CASCADE, + CONSTRAINT CHANNELS_DS_EXP_ARC_CK CHECK ((DS_ID IS NOT NULL AND EXP_ID IS NULL) OR (DS_ID IS NULL AND EXP_ID IS NOT NULL)), + + CONSTRAINT CHANNELS_UK_1 UNIQUE(CODE, DS_ID), + CONSTRAINT CHANNELS_UK_2 UNIQUE(CODE, EXP_ID) +); + +CREATE INDEX CHANNELS_DS_IDX ON CHANNELS(DS_ID); + +CREATE TABLE IMAGE_TRANSFORMATIONS ( + ID BIGSERIAL NOT NULL, + + CODE NAME NOT NULL, + LABEL NAME NOT NULL, + DESCRIPTION character varying(1000), + IMAGE_TRANSFORMER_FACTORY BYTEA NOT NULL, + + -- For now there can be only one transformation for each channel which is editable by Image Viewer, + -- but when GUI will support more then this column will become really useful. + IS_EDITABLE BOOLEAN_CHAR NOT NULL, + + -- The default choice to present the image. + -- If not present a 'hard-coded' default transformation will become available. + IS_DEFAULT BOOLEAN_CHAR NOT NULL DEFAULT 'F', + + CHANNEL_ID TECH_ID NOT NULL, + + PRIMARY KEY (ID), + CONSTRAINT FK_IMAGE_TRANSFORMATIONS_CHANNEL FOREIGN KEY (CHANNEL_ID) REFERENCES CHANNELS (ID) ON DELETE CASCADE ON UPDATE CASCADE, + + CONSTRAINT IMAGE_TRANSFORMATIONS_UK_1 UNIQUE(CODE, CHANNEL_ID) +); + +CREATE INDEX IMAGE_TRANSFORMATIONS_CHANNELS_IDX ON IMAGE_TRANSFORMATIONS(CHANNEL_ID); + +CREATE TABLE CHANNEL_STACKS ( + ID BIGSERIAL NOT NULL, + + -- x and y are kind of a two dimensional sequence number, some use case may only use x and leave y alone + X INTEGER, + Y INTEGER, + -- We use the fixed dimension meters here. + Z_in_M REAL, + -- We use the fixed dimension seconds here. + T_in_SEC REAL, + SERIES_NUMBER INTEGER, + + -- For all channel stacks of a well (HCS) or image dataset (microscopy) there should be exactly + -- one record with is_representative = true + is_representative BOOLEAN_CHAR NOT NULL DEFAULT 'F', + + DS_ID TECH_ID NOT NULL, + SPOT_ID TECH_ID, + + PRIMARY KEY (ID), + CONSTRAINT FK_CHANNEL_STACKS_1 FOREIGN KEY (SPOT_ID) REFERENCES SPOTS (ID) ON DELETE CASCADE ON UPDATE CASCADE, + CONSTRAINT FK_CHANNEL_STACKS_2 FOREIGN KEY (DS_ID) REFERENCES IMAGE_DATA_SETS (ID) ON DELETE CASCADE ON UPDATE CASCADE +); + +CREATE INDEX CHANNEL_STACKS_DS_IDX ON CHANNEL_STACKS(DS_ID); +CREATE INDEX CHANNEL_STACKS_SPOT_IDX ON CHANNEL_STACKS(SPOT_ID); +CREATE INDEX CHANNEL_STACKS_DIM_IDX ON CHANNEL_STACKS(X, Y, Z_in_M, T_in_SEC); + +CREATE TABLE IMAGES ( + ID BIGSERIAL NOT NULL, + + PATH FILE_PATH NOT NULL, + IMAGE_ID CODE, + COLOR COLOR_COMPONENT, + + PRIMARY KEY (ID) +); + +CREATE TABLE ACQUIRED_IMAGES ( + ID BIGSERIAL NOT NULL, + + IMG_ID TECH_ID, + THUMBNAIL_ID TECH_ID, + IMAGE_TRANSFORMER_FACTORY BYTEA, + + CHANNEL_STACK_ID TECH_ID NOT NULL, + CHANNEL_ID TECH_ID NOT NULL, + + PRIMARY KEY (ID), + CONSTRAINT FK_IMAGES_1 FOREIGN KEY (CHANNEL_STACK_ID) REFERENCES CHANNEL_STACKS (ID) ON DELETE CASCADE ON UPDATE CASCADE, + CONSTRAINT FK_IMAGES_2 FOREIGN KEY (CHANNEL_ID) REFERENCES CHANNELS (ID) ON DELETE CASCADE ON UPDATE CASCADE, + CONSTRAINT FK_IMAGES_3 FOREIGN KEY (IMG_ID) REFERENCES IMAGES (ID) ON DELETE SET NULL ON UPDATE CASCADE, + CONSTRAINT FK_IMAGES_4 FOREIGN KEY (THUMBNAIL_ID) REFERENCES IMAGES (ID) ON DELETE SET NULL ON UPDATE CASCADE +); + +CREATE INDEX IMAGES_CHANNEL_STACK_IDX ON ACQUIRED_IMAGES(CHANNEL_STACK_ID); +CREATE INDEX IMAGES_CHANNEL_IDX ON ACQUIRED_IMAGES(CHANNEL_ID); +CREATE INDEX IMAGES_IMG_IDX ON ACQUIRED_IMAGES(IMG_ID); +CREATE INDEX IMAGES_THUMBNAIL_IDX ON ACQUIRED_IMAGES(THUMBNAIL_ID); + +CREATE TABLE EVENTS ( + LAST_SEEN_DELETION_EVENT_ID TECH_ID NOT NULL +); + +/* ---------------------------------------------------------------------- */ +/* FEATURE VECTORS */ +/* ---------------------------------------------------------------------- */ + +CREATE TABLE FEATURE_DEFS ( + ID BIGSERIAL NOT NULL, + + CODE NAME NOT NULL, + LABEL NAME NOT NULL, + DESCRIPTION DESCRIPTION, + + DS_ID TECH_ID NOT NULL, + + PRIMARY KEY (ID), + CONSTRAINT FK_FEATURE_DEFS_1 FOREIGN KEY (DS_ID) REFERENCES ANALYSIS_DATA_SETS (ID) ON DELETE CASCADE ON UPDATE CASCADE, + CONSTRAINT FEATURE_DEFS_UK_1 UNIQUE(CODE, DS_ID) +); + +CREATE INDEX FEATURE_DEFS_DS_IDX ON FEATURE_DEFS(DS_ID); + +CREATE TABLE FEATURE_VOCABULARY_TERMS ( + ID BIGSERIAL NOT NULL, + + CODE NAME NOT NULL, + SEQUENCE_NUMBER INTEGER NOT NULL, + FD_ID TECH_ID NOT NULL, + + PRIMARY KEY (ID), + CONSTRAINT FK_FEATURE_VOCABULARY_TERMS_1 FOREIGN KEY (FD_ID) REFERENCES FEATURE_DEFS (ID) ON DELETE CASCADE ON UPDATE CASCADE +); + +CREATE INDEX FEATURE_VOCABULARY_TERMS_FD_IDX ON FEATURE_VOCABULARY_TERMS(FD_ID); + +CREATE TABLE FEATURE_VALUES ( + ID BIGSERIAL NOT NULL, + + -- we use the fixed dimension meters here + Z_in_M REAL, + -- we use the fixed dimension seconds here + T_in_SEC REAL, + -- Serialized 2D matrix with values for each spot. + -- Contains floats which can be NaN. + -- It is never a case that the whole matrix contains NaN - in such a case we save nothing. + -- If feature definition has some connected vocabulary terms then the matrix + -- stores FEATURE_VOCABULARY_TERMS.SEQUENCE_NUMBER of the terms (should be casted from float to int). + -- If the term is null the Float.NaN is stored. + VALUES BYTEA NOT NULL, + + FD_ID TECH_ID NOT NULL, + + PRIMARY KEY (ID), + CONSTRAINT FK_FEATURE_VALUES_1 FOREIGN KEY (FD_ID) REFERENCES FEATURE_DEFS (ID) ON DELETE CASCADE ON UPDATE CASCADE + -- This constaint does not make any sense. Leave it out for now. + -- CONSTRAINT FEATURE_VALUES_UK_1 UNIQUE(Z_in_M, T_in_SEC) +); + +CREATE INDEX FEATURE_VALUES_FD_IDX ON FEATURE_VALUES(FD_ID); +CREATE INDEX FEATURE_VALUES_Z_AND_T_IDX ON FEATURE_VALUES(Z_in_M, T_in_SEC); + + +/* ---------------------------------------------------------------------- */ +/* FUNCTIONS AND TRIGGERS */ +/* ---------------------------------------------------------------------- */ + +CREATE OR REPLACE FUNCTION DELETE_UNUSED_IMAGES() RETURNS trigger AS $$ +BEGIN + delete from images where id = OLD.img_id or id = OLD.thumbnail_id; + RETURN NEW; +END; +$$ LANGUAGE 'plpgsql'; + +CREATE TRIGGER UNUSED_IMAGES AFTER DELETE ON ACQUIRED_IMAGES + FOR EACH ROW EXECUTE PROCEDURE DELETE_UNUSED_IMAGES(); + +CREATE OR REPLACE FUNCTION DELETE_UNUSED_NULLED_IMAGES() RETURNS trigger AS $$ +BEGIN + if NEW.img_id IS NULL then + if OLD.img_id IS NOT NULL then + delete from images where id = OLD.img_id; + end if; + end if; + if NEW.thumbnail_id IS NULL then + if OLD.thumbnail_id IS NOT NULL then + delete from images where id = OLD.thumbnail_id; + end if; + end if; + RETURN NEW; +END; +$$ LANGUAGE 'plpgsql'; + +CREATE TRIGGER UNUSED_NULLED_IMAGES AFTER UPDATE ON ACQUIRED_IMAGES + FOR EACH ROW EXECUTE PROCEDURE DELETE_UNUSED_NULLED_IMAGES(); + +CREATE OR REPLACE FUNCTION DELETE_EMPTY_ACQUIRED_IMAGES() RETURNS trigger AS $$ +BEGIN + delete from acquired_images where id = OLD.id; + RETURN NEW; +END; +$$ LANGUAGE 'plpgsql'; + +CREATE TRIGGER EMPTY_ACQUIRED_IMAGES BEFORE UPDATE ON ACQUIRED_IMAGES + FOR EACH ROW + WHEN (NEW.img_id IS NULL AND NEW.thumbnail_id IS NULL) + EXECUTE PROCEDURE DELETE_EMPTY_ACQUIRED_IMAGES(); + + +------------------------------------------------------------------------------------ +-- Purpose: Create trigger CHANNEL_STACKS_CHECK which checks if both spot_id and dataset.cont_id +-- are both null or not null. +------------------------------------------------------------------------------------ + +CREATE OR REPLACE FUNCTION CHANNEL_STACKS_CHECK() RETURNS trigger AS $$ +DECLARE + v_cont_id CODE; +BEGIN + + select cont_id into v_cont_id from image_data_sets where id = NEW.ds_id; + + -- Check that if there is no spot than there is no dataset container as well + if v_cont_id IS NULL then + if NEW.spot_id IS NOT NULL then + RAISE EXCEPTION 'Insert/Update of CHANNEL_STACKS failed, as the dataset container is not set, but spot is (spot id = %).',NEW.spot_id; + end if; + else + if NEW.spot_id IS NULL then + RAISE EXCEPTION 'Insert/Update of CHANNEL_STACKS failed, as the dataset container is set (id = %), but spot is not set.',v_cont_id; + end if; + end if; + RETURN NEW; +END; +$$ LANGUAGE 'plpgsql'; + +CREATE TRIGGER CHANNEL_STACKS_CHECK BEFORE INSERT OR UPDATE ON CHANNEL_STACKS + FOR EACH ROW EXECUTE PROCEDURE CHANNEL_STACKS_CHECK(); + +------------------------------------------------------------------------------------ +-- Purpose: Create trigger IMAGE_TRANSFORMATIONS_DEFAULT_CHECK which checks +-- if at most one channel's transformation is default +------------------------------------------------------------------------------------ + +CREATE OR REPLACE FUNCTION IMAGE_TRANSFORMATIONS_DEFAULT_CHECK() RETURNS trigger AS $$ +DECLARE + v_is_default boolean; +BEGIN + if NEW.is_default = 'T' then + select is_default into v_is_default from IMAGE_TRANSFORMATIONS + where is_default = 'T' + and channel_id = NEW.channel_id + and id != NEW.id; + if v_is_default is NOT NULL then + RAISE EXCEPTION 'Insert/Update of image transformation (Code: %) failed, as the new record has is_default set to true and there is already a default record defined.', NEW.code; + end if; + end if; + + RETURN NEW; +END; +$$ LANGUAGE 'plpgsql'; + +CREATE TRIGGER IMAGE_TRANSFORMATIONS_DEFAULT_CHECK BEFORE INSERT OR UPDATE ON IMAGE_TRANSFORMATIONS + FOR EACH ROW EXECUTE PROCEDURE IMAGE_TRANSFORMATIONS_DEFAULT_CHECK(); + \ No newline at end of file diff --git a/screening/source/sql/imaging/postgresql/migration/migration-018-019.sql b/screening/source/sql/imaging/postgresql/migration/migration-018-019.sql new file mode 100644 index 0000000000000000000000000000000000000000..4e727895a8047c5c995cd14844414d62ffed24e3 --- /dev/null +++ b/screening/source/sql/imaging/postgresql/migration/migration-018-019.sql @@ -0,0 +1,5 @@ +-- Migration from 018 to 019 + +ALTER TABLE IMAGE_ZOOM_LEVELS ADD COLUMN PATH FILE_PATH; +ALTER TABLE IMAGE_ZOOM_LEVELS ADD COLUMN WIDTH INTEGER; +ALTER TABLE IMAGE_ZOOM_LEVELS ADD COLUMN HEIGHT INTEGER; \ No newline at end of file diff --git a/screening/sourceTest/java/ch/systemsx/cisd/openbis/dss/etl/dataaccess/ImagingQueryDAOTest.java b/screening/sourceTest/java/ch/systemsx/cisd/openbis/dss/etl/dataaccess/ImagingQueryDAOTest.java index 068607e700745c2063f4de82fb677d6898c97618..fc2f669aab8a7758701c1dcf09bcea59136341f1 100644 --- a/screening/sourceTest/java/ch/systemsx/cisd/openbis/dss/etl/dataaccess/ImagingQueryDAOTest.java +++ b/screening/sourceTest/java/ch/systemsx/cisd/openbis/dss/etl/dataaccess/ImagingQueryDAOTest.java @@ -428,9 +428,9 @@ public class ImagingQueryDAOTest extends AbstractDBTest final long datasetId = dao.addImageDataset(dataset); String physicalDatasetPermIdPrefix = permId + "666"; dao.addImageZoomLevel(new ImgImageZoomLevelDTO(physicalDatasetPermIdPrefix + "-123", true, - datasetId)); + "original", 800, 600, datasetId)); dao.addImageZoomLevel(new ImgImageZoomLevelDTO(physicalDatasetPermIdPrefix + "-666", false, - datasetId)); + "thumbs", 200, 150, datasetId)); final ImgImageDatasetDTO loadedDataset = dao.tryGetImageDatasetByPermId(permId); assertNotNull(loadedDataset); @@ -448,12 +448,23 @@ public class ImagingQueryDAOTest extends AbstractDBTest assertEquals(loadedDataset, datasets.get(0)); // test listImageZoomLevels - List<ImgImageZoomLevelDTO> zoomLevels = dao.listImageZoomLevels(permId); + List<ImgImageZoomLevelDTO> zoomLevels = dao.listImageZoomLevels(datasetId); assertEquals(2, zoomLevels.size()); for (ImgImageZoomLevelDTO zoomLevel : zoomLevels) { AssertionUtil.assertContains(physicalDatasetPermIdPrefix, zoomLevel.getPhysicalDatasetPermId()); + if (zoomLevel.getIsOriginal()) + { + assertEquals("original", zoomLevel.getRootPath()); + assertEquals(800, zoomLevel.getWidth().intValue()); + assertEquals(600, zoomLevel.getHeight().intValue()); + } else + { + assertEquals("thumbs", zoomLevel.getRootPath()); + assertEquals(200, zoomLevel.getWidth().intValue()); + assertEquals(150, zoomLevel.getHeight().intValue()); + } } return datasetId;