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);
     }