All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.labkey.remoteapi.test.MS2SearchClient Maven / Gradle / Ivy

Go to download

The client-side library for Java developers is a separate JAR from the LabKey Server code base. It can be used by any Java program, including another Java web application.

There is a newer version: 6.2.0
Show newest version
/*
 * Copyright (c) 2011-2015 LabKey Corporation
 *
 * 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 org.labkey.remoteapi.test;

import au.com.bytecode.opencsv.CSVReader;
import org.labkey.remoteapi.CommandException;
import org.labkey.remoteapi.Connection;
import org.labkey.remoteapi.assay.Batch;
import org.labkey.remoteapi.assay.Data;
import org.labkey.remoteapi.assay.Run;
import org.labkey.remoteapi.assay.SaveAssayBatchCommand;
import org.labkey.remoteapi.ms2.StartSearchCommand;

import java.io.*;
import java.util.*;

/**
 * Demo code that accepts one or more CSV files on the command line. It creates assay runs to hold the metadata
 * from the CSV, and then initiates MS2 searches on the files. If the files are not yet available, the jobs will block
 * until the server is notified that files are available (see PipelineFileAvailableClient).
 *
 * Expects to receive the path to one or more CSV files on the command line. Each CSV is required to have the following
 * columns:
 *
 * FileName: the name of the mzXML or RAW file to be searched
 * Path: the path to the file
 * Sample: the name of the sample. Used to determine if files are fractions or independent samples.
 * LabKeyFolder: the target folder in LabKey Server
 * ProtocolToRun: the name of the existing search protocol to be used when searching the files
 *
 * If other columns are present in the CSV, they will be associated with the assay runs as run properties if the assay
 * design includes run properties with the same names.
 *
 * By default looks for a config.properties in the working directory, but the location can be specified with a
 * -config=<PATH TO CONFIG FILE> argument.
 *
 * Expects the following config properties:
 * baseServerURL: base URL of the LabKey Server, such as "http://www.labkey.org"
 * username: credentials for logging in
 * password: credentials for logging in
 * pipelineRoot: the path to the pipeline root for the target folder. Used to pass a relative path to the server.  
 * assayId: the rowId of the GPAT assay design to be used for storing metadata
 * searchEngine: the name of the MS2 search engine to use. Supported values are XTandem, Sequest, and Mascot. 
 * debug: (Optional) If true, print verbose HTTP connection debugging information
 */
public class MS2SearchClient
{
    /** Simple bean class to hold the values that uniquely identify the files to be submitted in one search */
    private static class SearchInfo
    {
        private final String _sample;
        private final String _protocol;
        private final String _folder;
        private final String _path;

        private SearchInfo(String sample, String protocol, String folder, String path)
        {
            _sample = sample;
            _protocol = protocol;
            _folder = folder;
            _path = path;
        }

        public String getProtocol()
        {
            return _protocol;
        }

        public String getFolder()
        {
            return _folder;
        }

        public String getPath()
        {
            return _path;
        }

        public boolean equals(Object o)
        {
            if (this == o)
                return true;
            if (!(o instanceof SearchInfo))
                return false;
            SearchInfo that = (SearchInfo) o;
            if (_folder == null ? that._folder != null : !_folder.equals(that._folder))
                return false;
            if (_path == null ? that._path != null : !_path.equals(that._path))
                return false;
            if (_protocol == null ? that._protocol != null : !_protocol.equals(that._protocol))
                return false;
            return _sample == null ? that._sample == null : _sample.equals(that._sample);
        }

        public int hashCode()
        {
            int result = _sample == null ? 0 : _sample.hashCode();
            result = 31 * result + (_protocol == null ? 0 : _protocol.hashCode());
            result = 31 * result + (_folder == null ? 0 : _folder.hashCode());
            result = 31 * result + (_path == null ? 0 : _path.hashCode());
            return result;
        }

    }
    
