diff --git a/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/AcquiredPlateImage.java b/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/AcquiredPlateImage.java index 4dc8e01d49c054fa585bcdee22cb750d4cc9caf3..ce4fafbf9aaa0692dfa740c0fe5383a0497d5019 100644 --- a/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/AcquiredPlateImage.java +++ b/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/AcquiredPlateImage.java @@ -17,49 +17,60 @@ package ch.systemsx.cisd.openbis.dss.etl; import ch.systemsx.cisd.bds.hcs.Location; +import ch.systemsx.cisd.common.utilities.AbstractHashable; /** * Describes properties extracted for one screening image. * * @author Tomasz Pylak */ -public class AcquiredPlateImage +public class AcquiredPlateImage extends AbstractHashable { private final Location wellLocation; private final Location tileLocation; - private final int channel; + private final String channelName; // can be null private final Float timePointOrNull, depthOrNull; private final RelativeImagePath imageFilePath; // relative to the dataset directory - public AcquiredPlateImage(Location wellLocation, Location tileLocation, int channel, + public AcquiredPlateImage(Location wellLocation, Location tileLocation, String channelName, Float timePointOrNull, Float depthOrNull, RelativeImagePath imageFilePath) { this.wellLocation = wellLocation; this.tileLocation = tileLocation; - this.channel = channel; + this.channelName = channelName; this.timePointOrNull = timePointOrNull; this.depthOrNull = depthOrNull; this.imageFilePath = imageFilePath; } - public Location getWellLocation() + public int getWellRow() { - return wellLocation; + return wellLocation.getY(); } - public Location getTileLocation() + public int getWellColumn() { - return tileLocation; + return wellLocation.getX(); } - public int getChannel() + public int getTileRow() { - return channel; + return tileLocation.getY(); + } + + public int getTileColumn() + { + return tileLocation.getX(); + } + + public String getChannelName() + { + return channelName; } public Float tryGetTimePoint() diff --git a/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/HCSDatasetUploader.java b/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/HCSDatasetUploader.java index 683605fad5758e71bf89da9dc386fd260fea1224..cabfa96ee3375c98d9921dc3b5e4269aa2b19e2b 100644 --- a/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/HCSDatasetUploader.java +++ b/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/HCSDatasetUploader.java @@ -17,13 +17,20 @@ package ch.systemsx.cisd.openbis.dss.etl; import java.util.ArrayList; -import java.util.HashSet; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.Set; +import java.util.Map.Entry; +import ch.systemsx.cisd.common.exceptions.UserFailureException; import ch.systemsx.cisd.openbis.dss.etl.dataaccess.IImagingUploadDAO; +import ch.systemsx.cisd.openbis.dss.etl.dataaccess.ImgAcquiredImageDTO; +import ch.systemsx.cisd.openbis.dss.etl.dataaccess.ImgChannelDTO; +import ch.systemsx.cisd.openbis.dss.etl.dataaccess.ImgChannelStackDTO; import ch.systemsx.cisd.openbis.dss.etl.dataaccess.ImgContainerDTO; import ch.systemsx.cisd.openbis.dss.etl.dataaccess.ImgDatasetDTO; +import ch.systemsx.cisd.openbis.dss.etl.dataaccess.ImgImageDTO; import ch.systemsx.cisd.openbis.dss.etl.dataaccess.ImgSpotDTO; /** @@ -32,9 +39,9 @@ import ch.systemsx.cisd.openbis.dss.etl.dataaccess.ImgSpotDTO; class HCSDatasetUploader { public static void upload(IImagingUploadDAO dao, ScreeningContainerDatasetInfo info, - List<AcquiredPlateImage> images) + List<AcquiredPlateImage> images, Set<HCSImageFileExtractionResult.Channel> channels) { - new HCSDatasetUploader(dao).upload(info, images); + new HCSDatasetUploader(dao).upload(info, images, channels); } private final IImagingUploadDAO dao; @@ -44,100 +51,276 @@ class HCSDatasetUploader this.dao = dao; } - private void upload(ScreeningContainerDatasetInfo info, List<AcquiredPlateImage> images) + private void upload(ScreeningContainerDatasetInfo info, List<AcquiredPlateImage> images, + Set<HCSImageFileExtractionResult.Channel> channels) { long expId = getOrCreateExperiment(info); long contId = getOrCreateContainer(expId, info); - Long[][] spotIds = getOrCreateSpots(contId, info); + Long[][] spotIds = getOrCreateSpots(contId, info, images); + Map<String, Long/* (tech id */> channelsMap = getOrCreateChannels(expId, channels); long datasetId = createDataset(contId, info); - createImages(images, spotIds, datasetId); + + createImages(images, spotIds, channelsMap, datasetId); } - private void createImages(List<AcquiredPlateImage> images, Long[][] spotIds, long datasetId) + private Map<String, Long> getOrCreateChannels(long expId, + Set<HCSImageFileExtractionResult.Channel> channels) { - // TODO 2010-05-18, Tomasz Pylak: create immages and channels + Map<String/* name */, ImgChannelDTO> existingChannels = + asNameMap(dao.getChannelsByExperimentId(expId)); + Map<String, Long> map = new HashMap<String, Long>(); + for (HCSImageFileExtractionResult.Channel channel : channels) + { + ImgChannelDTO channelDTO = getOrCreateChannel(channel, expId, existingChannels); + map.put(channelDTO.getName(), channelDTO.getId()); + } + return map; } - // returns a matrix of spot tech ids. The matrix[row][col] contains null is spot at (row,col) - // does not exist. Spot coordinates are 0-based in the matrix. - private Long[][] getOrCreateSpots(long contId, ScreeningContainerDatasetInfo info) + private static Map<String, ImgChannelDTO> asNameMap(List<ImgChannelDTO> channels) { - String[][] wantedSpotPermIds = info.getSpotPermIds(); - List<ImgSpotDTO> spots = dao.listSpots(asList(wantedSpotPermIds)); - List<ImgSpotDTO> newSpots = findSpotsToCreate(spots, wantedSpotPermIds, contId); - addSpotsAndSetIds(newSpots); - spots.addAll(newSpots); - return createPermIdMatrix(spots, info.getContainerWidth(), info.getContainerHeight()); + Map<String, ImgChannelDTO> nameMap = new HashMap<String, ImgChannelDTO>(); + for (ImgChannelDTO channel : channels) + { + nameMap.put(channel.getName(), channel); + } + return nameMap; } - private static Long[][] createPermIdMatrix(List<ImgSpotDTO> spots, int width, int height) + private ImgChannelDTO getOrCreateChannel(HCSImageFileExtractionResult.Channel channel, + long expId, Map<String, ImgChannelDTO> existingChannels) { - Long[][] matrix = new Long[width][height]; - for (ImgSpotDTO spot : spots) + ImgChannelDTO channelDTO = createChannelDTO(channel, expId); + ImgChannelDTO existingChannel = existingChannels.get(channelDTO.getName()); + if (existingChannel != null) + { + // if a channel with a specified name already exists for an experiment, its description + // will be updated + if (existingChannel.getWavelength() != channelDTO.getWavelength()) + { + throw UserFailureException + .fromTemplate( + "There are already datasets registered for the experiment " + + "which use the same channel name, but with a different wavelength! " + + "Channel %s, old wavelength %d, new wavelength %d.", + channelDTO.getName(), existingChannel.getWavelength(), channelDTO + .getWavelength()); + } + channelDTO.setId(existingChannel.getId()); + dao.updateChannel(channelDTO); + } else { - matrix[spot.getY() - 1][spot.getX() - 1] = spot.getId(); + // if a channel with a specified name does not exists for an experiment, it's created + long channelId = dao.addChannel(channelDTO); + channelDTO.setId(channelId); } - return matrix; + return channelDTO; + } + + private static ImgChannelDTO createChannelDTO(HCSImageFileExtractionResult.Channel channel, + long expId) + { + return ImgChannelDTO.createExperimentChannel(channel.getName(), channel.getDescription(), + channel.getWavelength(), expId); } - private void addSpotsAndSetIds(List<ImgSpotDTO> spots) + private static class AcquiredImageInStack { - for (ImgSpotDTO spot : spots) + private final String channelName; + + private final RelativeImagePath imageFilePath; + + public AcquiredImageInStack(String channelName, RelativeImagePath imageFilePath) { - long id = dao.addSpot(spot); - spot.setId(id); + this.channelName = channelName; + this.imageFilePath = imageFilePath; + } + + public String getChannelName() + { + return channelName; + } + + public RelativeImagePath getImageFilePath() + { + return imageFilePath; } } - private static List<ImgSpotDTO> findSpotsToCreate(List<ImgSpotDTO> existingSpots, - String[][] wantedSpotPermIds, long contId) + private void createImages(List<AcquiredPlateImage> images, Long[][] spotIds, + Map<String, Long> channelsMap, long datasetId) { - List<ImgSpotDTO> newSpots = new ArrayList<ImgSpotDTO>(); - Set<String> existingPermIds = asPermIdSet(existingSpots); - for (int row = 0; row < wantedSpotPermIds.length; row++) + Map<ImgChannelStackDTO, List<AcquiredImageInStack>> stackImagesMap = + makeStackImagesMap(images, spotIds, datasetId); + createChannelStacks(stackImagesMap.keySet()); + createImages(stackImagesMap, channelsMap); + } + + private Map<ImgChannelStackDTO, List<AcquiredImageInStack>> makeStackImagesMap( + List<AcquiredPlateImage> images, Long[][] spotIds, long datasetId) + { + Map<ImgChannelStackDTO, List<AcquiredImageInStack>> map = + new HashMap<ImgChannelStackDTO, List<AcquiredImageInStack>>(); + for (AcquiredPlateImage image : images) { - String[] spotRow = wantedSpotPermIds[row]; - for (int col = 0; col < spotRow.length; col++) + ImgChannelStackDTO stackDTO = makeStackDTO(image, spotIds, datasetId); + List<AcquiredImageInStack> stackImages = map.get(stackDTO); + if (stackImages == null) { - String spotPermId = spotRow[col]; - if (spotPermId != null && existingPermIds.contains(spotPermId) == false) - { - newSpots.add(new ImgSpotDTO(spotPermId, col + 1, row + 1, contId)); - } + stackImages = new ArrayList<AcquiredImageInStack>(); } + stackImages.add(makeAcquiredImageInStack(image)); + map.put(stackDTO, stackImages); + } + return map; + } + + private static AcquiredImageInStack makeAcquiredImageInStack(AcquiredPlateImage image) + { + return new AcquiredImageInStack(image.getChannelName(), image.getImageFilePath()); + } + + private static ImgChannelStackDTO makeStackDTO(AcquiredPlateImage image, Long[][] spotIds, + long datasetId) + { + long spotId = getSpotId(image, spotIds); + return new ImgChannelStackDTO(image.getTileRow(), image.getTileColumn(), datasetId, spotId); + } + + private static long getSpotId(AcquiredPlateImage image, Long[][] spotIds) + { + int wellRow = image.getWellRow(); + int wellColumn = image.getWellColumn(); + Long spotId = spotIds[wellRow - 1][wellColumn - 1]; + assert spotId != null : "no spot for " + image; + return spotId; + } + + private void createImages(Map<ImgChannelStackDTO, List<AcquiredImageInStack>> stackImagesMap, + Map<String, Long> channelsMap) + { + for (Entry<ImgChannelStackDTO, List<AcquiredImageInStack>> entry : stackImagesMap + .entrySet()) + { + long stackId = entry.getKey().getId(); + createImages(stackId, channelsMap, entry.getValue()); + } + } + + private void createImages(long stackId, Map<String, Long> channelsMap, + List<AcquiredImageInStack> images) + { + for (AcquiredImageInStack image : images) + { + long channelTechId = channelsMap.get(image.getChannelName()); + createImage(stackId, channelTechId, image); + } + } + + private void createImage(long stackId, long channelTechId, AcquiredImageInStack image) + { + long imageId = dao.addImage(new ImgImageDTO(image.getImageFilePath().getImagePath())); + ImgAcquiredImageDTO acquiredImage = + new ImgAcquiredImageDTO(imageId, stackId, channelTechId); + dao.addAcquiredImage(acquiredImage); + } + + private void createChannelStacks(Set<ImgChannelStackDTO> stacks) + { + for (ImgChannelStackDTO stack : stacks) + { + long id = dao.addChannelStack(stack); + stack.setId(id); + } + } + + // returns a matrix of spot tech ids. The matrix[row][col] contains null is spot at (row,col) + // does not exist. Spot coordinates are 0-based in the matrix. + private Long[][] getOrCreateSpots(long contId, ScreeningContainerDatasetInfo info, + List<AcquiredPlateImage> images) + { + List<ImgSpotDTO> allSpots = dao.listSpots(contId); + List<ImgSpotDTO> newSpots = + createNewSpots(contId, images, allSpots, info.getContainerRows(), info + .getContainerColumns()); + allSpots.addAll(newSpots); + return makeTechIdMatrix(allSpots, info.getContainerRows(), info.getContainerColumns()); + } + + private List<ImgSpotDTO> createNewSpots(long contId, List<AcquiredPlateImage> images, + List<ImgSpotDTO> existingSpots, int rows, int columns) + { + Boolean[][] newSpotMatrix = extractNewSpots(rows, columns, images, existingSpots); + List<ImgSpotDTO> newSpots = makeSpotDTOs(newSpotMatrix, contId); + for (ImgSpotDTO spot : newSpots) + { + long id = dao.addSpot(spot); + spot.setId(id); } return newSpots; } - private static Set<String> asPermIdSet(List<ImgSpotDTO> spots) + private Boolean[][] extractNewSpots(int rows, int columns, List<AcquiredPlateImage> images, + List<ImgSpotDTO> existingSpots) + { + Boolean[][] spots = extractExistingSpots(rows, columns, images); + unmarkSpots(existingSpots, spots); + return spots; + } + + private static Boolean[][] extractExistingSpots(int rows, int columns, + List<AcquiredPlateImage> images) { - Set<String> set = new HashSet<String>(); - for (ImgSpotDTO spot : spots) + Boolean[][] spots = new Boolean[rows][columns]; + for (AcquiredPlateImage image : images) { - set.add(spot.getPermId()); + spots[image.getWellRow() - 1][image.getWellColumn() - 1] = true; } - return set; + return spots; } - private static List<String> asList(String[][] spotPermIds) + private static Long[][] makeTechIdMatrix(List<ImgSpotDTO> existingSpots, int rows, int columns) { - List<String> result = new ArrayList<String>(); - for (int row = 0; row < spotPermIds.length; row++) + Long[][] matrix = new Long[rows][columns]; + for (ImgSpotDTO spot : existingSpots) { - String[] spotRow = spotPermIds[row]; + matrix[spot.getRow() - 1][spot.getColumn() - 1] = spot.getId(); + } + return matrix; + } + + private static List<ImgSpotDTO> makeSpotDTOs(Boolean[][] spots, long contId) + { + + List<ImgSpotDTO> newSpots = new ArrayList<ImgSpotDTO>(); + for (int row = 0; row < spots.length; row++) + { + Boolean[] spotRow = spots[row]; for (int col = 0; col < spotRow.length; col++) { - result.add(spotRow[col]); + Boolean wanted = spotRow[col]; + if (wanted != null && wanted) + { + newSpots.add(new ImgSpotDTO(row + 1, col + 1, contId)); + } } } - return result; + return newSpots; + } + + private static void unmarkSpots(List<ImgSpotDTO> existingSpots, Boolean[][] spotMatrix) + { + for (ImgSpotDTO existingSpot : existingSpots) + { + spotMatrix[existingSpot.getRow()][existingSpot.getColumn()] = false; + } } private long createDataset(long contId, ScreeningContainerDatasetInfo info) { ImgDatasetDTO dataset = - new ImgDatasetDTO(info.getDatasetPermId(), info.getTileWidth(), info - .getTileHeight(), contId); + new ImgDatasetDTO(info.getDatasetPermId(), info.getTileRows(), info + .getTileColumns(), contId); return dao.addDataset(dataset); } @@ -151,8 +334,8 @@ class HCSDatasetUploader } else { ImgContainerDTO container = - new ImgContainerDTO(containerPermId, info.getContainerWidth(), info - .getContainerHeight(), expId); + new ImgContainerDTO(containerPermId, info.getContainerRows(), info + .getContainerColumns(), expId); return dao.addContainer(container); } } diff --git a/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/HCSImageCheckList.java b/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/HCSImageCheckList.java new file mode 100644 index 0000000000000000000000000000000000000000..70b6872723a9490090b889088d7d2cdbd1cd62ad --- /dev/null +++ b/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/HCSImageCheckList.java @@ -0,0 +1,164 @@ +/* + * Copyright 2008 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 java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import ch.systemsx.cisd.bds.hcs.Geometry; +import ch.systemsx.cisd.common.utilities.AbstractHashable; + +/** + * Helper class to set the <code>is_complete</code> flag in the <i>BDS</i> library. + * <p> + * All the possible combinations are computed in the constructor. This class is also able to spot + * images which have already been handled. + * </p> + * + * @author Franz-Josef Elmer + */ +public final class HCSImageCheckList +{ + + private final Map<FullLocation, Check> imageMap; + + public HCSImageCheckList(final String[] channelNames, final Geometry plateGeometry, + final Geometry wellGeometry) + { + if (channelNames.length < 1) + { + throw new IllegalArgumentException("Number of channels smaller than one."); + } + if (plateGeometry == null) + { + throw new IllegalArgumentException("Unspecified plate geometry."); + } + if (wellGeometry == null) + { + throw new IllegalArgumentException("Unspecified well geometry."); + } + imageMap = new HashMap<FullLocation, Check>(); + for (String channelName : channelNames) + { + for (int wellCol = 1; wellCol <= plateGeometry.getColumns(); wellCol++) + { + for (int wellRow = 1; wellRow <= plateGeometry.getRows(); wellRow++) + { + for (int tileCol = 1; tileCol <= wellGeometry.getColumns(); tileCol++) + { + for (int tileRow = 1; tileRow <= wellGeometry.getRows(); tileRow++) + { + imageMap.put(new FullLocation(wellRow, wellCol, tileRow, tileCol, + channelName), new Check()); + } + } + } + } + } + assert imageMap.size() == channelNames.length * plateGeometry.getColumns() + * plateGeometry.getRows() * wellGeometry.getColumns() * wellGeometry.getRows() : "Wrong map size"; + } + + public final void checkOff(AcquiredPlateImage image) + { + assert image != null : "Unspecified image."; + final Check check = imageMap.get(createLocation(image)); + if (check == null) + { + throw new IllegalArgumentException("Invalid channel/well/tile: " + image); + } + if (check.isCheckedOff()) + { + throw new IllegalArgumentException("Image already handled: " + image); + } + check.checkOff(); + } + + private static FullLocation createLocation(AcquiredPlateImage image) + { + return new FullLocation(image.getWellRow(), image.getWellColumn(), image.getTileRow(), + image.getTileColumn(), image.getChannelName()); + } + + public final List<FullLocation> getCheckedOnFullLocations() + { + final List<FullLocation> fullLocations = new ArrayList<FullLocation>(); + for (final Map.Entry<FullLocation, Check> entry : imageMap.entrySet()) + { + if (entry.getValue().isCheckedOff() == false) + { + fullLocations.add(entry.getKey()); + } + } + return fullLocations; + } + + // + // Helper classes + // + + private static final class Check + { + private boolean checkedOff; + + final void checkOff() + { + checkedOff = true; + } + + final boolean isCheckedOff() + { + return checkedOff; + } + } + + public final static class FullLocation extends AbstractHashable + { + final int wellRow, wellCol; + + final int tileRow, tileCol; + + final String channelName; + + public FullLocation(int wellRow, int wellCol, int tileRow, int tileCol, String channelName) + { + this.wellRow = wellRow; + this.wellCol = wellCol; + this.tileRow = tileRow; + this.tileCol = tileCol; + this.channelName = channelName; + } + + private final static String toString(final int row, final int col, final String type) + { + return type + "= (" + row + ", " + col + ")"; + } + + // + // AbstractHashable + // + + @Override + public final String toString() + { + return "[ channel = " + channelName + "," + toString(wellRow, wellCol, "well") + "," + + toString(tileRow, tileCol, "tile") + "]"; + } + } +} diff --git a/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/HCSImageFileExtractionResult.java b/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/HCSImageFileExtractionResult.java index 66b14cb63ea7daf38a01b9e624f7479165108775..25d3cd0ece4d0c2d710d7b944170136e848d52d9 100644 --- a/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/HCSImageFileExtractionResult.java +++ b/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/HCSImageFileExtractionResult.java @@ -19,8 +19,6 @@ import java.io.File; import java.util.List; import java.util.Set; -import ch.systemsx.cisd.bds.hcs.Channel; - /** * Class which contains the image extraction process results. * @@ -58,4 +56,46 @@ public final class HCSImageFileExtractionResult { return channels; } + + /** + * A channel in which the image has been acquired. + * <p> + * Each channel has its <code>name</code> which uniquely identifies it in one experiment or + * dataset. + * </p> + * + * @author Tomasz Pylak + */ + public static final class Channel + { + private final String name; + + private final String description; + + private final int wavelength; + + public Channel(String name, String description, int wavelength) + { + assert name != null : "name is null"; + this.name = name; + this.description = description; + this.wavelength = wavelength; + } + + public String getName() + { + return name; + } + + public String getDescription() + { + return description; + } + + public int getWavelength() + { + return wavelength; + } + } + } \ No newline at end of file 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 3d05045fb308754ac7a85eac7e12459ca395a731..4017a3dda27298e899bf88451d9a92137e154189 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 @@ -19,8 +19,10 @@ package ch.systemsx.cisd.openbis.dss.etl; import java.io.File; import java.io.IOException; import java.util.ArrayList; +import java.util.HashSet; import java.util.List; import java.util.Properties; +import java.util.Set; import javax.sql.DataSource; @@ -30,6 +32,7 @@ import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.time.DurationFormatUtils; import org.apache.log4j.Logger; +import ch.systemsx.cisd.bds.hcs.Channel; import ch.systemsx.cisd.bds.hcs.Geometry; import ch.systemsx.cisd.bds.hcs.Location; import ch.systemsx.cisd.bds.hcs.PlateGeometry; @@ -48,12 +51,11 @@ import ch.systemsx.cisd.common.mail.IMailClient; import ch.systemsx.cisd.common.utilities.ClassUtils; import ch.systemsx.cisd.common.utilities.PropertyUtils; import ch.systemsx.cisd.etlserver.AbstractStorageProcessor; -import ch.systemsx.cisd.etlserver.HCSImageCheckList; import ch.systemsx.cisd.etlserver.IHCSImageFileAccepter; import ch.systemsx.cisd.etlserver.ITypeExtractor; import ch.systemsx.cisd.etlserver.PlateDimension; import ch.systemsx.cisd.etlserver.PlateDimensionParser; -import ch.systemsx.cisd.etlserver.HCSImageCheckList.FullLocation; +import ch.systemsx.cisd.openbis.dss.etl.HCSImageCheckList.FullLocation; import ch.systemsx.cisd.openbis.dss.etl.dataaccess.IImagingUploadDAO; import ch.systemsx.cisd.openbis.dss.generic.shared.ServiceProvider; import ch.systemsx.cisd.openbis.dss.generic.shared.dto.DataSetInformation; @@ -77,21 +79,24 @@ public final class PlateStorageProcessor extends AbstractStorageProcessor private static final Logger notificationLog = LogFactory.getLogger(LogCategory.OPERATION, PlateStorageProcessor.class); - private static final String NUMBER_OF_CHANNELS_PROPERTY = "number_of_channels"; - private static final String SPOT_GEOMETRY_PROPERTY = "well_geometry"; private static final String FILE_EXTRACTOR_PROPERTY = "file-extractor"; private static final String DEPRECATED_FILE_EXTRACTOR_PROPERTY = "deprecated-file-extractor"; + // comma separated list of channel names, order matters + private static final String CHANNEL_NAMES = "channel-names"; + + private static final String CHANNEL_SEPARATOR = ","; + // ----------- private final DataSource dataSource; private final Geometry spotGeometry; - private final int numberOfChannels; + private final String[] channelNames; // one of the extractors is always null and one not null private final IHCSImageFileExtractor imageFileExtractor; @@ -106,7 +111,7 @@ public final class PlateStorageProcessor extends AbstractStorageProcessor String spotGeometryText = getMandatoryProperty(SPOT_GEOMETRY_PROPERTY); this.spotGeometry = Geometry.createFromString(spotGeometryText); - this.numberOfChannels = extractNumberOfChannels(); + this.channelNames = extractChannelNames(properties); String fileExtractorClass = PropertyUtils.getProperty(properties, FILE_EXTRACTOR_PROPERTY); if (fileExtractorClass != null) @@ -126,16 +131,10 @@ public final class PlateStorageProcessor extends AbstractStorageProcessor this.currentTransaction = null; } - private int extractNumberOfChannels() + private static String[] extractChannelNames(Properties properties) { - int channels = PropertyUtils.getInt(properties, NUMBER_OF_CHANNELS_PROPERTY, -1); - if (channels == -1) - { - throw UserFailureException.fromTemplate( - "Unconfigured property %s for storage processor %s.", - NUMBER_OF_CHANNELS_PROPERTY, getClass().getName()); - } - return channels; + String names = PropertyUtils.getMandatoryProperty(properties, CHANNEL_NAMES); + return names.split(CHANNEL_SEPARATOR); } private IImagingUploadDAO createQuery() @@ -176,33 +175,17 @@ public final class PlateStorageProcessor extends AbstractStorageProcessor info.setDatasetPermId(dataSetInformation.getDataSetCode()); Geometry plateGeometry = getPlateGeometry(dataSetInformation); - int plateCols = plateGeometry.getColumns(); int plateRows = plateGeometry.getRows(); - info.setContainerWidth(plateCols); - info.setContainerHeight(plateRows); - - info.setTileWidth(spotGeometry.getColumns()); - info.setTileHeight(spotGeometry.getRows()); + int plateCols = plateGeometry.getColumns(); + info.setContainerRows(plateRows); + info.setContainerColumns(plateCols); - // TODO 2010-05-18, Tomasz Pylak: fetch spots from openBIS - info.setSpotPermIds(createDummySpotPermIds(plateRows, plateCols)); + info.setTileRows(spotGeometry.getRows()); + info.setTileColumns(spotGeometry.getColumns()); return info; } - private String[][] createDummySpotPermIds(int plateRows, int plateCols) - { - String[][] spots = new String[plateRows][plateCols]; - for (int row = 0; row < plateRows; row++) - { - for (int col = 0; col < plateCols; col++) - { - spots[row][col] = "dummy_" + row + "_" + col + "_" + System.currentTimeMillis(); - } - } - return spots; - } - private Geometry getPlateGeometry(final DataSetInformation dataSetInformation) { final IEntityProperty[] sampleProperties = dataSetInformation.getProperties(); @@ -284,17 +267,17 @@ public final class PlateStorageProcessor extends AbstractStorageProcessor { throw new UserFailureException("Experiment unknown for data set " + dataSetInformation); } - ScreeningContainerDatasetInfo info = - createScreeningDatasetInfo(experiment, dataSetInformation); - HCSImageFileExtractionResult extractionResult = extractImages(dataSetInformation, incomingDataSetDirectory); + ScreeningContainerDatasetInfo info = + createScreeningDatasetInfo(experiment, dataSetInformation); + validateImages(dataSetInformation, mailClient, incomingDataSetDirectory, info, extractionResult); moveFileToDirectory(incomingDataSetDirectory, originalFolder); - storeInDatabase(info, extractionResult.getImages()); + storeInDatabase(info, extractionResult.getImages(), extractionResult.getChannels()); return rootDirectory; } @@ -326,8 +309,7 @@ public final class PlateStorageProcessor extends AbstractStorageProcessor List<AcquiredPlateImage> images = extractionResult.getImages(); for (AcquiredPlateImage image : images) { - imageCheckList.checkOff(image.getChannel(), image.getWellLocation(), image - .getTileLocation()); + imageCheckList.checkOff(image); } } @@ -335,7 +317,7 @@ public final class PlateStorageProcessor extends AbstractStorageProcessor { Geometry plateGeometry = getPlateGeometry(info); HCSImageCheckList imageCheckList = - new HCSImageCheckList(numberOfChannels, plateGeometry, spotGeometry); + new HCSImageCheckList(channelNames, plateGeometry, spotGeometry); return imageCheckList; } @@ -346,7 +328,7 @@ public final class PlateStorageProcessor extends AbstractStorageProcessor IHCSImageFileExtractor extractor = imageFileExtractor; if (extractor == null) { - extractor = adapt(deprecatedImageFileExtractor, incomingDataSetDirectory); + extractor = adapt(deprecatedImageFileExtractor, incomingDataSetDirectory, channelNames); } final HCSImageFileExtractionResult result = extractor.extract(incomingDataSetDirectory, dataSetInformation); @@ -361,7 +343,7 @@ public final class PlateStorageProcessor extends AbstractStorageProcessor private static Geometry getPlateGeometry(ScreeningContainerDatasetInfo info) { - return new Geometry(info.getContainerHeight(), info.getContainerWidth()); + return new Geometry(info.getContainerRows(), info.getContainerColumns()); } @Override @@ -433,14 +415,15 @@ public final class PlateStorageProcessor extends AbstractStorageProcessor } } - private void storeInDatabase(ScreeningContainerDatasetInfo info, List<AcquiredPlateImage> images) + private void storeInDatabase(ScreeningContainerDatasetInfo info, + List<AcquiredPlateImage> images, Set<HCSImageFileExtractionResult.Channel> channels) { if (currentTransaction != null) { throw new IllegalStateException("previous transaction has not been commited!"); } currentTransaction = createQuery(); - HCSDatasetUploader.upload(currentTransaction, info, images); + HCSDatasetUploader.upload(currentTransaction, info, images, channels); } private void rollbackDatabaseChanges() @@ -566,7 +549,7 @@ public final class PlateStorageProcessor extends AbstractStorageProcessor // adapts old-style image extractor to the new one which is stateless private static IHCSImageFileExtractor adapt( final ch.systemsx.cisd.etlserver.IHCSImageFileExtractor extractor, - final File imageFileRootDirectory) + final File imageFileRootDirectory, final String[] channelNames) { return new IHCSImageFileExtractor() { @@ -574,14 +557,28 @@ public final class PlateStorageProcessor extends AbstractStorageProcessor DataSetInformation dataSetInformation) { HCSImageFileAccepter accepter = - new HCSImageFileAccepter(imageFileRootDirectory); + new HCSImageFileAccepter(imageFileRootDirectory, channelNames); ch.systemsx.cisd.etlserver.HCSImageFileExtractionResult originalResult = extractor.process(NodeFactory .createDirectoryNode(incomingDataSetDirectory), dataSetInformation, accepter); + Set<HCSImageFileExtractionResult.Channel> channels = + convert(originalResult.getChannels()); return new HCSImageFileExtractionResult(accepter.getImages(), - asRelativePaths(originalResult.getInvalidFiles()), originalResult - .getChannels()); + asRelativePaths(originalResult.getInvalidFiles()), channels); + } + + private Set<HCSImageFileExtractionResult.Channel> convert(Set<Channel> channels) + { + Set<HCSImageFileExtractionResult.Channel> result = + new HashSet<HCSImageFileExtractionResult.Channel>(); + for (Channel channel : channels) + { + String name = getChannelName(channelNames, channel.getCounter()); + result.add(new HCSImageFileExtractionResult.Channel(name, null, channel + .getWavelength())); + } + return result; } private List<File> asRelativePaths(List<IFile> files) @@ -596,15 +593,29 @@ public final class PlateStorageProcessor extends AbstractStorageProcessor }; } + private static String getChannelName(final String[] channelNames, int channelId) + { + if (channelId > channelNames.length) + { + throw UserFailureException.fromTemplate( + "Too large channel number %d, configured channels: %s.", channelId, + CollectionUtils.abbreviate(channelNames, -1)); + } + return channelNames[channelId - 1]; + } + private static final class HCSImageFileAccepter implements IHCSImageFileAccepter { private final List<AcquiredPlateImage> images = new ArrayList<AcquiredPlateImage>(); private final File imageFileRootDirectory; - public HCSImageFileAccepter(File imageFileRootDirectory) + private final String[] channelNames; + + public HCSImageFileAccepter(File imageFileRootDirectory, String[] channelNames) { this.imageFileRootDirectory = imageFileRootDirectory; + this.channelNames = channelNames; } public final void accept(final int channel, final Location wellLocation, @@ -614,8 +625,9 @@ public final class PlateStorageProcessor extends AbstractStorageProcessor FileUtilities.getRelativeFile(imageFileRootDirectory, new File(imageFile .getPath())); assert imageRelativePath != null : "Image relative path should not be null."; + String channelName = getChannelName(channelNames, channel); AcquiredPlateImage imageDesc = - new AcquiredPlateImage(wellLocation, tileLocation, channel, null, null, + new AcquiredPlateImage(wellLocation, tileLocation, channelName, null, null, new RelativeImagePath(imageRelativePath)); images.add(imageDesc); } diff --git a/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/ScreeningContainerDatasetInfo.java b/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/ScreeningContainerDatasetInfo.java index 176f9b3c09559b0d575da60b6f66588eb7f9d8d3..46a5705dbdf843bfd5a03f051543d31d1adc41ba 100644 --- a/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/ScreeningContainerDatasetInfo.java +++ b/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/ScreeningContainerDatasetInfo.java @@ -29,12 +29,9 @@ class ScreeningContainerDatasetInfo private String datasetPermId; - // [row][col] - private String[][] spotPermIds; + private int containerRows, containerColumns; - private int containerWidth, containerHeight; - - private int tileWidth, tileHeight; + private int tileRows, tileColumns; public String getExperimentPermId() { @@ -66,54 +63,44 @@ class ScreeningContainerDatasetInfo this.datasetPermId = datasetPermId; } - public String[][] getSpotPermIds() - { - return spotPermIds; - } - - public void setSpotPermIds(String[][] spotPermIds) - { - this.spotPermIds = spotPermIds; - } - - public int getContainerWidth() + public int getContainerRows() { - return containerWidth; + return containerRows; } - public void setContainerWidth(int containerWidth) + public void setContainerRows(int containerRows) { - this.containerWidth = containerWidth; + this.containerRows = containerRows; } - public int getContainerHeight() + public int getContainerColumns() { - return containerHeight; + return containerColumns; } - public void setContainerHeight(int containerHeight) + public void setContainerColumns(int containerColumns) { - this.containerHeight = containerHeight; + this.containerColumns = containerColumns; } - public int getTileWidth() + public int getTileRows() { - return tileWidth; + return tileRows; } - public void setTileWidth(int tileWidth) + public void setTileRows(int tileRows) { - this.tileWidth = tileWidth; + this.tileRows = tileRows; } - public int getTileHeight() + public int getTileColumns() { - return tileHeight; + return tileColumns; } - public void setTileHeight(int tileHeight) + public void setTileColumns(int tileColumns) { - this.tileHeight = tileHeight; + this.tileColumns = tileColumns; } } \ No newline at end of file diff --git a/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/dataaccess/HCSDatasetLoader.java b/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/dataaccess/HCSDatasetLoader.java index 90c54cf78d461da52cdd2da3126987524072d1b5..c4742d1454a76c272ed0285deb757638d51da7a0 100644 --- a/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/dataaccess/HCSDatasetLoader.java +++ b/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/dataaccess/HCSDatasetLoader.java @@ -71,7 +71,7 @@ public class HCSDatasetLoader public Geometry getPlateGeometry() { - return new Geometry(getContainer().getSpotHeight(), getContainer().getSpotWidth()); + return new Geometry(getContainer().getNumberOfRows(), getContainer().getNumberOfColumns()); } private ImgDatasetDTO getDataset() @@ -81,7 +81,8 @@ public class HCSDatasetLoader public Geometry getWellGeometry() { - return new Geometry(getDataset().getFieldsHeight(), getDataset().getFieldsWidth()); + return new Geometry(getDataset().getFieldNumberOfRows(), getDataset() + .getFieldNumberOfColumns()); } public long getChannelCount() @@ -103,10 +104,10 @@ public class HCSDatasetLoader Location tileLocation) { assert chosenChannel > 0; - assert tileLocation.getX() <= getDataset().getFieldsWidth(); - assert tileLocation.getY() <= getDataset().getFieldsHeight(); - assert wellLocation.getX() <= getContainer().getSpotWidth(); - assert wellLocation.getY() <= getContainer().getSpotHeight(); + assert tileLocation.getX() <= getDataset().getFieldNumberOfColumns(); + assert tileLocation.getY() <= getDataset().getFieldNumberOfRows(); + assert wellLocation.getX() <= getContainer().getNumberOfColumns(); + assert wellLocation.getY() <= getContainer().getNumberOfRows(); long[] channelIds = query.getChannelIdsByDatasetIdOrExperimentId(getDataset().getId(), getContainer() diff --git a/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/dataaccess/IImagingUploadDAO.java b/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/dataaccess/IImagingUploadDAO.java index 2e572ac15f463a4b0ecf21372926d7d3b434db79..299b774adea26d6ea9950bb95f286a8b69e9ab62 100644 --- a/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/dataaccess/IImagingUploadDAO.java +++ b/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/dataaccess/IImagingUploadDAO.java @@ -20,6 +20,7 @@ import java.util.List; import net.lemnik.eodsql.Select; import net.lemnik.eodsql.TransactionQuery; +import net.lemnik.eodsql.Update; import ch.systemsx.cisd.bds.hcs.Location; @@ -68,6 +69,12 @@ public interface IImagingUploadDAO extends TransactionQuery @Select(sql = "select id from CHANNELS where DS_ID = ?{1} or EXP_ID = ?{2} order by name", fetchSize = FETCH_SIZE) public long[] getChannelIdsByDatasetIdOrExperimentId(long datasetId, long experimentId); + @Select(sql = "select * from CHANNELS where EXP_ID = ?{1} order by name", fetchSize = FETCH_SIZE) + public List<ImgChannelDTO> getChannelsByExperimentId(long experimentId); + + @Select("select * from SPOTS where cont_id = ?{1}") + public List<ImgSpotDTO> listSpots(long contId); + // inserts @Select("insert into EXPERIMENTS (PERM_ID) values (?{1}) returning ID") @@ -82,27 +89,30 @@ public interface IImagingUploadDAO extends TransactionQuery public long addChannel(ImgChannelDTO channel); @Select("insert into CHANNEL_STACKS (X, Y, Z_in_M, T_in_SEC, DS_ID, SPOT_ID) values " - + "(?{1.x}, ?{1.y}, ?{1.z}, ?{1.t}, ?{1.datasetId}, ?{1.spotId}) returning ID") + + "(?{1.column}, ?{1.row}, ?{1.z}, ?{1.t}, ?{1.datasetId}, ?{1.spotId}) returning ID") public long addChannelStack(ImgChannelStackDTO channelStack); @Select("insert into CONTAINERS (PERM_ID, SPOTS_WIDTH, SPOTS_HEIGHT, EXPE_ID) values " - + "(?{1.permId}, ?{1.spotWidth}, ?{1.spotHeight}, ?{1.experimentId}) returning ID") + + "(?{1.permId}, ?{1.numberOfColumns}, ?{1.numberOfRows}, ?{1.experimentId}) returning ID") public long addContainer(ImgContainerDTO container); @Select("insert into DATA_SETS (PERM_ID, FIELDS_WIDTH, FIELDS_HEIGHT, CONT_ID) values " - + "(?{1.permId}, ?{1.fieldsWidth}, ?{1.fieldsHeight}, ?{1.containerId}) returning ID") + + "(?{1.permId}, ?{1.fieldNumberOfColumns}, ?{1.fieldNumberOfRows}, ?{1.containerId}) returning ID") public long addDataset(ImgDatasetDTO dataset); @Select("insert into IMAGES (PATH, PAGE, COLOR) values " + "(?{1.filePath}, ?{1.page}, ?{1.colorComponentAsString}) returning ID") public long addImage(ImgImageDTO image); - @Select("insert into SPOTS (PERM_ID, X, Y, CONT_ID) values " - + "(?{1.permId}, ?{1.x}, ?{1.y}, ?{1.containerId}) returning ID") + @Select("insert into SPOTS (X, Y, CONT_ID) values " + + "(?{1.column}, ?{1.row}, ?{1.containerId}) returning ID") public long addSpot(ImgSpotDTO spot); - // TODO 2010-05-19, Tomasz Pylak: implement me - @Select("select * from SPOTS where TODO") - public List<ImgSpotDTO> listSpots(List<String> spotPermIds); + // updates + + @Update("update CHANNELS " + + "set DESCRIPTION = ?{1.description}, WAVELENGTH = ?{1.wavelength} " + + "where ID = ?{1.id}") + public void updateChannel(ImgChannelDTO channel); } diff --git a/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/dataaccess/ImgAcquiredImageDTO.java b/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/dataaccess/ImgAcquiredImageDTO.java index 85d16ddca110e5e184f579da7fc60a242e31234c..72f54157aef2b0a601f11523f0b5d5666558d364 100644 --- a/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/dataaccess/ImgAcquiredImageDTO.java +++ b/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/dataaccess/ImgAcquiredImageDTO.java @@ -19,10 +19,12 @@ package ch.systemsx.cisd.openbis.dss.etl.dataaccess; import net.lemnik.eodsql.AutoGeneratedKeys; import net.lemnik.eodsql.ResultColumn; +import ch.systemsx.cisd.common.utilities.AbstractHashable; + /** * @author Tomasz Pylak */ -public class ImgAcquiredImageDTO +public class ImgAcquiredImageDTO extends AbstractHashable { @AutoGeneratedKeys private long id; @@ -40,6 +42,12 @@ public class ImgAcquiredImageDTO @ResultColumn("THUMBNAIL_ID") private Long thumbnailId; + @SuppressWarnings("unused") + private ImgAcquiredImageDTO() + { + // All Data-Object classes must have a default constructor. + } + public ImgAcquiredImageDTO(long imageId, long channelStackId, long channelId) { this.imageId = imageId; diff --git a/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/dataaccess/ImgChannelDTO.java b/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/dataaccess/ImgChannelDTO.java index 0178d712b7e135622ef5480c5b028c2b8d852337..fbc44eb0088f223217f885323f14cf0140ac8187 100644 --- a/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/dataaccess/ImgChannelDTO.java +++ b/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/dataaccess/ImgChannelDTO.java @@ -19,28 +19,12 @@ package ch.systemsx.cisd.openbis.dss.etl.dataaccess; import net.lemnik.eodsql.AutoGeneratedKeys; import net.lemnik.eodsql.ResultColumn; +import ch.systemsx.cisd.common.utilities.AbstractHashable; + /** * @author Tomasz Pylak */ -// CREATE TABLE CHANNELS ( -// ID BIGSERIAL NOT NULL, -// -// NAME NAME NOT NULL, -// DESCRIPTION DESCRIPTION, -// WAVELENGTH INTEGER, -// -// DS_ID TECH_ID, -// EXP_ID TECH_ID, -// -// PRIMARY KEY (ID), -// CONSTRAINT FK_CHANNELS_1 FOREIGN KEY (DS_ID) REFERENCES 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)) -// ); -public class ImgChannelDTO +public class ImgChannelDTO extends AbstractHashable { @AutoGeneratedKeys private long id; @@ -74,6 +58,11 @@ public class ImgChannelDTO return new ImgChannelDTO(name, descriptionOrNull, wavelengthOrNull, null, experimentId); } + private ImgChannelDTO() + { + // All Data-Object classes must have a default constructor. + } + private ImgChannelDTO(String name, String descriptionOrNull, Integer wavelengthOrNull, Long datasetIdOrNull, Long experimentIdOrNull) { diff --git a/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/dataaccess/ImgChannelStackDTO.java b/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/dataaccess/ImgChannelStackDTO.java index d92339eb5028a12d28f77bc2fc831a79ba97b499..44ff7859cef51079ce5b332482710249e8494998 100644 --- a/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/dataaccess/ImgChannelStackDTO.java +++ b/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/dataaccess/ImgChannelStackDTO.java @@ -19,22 +19,24 @@ package ch.systemsx.cisd.openbis.dss.etl.dataaccess; import net.lemnik.eodsql.AutoGeneratedKeys; import net.lemnik.eodsql.ResultColumn; +import ch.systemsx.cisd.common.utilities.AbstractHashable; + /** * @author Tomasz Pylak */ -public class ImgChannelStackDTO +public class ImgChannelStackDTO extends AbstractHashable { @AutoGeneratedKeys private long id; // x and y are kind of a two dimensional sequence number, (e.g. tile column) @ResultColumn("X") - private Integer x; + private Integer column; // x and y are kind of a two dimensional sequence number, (e.g. tile row, 1 is the first row) // Some use case may only use x and leave y alone. @ResultColumn("Y") - private Integer y; + private Integer row; // can be null @ResultColumn("Z_in_M") @@ -50,10 +52,16 @@ public class ImgChannelStackDTO @ResultColumn("SPOT_ID") private long spotId; - public ImgChannelStackDTO(int x, int y, long datasetId, long spotId) + @SuppressWarnings("unused") + private ImgChannelStackDTO() { - this.x = x; - this.y = y; + // All Data-Object classes must have a default constructor. + } + + public ImgChannelStackDTO(int row, int column, long datasetId, long spotId) + { + this.row = row; + this.column = column; this.datasetId = datasetId; this.spotId = spotId; } @@ -68,24 +76,14 @@ public class ImgChannelStackDTO this.id = id; } - public Integer getX() - { - return x; - } - - public void setX(Integer x) - { - this.x = x; - } - - public Integer getY() + public Integer getColumn() { - return y; + return column; } - public void setY(Integer y) + public Integer getRow() { - this.y = y; + return row; } public Float getZ() diff --git a/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/dataaccess/ImgContainerDTO.java b/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/dataaccess/ImgContainerDTO.java index 84a8a25e06b0eb1fd9697b4dc98509a47d7a288b..4427208dd4286348d36ff2e3b5f6de64fc85be92 100644 --- a/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/dataaccess/ImgContainerDTO.java +++ b/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/dataaccess/ImgContainerDTO.java @@ -19,12 +19,14 @@ package ch.systemsx.cisd.openbis.dss.etl.dataaccess; import net.lemnik.eodsql.AutoGeneratedKeys; import net.lemnik.eodsql.ResultColumn; +import ch.systemsx.cisd.common.utilities.AbstractHashable; + /** * DTO holding information about the container in the imaging database. * * @author Tomasz Pylak */ -public class ImgContainerDTO +public class ImgContainerDTO extends AbstractHashable { @AutoGeneratedKeys private long id; @@ -33,10 +35,10 @@ public class ImgContainerDTO private String permId; @ResultColumn("SPOTS_WIDTH") - private Integer spotWidth; + private Integer numberOfColumns; @ResultColumn("SPOTS_HEIGHT") - private Integer spotHeight; + private Integer numberOfRows; @ResultColumn("EXPE_ID") private long experimentId; @@ -46,11 +48,12 @@ public class ImgContainerDTO { } - public ImgContainerDTO(String permId, Integer spotWidth, Integer spotHeight, long experimentId) + public ImgContainerDTO(String permId, Integer numberOfRows, Integer numberOfColumns, + long experimentId) { this.permId = permId; - this.spotWidth = spotWidth; - this.spotHeight = spotHeight; + this.numberOfRows = numberOfRows; + this.numberOfColumns = numberOfColumns; this.experimentId = experimentId; } @@ -74,24 +77,24 @@ public class ImgContainerDTO this.permId = permId; } - public Integer getSpotWidth() + public Integer getNumberOfColumns() { - return spotWidth; + return numberOfColumns; } - public void setSpotWidth(Integer spotWidth) + public void setNumberOfColumns(Integer spotNumberOfColumns) { - this.spotWidth = spotWidth; + this.numberOfColumns = spotNumberOfColumns; } - public Integer getSpotHeight() + public Integer getNumberOfRows() { - return spotHeight; + return numberOfRows; } - public void setSpotHeight(Integer spotHeight) + public void setNumberOfRows(Integer spotNumberOfRows) { - this.spotHeight = spotHeight; + this.numberOfRows = spotNumberOfRows; } public long getExperimentId() diff --git a/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/dataaccess/ImgDatasetDTO.java b/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/dataaccess/ImgDatasetDTO.java index ede179f6edae390898ad2639a6f244c841525f4d..18a1ee6d1090b95769835af1ab91297690146f5f 100644 --- a/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/dataaccess/ImgDatasetDTO.java +++ b/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/dataaccess/ImgDatasetDTO.java @@ -19,10 +19,12 @@ package ch.systemsx.cisd.openbis.dss.etl.dataaccess; import net.lemnik.eodsql.AutoGeneratedKeys; import net.lemnik.eodsql.ResultColumn; +import ch.systemsx.cisd.common.utilities.AbstractHashable; + /** * @author Tomasz Pylak */ -public class ImgDatasetDTO +public class ImgDatasetDTO extends AbstractHashable { @AutoGeneratedKeys private long id; @@ -31,10 +33,10 @@ public class ImgDatasetDTO private String permId; @ResultColumn("FIELDS_WIDTH") - private Integer fieldsWidthOrNull; + private Integer fieldNumberOfColumnsOrNull; @ResultColumn("FIELDS_HEIGHT") - private Integer fieldsHeightOrNull; + private Integer fieldNumberOfRowsOrNull; @ResultColumn("CONT_ID") private long containerId; @@ -45,12 +47,12 @@ public class ImgDatasetDTO // All Data-Object classes must have a default constructor. } - public ImgDatasetDTO(String permId, Integer fieldsWidthOrNull, Integer fieldsHeightOrNull, - long containerId) + public ImgDatasetDTO(String permId, Integer fieldNumberOfRowsOrNull, + Integer fieldNumberOfColumnsOrNull, long containerId) { this.permId = permId; - this.fieldsWidthOrNull = fieldsWidthOrNull; - this.fieldsHeightOrNull = fieldsHeightOrNull; + this.fieldNumberOfColumnsOrNull = fieldNumberOfColumnsOrNull; + this.fieldNumberOfRowsOrNull = fieldNumberOfRowsOrNull; this.containerId = containerId; } @@ -74,24 +76,24 @@ public class ImgDatasetDTO this.permId = permId; } - public Integer getFieldsWidth() + public Integer getFieldNumberOfColumns() { - return fieldsWidthOrNull; + return fieldNumberOfColumnsOrNull; } - public void setFieldsWidth(Integer fieldsWidth) + public void setFieldNumberOfColumns(Integer numberOfColumns) { - this.fieldsWidthOrNull = fieldsWidth; + this.fieldNumberOfColumnsOrNull = numberOfColumns; } - public Integer getFieldsHeight() + public Integer getFieldNumberOfRows() { - return fieldsHeightOrNull; + return fieldNumberOfRowsOrNull; } - public void setFieldsHeight(Integer fieldsHeight) + public void setFieldNumberOfRows(Integer numberOfRows) { - this.fieldsHeightOrNull = fieldsHeight; + this.fieldNumberOfRowsOrNull = numberOfRows; } public long getContainerId() diff --git a/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/dataaccess/ImgImageDTO.java b/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/dataaccess/ImgImageDTO.java index 2ec7299980782cfd717bb78e30e3f6c14d988ea6..efc4c58517b575ff742f68ea59c7da6404c0e362 100644 --- a/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/dataaccess/ImgImageDTO.java +++ b/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/dataaccess/ImgImageDTO.java @@ -19,10 +19,12 @@ package ch.systemsx.cisd.openbis.dss.etl.dataaccess; import net.lemnik.eodsql.AutoGeneratedKeys; import net.lemnik.eodsql.ResultColumn; +import ch.systemsx.cisd.common.utilities.AbstractHashable; + /** * @author Tomasz Pylak */ -public class ImgImageDTO +public class ImgImageDTO extends AbstractHashable { @AutoGeneratedKeys private long id; diff --git a/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/dataaccess/ImgSpotDTO.java b/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/dataaccess/ImgSpotDTO.java index 1a0acb4a05ad624e154b709a305d5ebf6cbe3ba7..8815546b8ab69e95dba889e296c9296b8d461b25 100644 --- a/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/dataaccess/ImgSpotDTO.java +++ b/screening/source/java/ch/systemsx/cisd/openbis/dss/etl/dataaccess/ImgSpotDTO.java @@ -19,24 +19,23 @@ package ch.systemsx.cisd.openbis.dss.etl.dataaccess; import net.lemnik.eodsql.AutoGeneratedKeys; import net.lemnik.eodsql.ResultColumn; +import ch.systemsx.cisd.common.utilities.AbstractHashable; + /** * @author Tomasz Pylak */ -public class ImgSpotDTO +public class ImgSpotDTO extends AbstractHashable { @AutoGeneratedKeys private long id; - @ResultColumn("PERM_ID") - private String permId; - // position in the container, one-based (e.g. well column) @ResultColumn("X") - private Integer x; + private Integer column; // position in the container, one-based (e.g. well row, 1 is the first row) @ResultColumn("Y") - private Integer y; + private Integer row; @ResultColumn("CONT_ID") private long containerId; @@ -47,11 +46,10 @@ public class ImgSpotDTO // All Data-Object classes must have a default constructor. } - public ImgSpotDTO(String permId, Integer x, Integer y, long containerId) + public ImgSpotDTO(Integer row, Integer column, long containerId) { - this.permId = permId; - this.x = x; - this.y = y; + this.column = column; + this.row = row; this.containerId = containerId; } @@ -65,34 +63,24 @@ public class ImgSpotDTO this.id = id; } - public String getPermId() - { - return permId; - } - - public void setPermId(String permId) - { - this.permId = permId; - } - - public Integer getX() + public Integer getColumn() { - return x; + return column; } - public void setX(Integer x) + public void setColumn(Integer column) { - this.x = x; + this.column = column; } - public Integer getY() + public Integer getRow() { - return y; + return row; } - public void setY(Integer y) + public void setRow(Integer row) { - this.y = y; + this.row = row; } public long getContainerId() diff --git a/screening/source/sql/postgresql/001/schema-001.sql b/screening/source/sql/postgresql/001/schema-001.sql index 87fdd3993029413dc6c8b3001b68dbe95d72c7dc..11f5df79d0cd95e60481487e8f984723a84c1975 100644 --- a/screening/source/sql/postgresql/001/schema-001.sql +++ b/screening/source/sql/postgresql/001/schema-001.sql @@ -45,7 +45,6 @@ CREATE INDEX CONTAINERS_EXPE_IDX ON CONTAINERS(EXPE_ID); CREATE TABLE SPOTS ( ID BIGSERIAL NOT NULL, - PERM_ID CODE NOT NULL, -- position in the container, one-based X INTEGER, @@ -53,7 +52,6 @@ CREATE TABLE SPOTS ( CONT_ID TECH_ID NOT NULL, PRIMARY KEY (ID), - UNIQUE (PERM_ID), CONSTRAINT FK_SPOT_1 FOREIGN KEY (CONT_ID) REFERENCES CONTAINERS (ID) ON DELETE CASCADE ON UPDATE CASCADE ); diff --git a/screening/sourceTest/java/ch/systemsx/cisd/openbis/dss/etl/dataaccess/ImagingUploadDAOTest.java b/screening/sourceTest/java/ch/systemsx/cisd/openbis/dss/etl/dataaccess/ImagingUploadDAOTest.java index 49b0e489923f3cbbcd2b1731d45317ab74ea1879..1d94d01c45116ea9ced5a15671d067e436a752ce 100644 --- a/screening/sourceTest/java/ch/systemsx/cisd/openbis/dss/etl/dataaccess/ImagingUploadDAOTest.java +++ b/screening/sourceTest/java/ch/systemsx/cisd/openbis/dss/etl/dataaccess/ImagingUploadDAOTest.java @@ -20,6 +20,7 @@ import static org.testng.AssertJUnit.assertEquals; import static org.testng.AssertJUnit.assertNotNull; import java.sql.SQLException; +import java.util.List; import org.testng.AssertJUnit; import org.testng.annotations.BeforeClass; @@ -60,11 +61,9 @@ public class ImagingUploadDAOTest extends AbstractDBTest private static final String DS_PERM_ID = "dsId"; - private static final String SPOT_ID = "sId"; - private static final String DS_CHANNEL = "dsChannel"; - private static final String DESCRIPTION = "desc"; + private static final String CHANNEL_DESCRIPTION = "channel desc"; private static final String EXP_CHANNEL = "expChannel"; @@ -95,14 +94,7 @@ public class ImagingUploadDAOTest extends AbstractDBTest final long channelId1 = addDatasetChannel(datasetId); final long channelId2 = addExperimentChannel(experimentId); - // test countChannelByDatasetIdOrExperimentId - assertEquals(2, dao.countChannelByDatasetIdOrExperimentId(datasetId, experimentId)); - - // test getChannelIdsByDatasetIdOrExperimentId - long[] channels = dao.getChannelIdsByDatasetIdOrExperimentId(datasetId, experimentId); - assertEquals(2, channels.length); - AssertJUnit.assertTrue(channels[0] == channelId1 && channels[1] == channelId2 - || channels[1] == channelId1 && channels[0] == channelId2); + testChannelMethods(experimentId, datasetId, channelId1, channelId2); // create channel stack, images and acquired images final long channelStackId = addChannelStack(datasetId, spotId); @@ -111,7 +103,11 @@ public class ImagingUploadDAOTest extends AbstractDBTest addAcquiredImage(imageId1, channelStackId, channelId1); addAcquiredImage(imageId2, channelStackId, channelId2); - // test getImage + testGetImage(datasetId, channelId1, channelId2); + } + + private void testGetImage(final long datasetId, final long channelId1, final long channelId2) + { ImgImageDTO image1 = dao.getImage(channelId1, datasetId, new Location(X_WELL_COLUMN, Y_WELL_ROW), new Location(X_TILE_COLUMN, Y_TILE_ROW)); @@ -125,6 +121,29 @@ public class ImagingUploadDAOTest extends AbstractDBTest assertEquals(ColorComponent.RED, image2.getColorComponent()); } + private void testChannelMethods(final long experimentId, final long datasetId, + final long channelId1, final long channelId2) + { + // test countChannelByDatasetIdOrExperimentId + assertEquals(2, dao.countChannelByDatasetIdOrExperimentId(datasetId, experimentId)); + + // test getChannelIdsByDatasetIdOrExperimentId + long[] channels = dao.getChannelIdsByDatasetIdOrExperimentId(datasetId, experimentId); + assertEquals(2, channels.length); + AssertJUnit.assertTrue(channels[0] == channelId1 && channels[1] == channelId2 + || channels[1] == channelId1 && channels[0] == channelId2); + + List<ImgChannelDTO> experimentChannels = dao.getChannelsByExperimentId(experimentId); + assertEquals(1, experimentChannels.size()); + + // test update + ImgChannelDTO channel = experimentChannels.get(0); + channel.setDescription("new " + CHANNEL_DESCRIPTION); + channel.setWavelength(WAVELENGTH + 100); + dao.updateChannel(channel); + assertEquals(channel, dao.getChannelsByExperimentId(experimentId).get(0)); + } + private long addImage(String path, ColorComponent colorComponent) { final ImgImageDTO image = new ImgImageDTO(path, PAGE, colorComponent); @@ -147,15 +166,15 @@ public class ImagingUploadDAOTest extends AbstractDBTest final Integer spotWidth = 1; final Integer spotHeight = 2; final ImgContainerDTO container = - new ImgContainerDTO(permId, spotWidth, spotHeight, experimentId); + new ImgContainerDTO(permId, spotHeight, spotWidth, experimentId); final Long containerId = dao.addContainer(container); // test tryGetContainerIdPermId assertEquals(containerId, dao.tryGetContainerIdPermId(permId)); final ImgContainerDTO loadedContainer = dao.getContainerById(containerId); assertNotNull(loadedContainer); assertEquals(permId, loadedContainer.getPermId()); - assertEquals(spotWidth, loadedContainer.getSpotWidth()); - assertEquals(spotHeight, loadedContainer.getSpotHeight()); + assertEquals(spotWidth, loadedContainer.getNumberOfColumns()); + assertEquals(spotHeight, loadedContainer.getNumberOfRows()); assertEquals(experimentId, loadedContainer.getExperimentId()); return containerId; @@ -167,14 +186,14 @@ public class ImagingUploadDAOTest extends AbstractDBTest final Integer fieldsWidth = 1; final Integer fieldsHeight = 2; final ImgDatasetDTO dataset = - new ImgDatasetDTO(permId, fieldsWidth, fieldsHeight, containerId); + new ImgDatasetDTO(permId, fieldsHeight, fieldsWidth, containerId); final long datasetId = dao.addDataset(dataset); final ImgDatasetDTO loadedDataset = dao.tryGetDatasetByPermId(DS_PERM_ID); assertNotNull(loadedDataset); assertEquals(permId, loadedDataset.getPermId()); - assertEquals(fieldsWidth, loadedDataset.getFieldsWidth()); - assertEquals(fieldsHeight, loadedDataset.getFieldsHeight()); + assertEquals(fieldsWidth, loadedDataset.getFieldNumberOfColumns()); + assertEquals(fieldsHeight, loadedDataset.getFieldNumberOfRows()); assertEquals(containerId, loadedDataset.getContainerId()); return datasetId; @@ -182,21 +201,22 @@ public class ImagingUploadDAOTest extends AbstractDBTest private long addSpot(long containerId) { - final ImgSpotDTO spot = new ImgSpotDTO(SPOT_ID, X_WELL_COLUMN, Y_WELL_ROW, containerId); + final ImgSpotDTO spot = new ImgSpotDTO(Y_WELL_ROW, X_WELL_COLUMN, containerId); return dao.addSpot(spot); } private long addDatasetChannel(long datasetId) { final ImgChannelDTO channel = - ImgChannelDTO.createDatasetChannel(DS_CHANNEL, DESCRIPTION, WAVELENGTH, datasetId); + ImgChannelDTO.createDatasetChannel(DS_CHANNEL, CHANNEL_DESCRIPTION, WAVELENGTH, + datasetId); return dao.addChannel(channel); } private long addExperimentChannel(long experimentId) { final ImgChannelDTO channel = - ImgChannelDTO.createExperimentChannel(EXP_CHANNEL, DESCRIPTION, WAVELENGTH, + ImgChannelDTO.createExperimentChannel(EXP_CHANNEL, CHANNEL_DESCRIPTION, WAVELENGTH, experimentId); return dao.addChannel(channel); } @@ -204,7 +224,7 @@ public class ImagingUploadDAOTest extends AbstractDBTest private long addChannelStack(long datasetId, long spotId) { final ImgChannelStackDTO channelStack = - new ImgChannelStackDTO(X_TILE_COLUMN, Y_TILE_ROW, datasetId, spotId); + new ImgChannelStackDTO(Y_TILE_ROW, X_TILE_COLUMN, datasetId, spotId); return dao.addChannelStack(channelStack); }