    public static void main(String... args) throws Exception
    {
        ClientConfig config = new ClientConfig(args);

        int assayId = getAssayId(config);

        // Map from target folder path to assay batch
        Map batches = new HashMap<>();

        // Map from MS2 search info to the list of file names to be searched
        Map> searches = new HashMap<>();

        parseCSVs(args, batches, searches);

        Connection connection = config.createConnection();
        try
        {
            submitAssayBatches(assayId, batches, connection);
            submitSearches(config, searches, connection);
        }
        catch (CommandException e)
        {
            System.err.println((new StringBuilder()).append("Failure! Response code: ").append(e.getStatusCode()).toString());
            e.printStackTrace();
            System.err.println();
            if (e.getResponseText() != null)
            {
                System.err.println("Response text: ");
                System.err.println(e.getResponseText());
            }
            System.exit(1);
        }
    }

    /**
     * Initiates MS2 searches 
     */
    private static void submitSearches(ClientConfig config, Map> searches, Connection connection)
            throws IOException, CommandException
    {
        String pipelineRoot = config.getProperty("pipelineRoot");
        String searchEngineName = config.getProperty("searchEngine");

        StartSearchCommand.SearchEngine searchEngine = null;

        for (StartSearchCommand.SearchEngine engine : StartSearchCommand.SearchEngine.values())
        {
            if (engine.name().equalsIgnoreCase(searchEngineName))
            {
                searchEngine = engine;
            }
        }

        if (searchEngine == null)
        {
            throw new IllegalArgumentException("searchEngine '" + searchEngineName + "' is not one of the available options: " + Arrays.asList(StartSearchCommand.SearchEngine.values()));
        }

        for (Map.Entry> entry : searches.entrySet())
        {
            SearchInfo searchInfo = entry.getKey();

            // Figure out a relative path from the pipeline root
            String fullPath = searchInfo.getPath();
            if (!fullPath.toLowerCase().startsWith(pipelineRoot.toLowerCase()))
            {
                throw new IllegalArgumentException((new StringBuilder()).append("Could not determine relative path for '").append(fullPath).toString());
            }
            String relativePath = fullPath.substring(pipelineRoot.length());
            relativePath = relativePath.replace('\\', '/');
            if (!relativePath.startsWith("/"))
            {
                relativePath = "/" + relativePath;
            }
            List fileNames = entry.getValue();

            System.out.print("Submitting search using protocol " + searchInfo.getProtocol() + " to " + searchInfo.getFolder() + ", with " + fileNames.size() + " files");
            StartSearchCommand startCommand = new StartSearchCommand(searchEngine, searchInfo.getProtocol(), relativePath, fileNames);
            startCommand.execute(connection, searchInfo.getFolder());
        }
    }

    /**
     * Iterate over all of the batch objects and submit them to the right target folder
     *
     * @param assayId rowId of the target assay design
     * @param batches a map from target folder to assay batch
     */
    private static void submitAssayBatches(int assayId, Map batches, Connection connection)
            throws IOException, CommandException
    {
        for (Map.Entry entry : batches.entrySet())
        {
            Batch batch = entry.getValue();
            String targetFolder = entry.getKey();
            SaveAssayBatchCommand command = new SaveAssayBatchCommand(assayId, batch);
            System.out.print("Submitting metadata from " + batch + " to " + targetFolder + ", describing " + batch.getRuns().size() + " files");
            command.execute(connection, targetFolder);
        }
    }

    /**
     * Parse all of the CSVs passed on the command line and build up the list of assay batches to be inserted,
     * and the searches to be initiated.
     */
    private static void parseCSVs(String[] args, Map batches, Map> searches)
            throws IOException
    {
        for (String arg : args)
        {
            // Skip -config= or other non-CSV arguments
            if (!arg.startsWith("-"))
            {
                File csvFile = new File(arg);
                System.out.println("Processing " + csvFile);
                List lines;
                InputStream in = new FileInputStream(csvFile);
                try
                {
                    // Parse the CSVs into lists of string values
                    Reader reader = new InputStreamReader(in);
                    CSVReader csvReader = new CSVReader(reader, ',', '"', '~');
                    lines = csvReader.readAll();
                }
                finally
                {
                    in.close();
                }

                if (lines.size() < 2)
                {
                    throw new IllegalArgumentException("Expected to have at least two rows in CSV, one with headers and others with data in " + csvFile.getPath());
                }

                String headers[] = lines.get(0);
                // Look at each data row within the CSV
                for (int i = 1; i < lines.size(); i++)
                {
                    String[] line = lines.get(i);

                    // Skip lines that are completely empty
                    boolean blankLink = true;
                    for (String value : line)
                    {
                        if (value != null && !value.trim().isEmpty())
                        {
                            blankLink = false;
                            break;
                        }
                    }
                    if (blankLink)
                    {
                        continue;
                    }

                    // Pull out the required values
                    String fileName = getValue(line, headers, "FileName", i);
                    String path = getValue(line, headers, "Path", i);
                    String sample = getValue(line, headers, "Sample", i);
                    String folder = getValue(line, headers, "LabKeyFolder", i);
                    String protocol = getValue(line, headers, "ProtocolToRun", i);

                    // Create a map of all the properties. If the assay design has run properties with names that
                    // match, the values will be stored with the run.
                    Map runProperties = new HashMap<>();
                    for (int columnIndex = 0; columnIndex < line.length; columnIndex++)
                    {
                        runProperties.put(headers[columnIndex], line[columnIndex]);
                    }

                    // We want to create one batch per folder, so grab an existing batch if we have one
                    Batch batch = batches.get(folder);
                    if (batch == null)
                    {
                        // This is the first run for this folder, so create a new batch
                        batch = new Batch();
                        batch.setName(csvFile.getName());
                        batches.put(folder, batch);
                    }

                    addRunToBatch(batch, fileName, path, runProperties);

                    SearchInfo info = new SearchInfo(sample, protocol, folder, path);
                    List files = searches.get(info);
                    if (files == null)
                    {
                        files = new ArrayList();
                        searches.put(info, files);
                    }
                    files.add(fileName);
                }
            }
        }
    }

    /** @return the parsed assayId config property as an int */
    private static int getAssayId(ClientConfig config)
    {
        String assayIdString = config.getProperty("assayId");
        int assayId;
        try
        {
            assayId = Integer.parseInt(assayIdString);
        }
        catch (NumberFormatException e)
        {
            throw new IllegalArgumentException((new StringBuilder()).append("Could not parse assayId: ").append(assayIdString).toString(), e);
        }
        return assayId;
    }

    /**
     * @return the value for the given column in the specified line.
     * @param lineNumber used only to build up an error message if the value can't be found
     */
    private static String getValue(String line[], String headers[], String columnName, int lineNumber)
    {
        int columnIndex = findIndex(headers, columnName);
        if (columnIndex >= line.length)
            throw new IllegalArgumentException((new StringBuilder()).append("Row ").append(lineNumber).append(" is incomplete in CSV file. It contains fewer columns than the header does.").toString());
        String result = line[columnIndex];
        if (result == null || result.trim().equals(""))
            throw new IllegalArgumentException((new StringBuilder()).append("Row ").append(lineNumber).append(" is incomplete in CSV file, it does not include a value for '").append(columnName).append("'").toString());
        else
            return result.trim();
    }

    /** Adds a new run to the assay batch to store the metadata for the current input MS2 file */
    private static void addRunToBatch(Batch batch, String fileName, String path, Map runProperties)
    {
        Run run = new Run();
        run.setName(fileName);
        run.getProperties().putAll(runProperties);
        batch.getRuns().add(run);
        List outputFiles = new ArrayList<>();
        Data data = new Data();
        data.setAbsolutePath(path + File.separator + fileName);
        outputFiles.add(data);
        run.setDataOutputs(outputFiles);
    }

    /** Finds a column by name given the column headers */
    private static int findIndex(String[] headers, String propertyName)
    {
        for (int i = 0; i < headers.length; i++)
        {
            if (propertyName.equalsIgnoreCase(headers[i]))
                return i;
        }

        throw new IllegalArgumentException("Unable to find required property '" + propertyName + "' in input CSV file");
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy