extensions/net.sf.basedb.reggie/trunk/src/net/sf/basedb/reggie/grid/RnaSeqDemuxJobCreator.java

Code
Comments
Other
Rev Date Author Line
5486 12 Jun 19 nicklas 1 package net.sf.basedb.reggie.grid;
5486 12 Jun 19 nicklas 2
5486 12 Jun 19 nicklas 3 import java.io.ByteArrayInputStream;
5486 12 Jun 19 nicklas 4 import java.io.IOException;
6649 21 Mar 22 nicklas 5 import java.io.StringWriter;
5486 12 Jun 19 nicklas 6 import java.nio.charset.Charset;
5486 12 Jun 19 nicklas 7 import java.util.ArrayList;
5486 12 Jun 19 nicklas 8 import java.util.Arrays;
5486 12 Jun 19 nicklas 9 import java.util.HashMap;
5486 12 Jun 19 nicklas 10 import java.util.HashSet;
5486 12 Jun 19 nicklas 11 import java.util.List;
5486 12 Jun 19 nicklas 12 import java.util.Map;
5486 12 Jun 19 nicklas 13 import java.util.Set;
5486 12 Jun 19 nicklas 14 import java.util.TreeMap;
5486 12 Jun 19 nicklas 15 import java.util.TreeSet;
5486 12 Jun 19 nicklas 16 import java.util.regex.Matcher;
5486 12 Jun 19 nicklas 17 import java.util.regex.Pattern;
5486 12 Jun 19 nicklas 18
5486 12 Jun 19 nicklas 19 import org.json.simple.JSONArray;
7079 27 Mar 23 nicklas 20 import org.slf4j.LoggerFactory;
5486 12 Jun 19 nicklas 21
5486 12 Jun 19 nicklas 22 import net.sf.basedb.core.AnyToAny;
5486 12 Jun 19 nicklas 23 import net.sf.basedb.core.DataFileType;
5486 12 Jun 19 nicklas 24 import net.sf.basedb.core.DbControl;
5486 12 Jun 19 nicklas 25 import net.sf.basedb.core.DerivedBioAssay;
5486 12 Jun 19 nicklas 26 import net.sf.basedb.core.Directory;
5486 12 Jun 19 nicklas 27 import net.sf.basedb.core.Extract;
5486 12 Jun 19 nicklas 28 import net.sf.basedb.core.File;
5486 12 Jun 19 nicklas 29 import net.sf.basedb.core.FileServer;
5486 12 Jun 19 nicklas 30 import net.sf.basedb.core.FileSetMember;
5486 12 Jun 19 nicklas 31 import net.sf.basedb.core.ItemNotFoundException;
5486 12 Jun 19 nicklas 32 import net.sf.basedb.core.ItemQuery;
5486 12 Jun 19 nicklas 33 import net.sf.basedb.core.ItemSubtype;
5486 12 Jun 19 nicklas 34 import net.sf.basedb.core.Job;
5486 12 Jun 19 nicklas 35 import net.sf.basedb.core.Path;
5486 12 Jun 19 nicklas 36 import net.sf.basedb.core.PhysicalBioAssay;
5486 12 Jun 19 nicklas 37 import net.sf.basedb.core.SessionControl;
5486 12 Jun 19 nicklas 38 import net.sf.basedb.core.Software;
5486 12 Jun 19 nicklas 39 import net.sf.basedb.core.StringParameterType;
5486 12 Jun 19 nicklas 40 import net.sf.basedb.core.Tag;
5486 12 Jun 19 nicklas 41 import net.sf.basedb.opengrid.CmdResult;
5486 12 Jun 19 nicklas 42 import net.sf.basedb.opengrid.JobDefinition;
5486 12 Jun 19 nicklas 43 import net.sf.basedb.opengrid.JobStatus;
5486 12 Jun 19 nicklas 44 import net.sf.basedb.opengrid.OpenGrid;
5486 12 Jun 19 nicklas 45 import net.sf.basedb.opengrid.OpenGridCluster;
5486 12 Jun 19 nicklas 46 import net.sf.basedb.opengrid.OpenGridSession;
5486 12 Jun 19 nicklas 47 import net.sf.basedb.opengrid.ScriptBuilder;
5486 12 Jun 19 nicklas 48 import net.sf.basedb.opengrid.config.ClusterConfig;
5486 12 Jun 19 nicklas 49 import net.sf.basedb.opengrid.config.JobConfig;
6649 21 Mar 22 nicklas 50 import net.sf.basedb.opengrid.filetransfer.StringUploadSource;
5486 12 Jun 19 nicklas 51 import net.sf.basedb.opengrid.service.JobCompletionHandler;
5486 12 Jun 19 nicklas 52 import net.sf.basedb.reggie.Reggie;
5486 12 Jun 19 nicklas 53 import net.sf.basedb.reggie.XmlConfig;
5486 12 Jun 19 nicklas 54 import net.sf.basedb.reggie.dao.Annotationtype;
5486 12 Jun 19 nicklas 55 import net.sf.basedb.reggie.dao.Datafiletype;
5486 12 Jun 19 nicklas 56 import net.sf.basedb.reggie.dao.DemuxedSequences;
5486 12 Jun 19 nicklas 57 import net.sf.basedb.reggie.dao.Fileserver;
5486 12 Jun 19 nicklas 58 import net.sf.basedb.reggie.dao.FlowCell;
5486 12 Jun 19 nicklas 59 import net.sf.basedb.reggie.dao.Library;
5486 12 Jun 19 nicklas 60 import net.sf.basedb.reggie.dao.MergedSequences;
5486 12 Jun 19 nicklas 61 import net.sf.basedb.reggie.dao.Pipeline;
5486 12 Jun 19 nicklas 62 import net.sf.basedb.reggie.dao.Subtype;
5486 12 Jun 19 nicklas 63 import net.sf.basedb.reggie.plugins.BarcodeFilesForDemuxExporter;
6649 21 Mar 22 nicklas 64 import net.sf.basedb.reggie.projectarchive.FilePermission;
5486 12 Jun 19 nicklas 65 import net.sf.basedb.util.NameableComparator;
5486 12 Jun 19 nicklas 66 import net.sf.basedb.util.Values;
6649 21 Mar 22 nicklas 67 import net.sf.basedb.util.export.TableWriter;
7079 27 Mar 23 nicklas 68 import net.sf.basedb.util.extensions.logging.ExtensionsLog;
7079 27 Mar 23 nicklas 69 import net.sf.basedb.util.extensions.logging.ExtensionsLogger;
5486 12 Jun 19 nicklas 70 import net.sf.basedb.util.parser.FlatFileParser;
5486 12 Jun 19 nicklas 71
5486 12 Jun 19 nicklas 72 /**
5486 12 Jun 19 nicklas 73   Helper class for creating items needed for demuxing and merging
5486 12 Jun 19 nicklas 74   RNA sequencing data. It will generate the demux script and send it
5486 12 Jun 19 nicklas 75   to the cluster for execution.
5486 12 Jun 19 nicklas 76   
5486 12 Jun 19 nicklas 77   @author nicklas
5486 12 Jun 19 nicklas 78   @since 4.23
5486 12 Jun 19 nicklas 79 */
5486 12 Jun 19 nicklas 80 public class RnaSeqDemuxJobCreator 
5486 12 Jun 19 nicklas 81   extends DemuxJobCreator
5486 12 Jun 19 nicklas 82 {
5486 12 Jun 19 nicklas 83   /**
5486 12 Jun 19 nicklas 84     Max percentage of unmapped sequences before a warning is issued for
5486 12 Jun 19 nicklas 85     the demux.
5486 12 Jun 19 nicklas 86   */
5486 12 Jun 19 nicklas 87   public static final float MAX_UNMAPPED_PERCENT = 5f;
5486 12 Jun 19 nicklas 88   
5486 12 Jun 19 nicklas 89   /**
5486 12 Jun 19 nicklas 90     Max percentage of an unused barcode before a warning is issued for
5486 12 Jun 19 nicklas 91     the demux.
5486 12 Jun 19 nicklas 92   */
5486 12 Jun 19 nicklas 93   public static final float MAX_UNUSED_PERCENT = 1f;
5486 12 Jun 19 nicklas 94   
5486 12 Jun 19 nicklas 95   /**
5486 12 Jun 19 nicklas 96     If the mapped sequences for a library falls below this percentage a
5486 12 Jun 19 nicklas 97     warning is issued for the demux.
5486 12 Jun 19 nicklas 98   */
5486 12 Jun 19 nicklas 99   public static final float MIN_LIBRARY_PERCENT = 1f;
5486 12 Jun 19 nicklas 100
5486 12 Jun 19 nicklas 101   public RnaSeqDemuxJobCreator() 
5492 13 Jun 19 nicklas 102   {
5492 13 Jun 19 nicklas 103     super(Pipeline.RNA_SEQ);
5492 13 Jun 19 nicklas 104   }
5486 12 Jun 19 nicklas 105   
5486 12 Jun 19 nicklas 106   /**
5486 12 Jun 19 nicklas 107     Create a child bioassays for all given sequencing runs and libraries
5486 12 Jun 19 nicklas 108     and schedule a job on the given cluster for demuxing and merging the
5486 12 Jun 19 nicklas 109     data.
5486 12 Jun 19 nicklas 110     @return The demux job
5486 12 Jun 19 nicklas 111   */
5486 12 Jun 19 nicklas 112   @Override
5486 12 Jun 19 nicklas 113   public Job createDemuxJob(DbControl dc, OpenGridCluster cluster, List<DemuxDefinition> demuxDefs, JSONArray jsonMessages)
5486 12 Jun 19 nicklas 114   {
5486 12 Jun 19 nicklas 115     // Add 'rm' in script to remove files that we no longer need (to preserve disk space)
5486 12 Jun 19 nicklas 116     boolean earlyCleanup = !debug;
5486 12 Jun 19 nicklas 117   
5486 12 Jun 19 nicklas 118     SessionControl sc = dc.getSessionControl();
5486 12 Jun 19 nicklas 119     
5486 12 Jun 19 nicklas 120     String demuxParameterSet = (String)Annotationtype.PARAMETER_SET.getAnnotationValue(dc, demuxSoftware);
5486 12 Jun 19 nicklas 121     String mergeParameterSet = (String)Annotationtype.PARAMETER_SET.getAnnotationValue(dc, mergeSoftware);
5486 12 Jun 19 nicklas 122     
5486 12 Jun 19 nicklas 123     ClusterConfig clusterCfg = cluster.getConfig();
5486 12 Jun 19 nicklas 124     XmlConfig cfg = Reggie.getConfig(cluster.getId());
5486 12 Jun 19 nicklas 125     if (cfg == null)
5486 12 Jun 19 nicklas 126     {
5486 12 Jun 19 nicklas 127       throw new ItemNotFoundException("No configuration in reggie-config.xml for cluster: " + cluster.getId());
5486 12 Jun 19 nicklas 128     }
5486 12 Jun 19 nicklas 129   
5486 12 Jun 19 nicklas 130     // Get global options
6693 22 Apr 22 nicklas 131     String global_env = ScriptUtil.multilineIndent(cfg.getConfig("global-env"));
5486 12 Jun 19 nicklas 132     String runArchive = cfg.getRequiredConfig("run-archive", null);
5486 12 Jun 19 nicklas 133     List<String> allRunArchives = new ArrayList<>();
5486 12 Jun 19 nicklas 134     allRunArchives.add(runArchive);
5486 12 Jun 19 nicklas 135     allRunArchives.addAll(cfg.getConfigList("run-archive", 2));
6655 24 Mar 22 nicklas 136     String projectArchive = cfg.getRequiredConfig("project-archive", null);
6655 24 Mar 22 nicklas 137     String externalArchive = cfg.getConfig("external-archive", null, projectArchive);
6655 24 Mar 22 nicklas 138
5486 12 Jun 19 nicklas 139     // Options for the programs when demuxing
7372 06 Oct 23 nicklas 140     String demux_submit = cfg.getConfig("demux/submit", demuxParameterSet, null);
7372 06 Oct 23 nicklas 141     String demux_submit_debug = cfg.getConfig("demux/submit-debug", demuxParameterSet, null);
6655 24 Mar 22 nicklas 142     String demux_env = ScriptUtil.multilineIndent(cfg.getRequiredConfig("demux/env", demuxParameterSet));
6665 05 Apr 22 nicklas 143     String demux_debug = ScriptUtil.multilineIndent(cfg.getConfig("demux/env-debug", demuxParameterSet, null));
6655 24 Mar 22 nicklas 144     String demux_execute = ScriptUtil.multilineIndent(cfg.getConfig("demux/execute", demuxParameterSet, "./rnaseq-demux.sh"));
6649 21 Mar 22 nicklas 145
5486 12 Jun 19 nicklas 146     // We need all possible barcodes to create the 
5486 12 Jun 19 nicklas 147     // demux template files
5486 12 Jun 19 nicklas 148     ItemQuery<Tag> barcodeQuery = Tag.getQuery();
5486 12 Jun 19 nicklas 149     Subtype.BARCODE.addFilter(dc, barcodeQuery);
5486 12 Jun 19 nicklas 150     barcodeQuery.include(Reggie.INCLUDE_IN_CURRENT_PROJECT);
5486 12 Jun 19 nicklas 151     Pipeline.RNA_SEQ.addFilter(dc, barcodeQuery);
5486 12 Jun 19 nicklas 152     List<Tag> allTags = barcodeQuery.list(dc);
5486 12 Jun 19 nicklas 153     
5486 12 Jun 19 nicklas 154     // Options common for all jobs
5486 12 Jun 19 nicklas 155     JobConfig jobConfig = new JobConfig();
5486 12 Jun 19 nicklas 156     if (priority != null) jobConfig.setPriority(priority);
7372 06 Oct 23 nicklas 157     if (partition != null) jobConfig.setSbatchOption("partition", ScriptUtil.checkValidScriptParameter(partition));
7372 06 Oct 23 nicklas 158     jobConfig.convertOptionsTo(clusterCfg.getType());
7372 06 Oct 23 nicklas 159     if (submitOptionsOverride != null)
7372 06 Oct 23 nicklas 160     {
7372 06 Oct 23 nicklas 161       ScriptUtil.addSubmitOptions(jobConfig, submitOptionsOverride, clusterCfg.getType());
7372 06 Oct 23 nicklas 162     }
7372 06 Oct 23 nicklas 163     else
7372 06 Oct 23 nicklas 164     {
7372 06 Oct 23 nicklas 165       ScriptUtil.addSubmitOptions(jobConfig, demux_submit, clusterCfg.getType());
7372 06 Oct 23 nicklas 166       if (debug) ScriptUtil.addSubmitOptions(jobConfig, demux_submit_debug, clusterCfg.getType());
7372 06 Oct 23 nicklas 167     }
5486 12 Jun 19 nicklas 168     
5486 12 Jun 19 nicklas 169     // Create job
5492 13 Jun 19 nicklas 170     Job demuxJob = createJobItem(dc, "RNAseq demux", demuxDefs);
5486 12 Jun 19 nicklas 171     OpenGridSession ogSession = null;
5486 12 Jun 19 nicklas 172     try
5486 12 Jun 19 nicklas 173     {
5486 12 Jun 19 nicklas 174       ogSession = cluster.connect(5);
5486 12 Jun 19 nicklas 175   
5486 12 Jun 19 nicklas 176       // Generated script for demuxing and merging the selected sequencing runs
6674 11 Apr 22 nicklas 177       JobDefinition jobDef = new JobDefinition("RnaDemux", jobConfig, batchConfig, demuxJob);
5486 12 Jun 19 nicklas 178       jobDef.setDebug(debug);
5486 12 Jun 19 nicklas 179       
6649 21 Mar 22 nicklas 180       ScriptBuilder script = new ScriptBuilder();
6665 05 Apr 22 nicklas 181       script.cmd(debug ? "set -ex" : "set -e");
6649 21 Mar 22 nicklas 182       script.cmd("umask -S u=rwx,g=,o=");
5486 12 Jun 19 nicklas 183       script.newLine();
5486 12 Jun 19 nicklas 184       
6693 22 Apr 22 nicklas 185       script.cmd(global_env);
6655 24 Mar 22 nicklas 186       script.export("ProjectArchiveFolder", projectArchive);
6655 24 Mar 22 nicklas 187       script.export("ExternalArchiveFolder", externalArchive);
6665 05 Apr 22 nicklas 188       script.export("RunArchive", runArchive);
6649 21 Mar 22 nicklas 189       script.export("AllRunArchives", Values.getString(allRunArchives, " ", true));
5486 12 Jun 19 nicklas 190       
5486 12 Jun 19 nicklas 191       // Maps Library->MergedSequences that are created by this job
5486 12 Jun 19 nicklas 192       Map<Extract, DerivedBioAssay> mergedSequences = new TreeMap<Extract, DerivedBioAssay>(new NameableComparator<Extract>(false));
5865 12 Mar 20 nicklas 193       BarcodeFilesForDemuxExporter exporter = new BarcodeFilesForDemuxExporter(dc, allTags);
5486 12 Jun 19 nicklas 194   
6649 21 Mar 22 nicklas 195       // Write information needed for demux of each lane to 'lane_info.txt'
6649 21 Mar 22 nicklas 196       StringWriter lane_info = new StringWriter();
6649 21 Mar 22 nicklas 197       TableWriter lane_info_writer = new TableWriter(lane_info);
6655 24 Mar 22 nicklas 198       lane_info_writer.setDataSeparator(",");
5486 12 Jun 19 nicklas 199       for (DemuxDefinition def : demuxDefs)
5486 12 Jun 19 nicklas 200       {
5486 12 Jun 19 nicklas 201         DerivedBioAssay seqRun = def.seqRun.getDerivedBioAssay();
5486 12 Jun 19 nicklas 202         Annotationtype.AUTO_PROCESSING.setAnnotationValue(dc, seqRun, null);
5486 12 Jun 19 nicklas 203   
5486 12 Jun 19 nicklas 204         // Create DEMUXED derived bioassay item
5492 13 Jun 19 nicklas 205         DerivedBioAssay demux = createDemuxedSequences(dc, demuxJob, def);
6649 21 Mar 22 nicklas 206         String demuxName = ScriptUtil.checkValidScriptParameter(demux.getName());
6649 21 Mar 22 nicklas 207
5492 13 Jun 19 nicklas 208         def.existingMergedSequences = mergedSequences;
5492 13 Jun 19 nicklas 209         createAllMergedSequences(dc, demuxJob, def);
5492 13 Jun 19 nicklas 210         
5486 12 Jun 19 nicklas 211         // Load flow cell information for the sequencing run
5486 12 Jun 19 nicklas 212         FlowCell fc = FlowCell.getBySequencingRun(dc, def.seqRun);
5486 12 Jun 19 nicklas 213         PhysicalBioAssay flowCell = fc.getPhysicalBioAssay();
6649 21 Mar 22 nicklas 214         String dataFilesFolder = ScriptUtil.checkValidScriptParameter((String)Annotationtype.DATA_FILES_FOLDER.getAnnotationValue(dc, seqRun));
6649 21 Mar 22 nicklas 215         String sequencerName = ScriptUtil.checkValidScriptParameter(getSequencerName(seqRun.getHardware()));
5486 12 Jun 19 nicklas 216         Integer runNumber = (Integer)Annotationtype.SEQUENCING_RUN_NUMBER.getAnnotationValue(dc, seqRun);
5486 12 Jun 19 nicklas 217         String flowCellBarcode = ScriptUtil.checkValidScriptParameter((String)Annotationtype.FLOWCELL_ID.getAnnotationValue(dc, flowCell));
5486 12 Jun 19 nicklas 218         String flowCellType = (String)Annotationtype.FLOWCELL_TYPE.getAnnotationValue(dc, flowCell);
5865 12 Mar 20 nicklas 219         String debug_tileLimit = cfg.getConfig("demux/debug-tile-limit-"+flowCellType.toLowerCase(), demuxParameterSet, "2");
5492 13 Jun 19 nicklas 220
6655 24 Mar 22 nicklas 221         String extractOptions = "";
6655 24 Mar 22 nicklas 222         String fastqOptions = "-RUN_BARCODE "+runNumber+" -MACHINE_NAME "+sequencerName;
6649 21 Mar 22 nicklas 223         if (debug) fastqOptions += " -TILE_LIMIT "+debug_tileLimit;
6649 21 Mar 22 nicklas 224
5492 13 Jun 19 nicklas 225         for (LaneInfo lane : def.laneInfo)
5486 12 Jun 19 nicklas 226         {
5492 13 Jun 19 nicklas 227           // Export files required by Picard
5592 06 Sep 19 nicklas 228           exportMultiplexFiles(dc, exporter, jobDef, def, lane, false);
5486 12 Jun 19 nicklas 229           
6649 21 Mar 22 nicklas 230           String poolName = ScriptUtil.checkValidScriptParameter(lane.pool.getName());
6649 21 Mar 22 nicklas 231           lane_info_writer.tablePrintData(flowCellBarcode, lane.laneNo, demuxName, poolName, dataFilesFolder, def.readString, extractOptions, fastqOptions);
5486 12 Jun 19 nicklas 232         }
5486 12 Jun 19 nicklas 233         if (jsonMessages != null)
5486 12 Jun 19 nicklas 234         {
6649 21 Mar 22 nicklas 235           jsonMessages.add("Created " + demux.getName() + " for demuxing " + def.libsOnFlowCell.size() + " libraries on " + def.laneInfo.size() + " lanes.");
5486 12 Jun 19 nicklas 236         }
5486 12 Jun 19 nicklas 237       }
5486 12 Jun 19 nicklas 238       
5486 12 Jun 19 nicklas 239       if (jsonMessages != null)
5486 12 Jun 19 nicklas 240       {
5486 12 Jun 19 nicklas 241         jsonMessages.add("Created MergedSequences items for " + mergedSequences.size() + " libraries ");
5486 12 Jun 19 nicklas 242       }
6649 21 Mar 22 nicklas 243
6649 21 Mar 22 nicklas 244       // Write information needed for merge of data for each sequenced library
6649 21 Mar 22 nicklas 245       StringWriter merge_info = new StringWriter();
6649 21 Mar 22 nicklas 246       TableWriter merge_info_writer = new TableWriter(merge_info);
6655 24 Mar 22 nicklas 247       merge_info_writer.setDataSeparator(",");
6649 21 Mar 22 nicklas 248       ScriptBuilder chgrp = new ScriptBuilder();
6649 21 Mar 22 nicklas 249       ScriptBuilder umask = new ScriptBuilder();
5486 12 Jun 19 nicklas 250       for (DerivedBioAssay merged : mergedSequences.values())
5486 12 Jun 19 nicklas 251       {
5524 24 Jun 19 nicklas 252         String mergeName = ScriptUtil.checkValidFilename(merged.getName());
5553 12 Aug 19 nicklas 253         boolean isExternal = Reggie.isExternalItem(mergeName);
6649 21 Mar 22 nicklas 254         String baseFastqName = isExternal ? Reggie.removePrefix(mergeName) : mergeName;
6655 24 Mar 22 nicklas 255         String archiveFolder =  isExternal ? externalArchive : projectArchive;
6655 24 Mar 22 nicklas 256         String fastqFolder = archiveFolder + (String)Annotationtype.DATA_FILES_FOLDER.getAnnotationValue(dc, merged);
5596 11 Sep 19 nicklas 257
5596 11 Sep 19 nicklas 258         // Set file permissions based on consent or external group!
6649 21 Mar 22 nicklas 259         Library lib = Library.get(merged.getExtract());
5596 11 Sep 19 nicklas 260         String externalGroup = isExternal ? Reggie.getExternalGroup(mergeName) : null;
6649 21 Mar 22 nicklas 261         FilePermission p = ScriptUtil.setUmaskForItem(dc, lib, externalGroup, umask);
6649 21 Mar 22 nicklas 262         if (externalGroup != null)
5486 12 Jun 19 nicklas 263         {
6655 24 Mar 22 nicklas 264           ScriptUtil.addChgrp(externalGroup, fastqFolder, mergeName, archiveFolder+"/"+Reggie.getPrefix(mergeName), chgrp);
5486 12 Jun 19 nicklas 265         }
5486 12 Jun 19 nicklas 266         
6649 21 Mar 22 nicklas 267         merge_info_writer.tablePrintData(mergeName, fastqFolder, baseFastqName, p.getUmask());
6649 21 Mar 22 nicklas 268       }
6655 24 Mar 22 nicklas 269       script.cmd(demux_env);
6665 05 Apr 22 nicklas 270       if (debug) script.cmd(demux_debug);
6649 21 Mar 22 nicklas 271       script.cmd(demux_execute);
5486 12 Jun 19 nicklas 272       script.newLine();
6649 21 Mar 22 nicklas 273       script.join(chgrp);
5486 12 Jun 19 nicklas 274       
5486 12 Jun 19 nicklas 275       // submit to cluster
5486 12 Jun 19 nicklas 276       jobDef.setCmd(script.toString());
6649 21 Mar 22 nicklas 277       jobDef.addFile(new StringUploadSource("lane_info.txt", lane_info.toString()));
6649 21 Mar 22 nicklas 278       jobDef.addFile(new StringUploadSource("merge_info.txt", merge_info.toString()));
6649 21 Mar 22 nicklas 279       jobDef.addFile(ScriptUtil.upload("rnaseq-demux.sh"));
6649 21 Mar 22 nicklas 280       jobDef.addFile(ScriptUtil.upload("demux-utils.sh"));
6649 21 Mar 22 nicklas 281       jobDef.addFile(ScriptUtil.upload("reggie-utils.sh"));
6649 21 Mar 22 nicklas 282       jobDef.addFile(ScriptUtil.upload("stdwrap.sh"));
6649 21 Mar 22 nicklas 283       jobDef.addFile(ScriptUtil.upload("singlecolumnaverager.awk"));
6649 21 Mar 22 nicklas 284       jobDef.addFile(ScriptUtil.upload("readlength_averager.awk"));
5486 12 Jun 19 nicklas 285       
5486 12 Jun 19 nicklas 286       CmdResult<List<JobStatus>> qsub = ogSession.qsub(dc, Arrays.asList(jobDef));
5486 12 Jun 19 nicklas 287       qsub.throwExceptionIfNonZeroExitStatus();
5486 12 Jun 19 nicklas 288       
5486 12 Jun 19 nicklas 289       JobStatus jobStatus = qsub.getResult().get(0);
5492 13 Jun 19 nicklas 290       demuxJob.setParameterValue("jobName", new StringParameterType(), jobStatus.getName());
5486 12 Jun 19 nicklas 291       String jobId = jobStatus.getJobIdentifier().getClusterJobId();
5486 12 Jun 19 nicklas 292       if (jobId == null || jobId.equals(""))
5486 12 Jun 19 nicklas 293       {
5486 12 Jun 19 nicklas 294         demuxJob.doneError("Cluster returned no job-id for this job");
5486 12 Jun 19 nicklas 295       }
5486 12 Jun 19 nicklas 296     }
5486 12 Jun 19 nicklas 297     finally
5486 12 Jun 19 nicklas 298     {
5486 12 Jun 19 nicklas 299       OpenGrid.close(ogSession);
5486 12 Jun 19 nicklas 300     }
5486 12 Jun 19 nicklas 301     
5486 12 Jun 19 nicklas 302     return demuxJob;
5486 12 Jun 19 nicklas 303   }
5486 12 Jun 19 nicklas 304
5486 12 Jun 19 nicklas 305   /**
5486 12 Jun 19 nicklas 306     Job completion handler for demux/merge jobs. The handler downloads the
5486 12 Jun 19 nicklas 307     'demultiplex_metrics.txt' file from the job folder and parses out number of
5486 12 Jun 19 nicklas 308     reads and passed filter information for each sequenced library. The information
5486 12 Jun 19 nicklas 309     is stored on {@link MergedSequences} items in {@link Annotationtype#READS}
5486 12 Jun 19 nicklas 310     and {@link Annotationtype#PF_READS} annotations.
5486 12 Jun 19 nicklas 311   */
5486 12 Jun 19 nicklas 312   public static class DemuxJobCompletionHandler
5486 12 Jun 19 nicklas 313     implements JobCompletionHandler
5486 12 Jun 19 nicklas 314   {
7079 27 Mar 23 nicklas 315     private static final ExtensionsLogger logger = 
7079 27 Mar 23 nicklas 316       ExtensionsLog.getLogger(JobCompletionHandlerFactory.ID, true).wrap(LoggerFactory.getLogger(DemuxJobCompletionHandler.class));
7079 27 Mar 23 nicklas 317     
5486 12 Jun 19 nicklas 318     private XmlConfig cfg;
5486 12 Jun 19 nicklas 319     
5486 12 Jun 19 nicklas 320     public DemuxJobCompletionHandler()
5486 12 Jun 19 nicklas 321     {}
5486 12 Jun 19 nicklas 322   
5486 12 Jun 19 nicklas 323     @Override
5486 12 Jun 19 nicklas 324     public String jobCompleted(SessionControl sc, OpenGridSession session, Job job, JobStatus status)
5486 12 Jun 19 nicklas 325     {
5486 12 Jun 19 nicklas 326       String jobName = status.getName();
5486 12 Jun 19 nicklas 327       String metrics = session.getJobFileAsString(jobName, "demultiplex_metrics.txt", "UTF-8");
5486 12 Jun 19 nicklas 328       String skippedTiles = session.getJobFileAsString(jobName, "skipped_tiles.txt", "UTF-8");
5486 12 Jun 19 nicklas 329       String trimmomatic = session.getJobFileAsString(jobName, "trimmomatic.out", "UTF-8");
5486 12 Jun 19 nicklas 330       String fragments = session.getJobFileAsString(jobName, "fragments.out", "UTF-8");
6421 23 Sep 21 nicklas 331       String readlength = session.getJobFileAsString(jobName, "readlength.out", "UTF-8");
5486 12 Jun 19 nicklas 332       String files = session.getJobFileAsString(jobName, "files.out", "UTF-8");
5619 20 Sep 19 nicklas 333       String chgrp = null;
5619 20 Sep 19 nicklas 334       try
5619 20 Sep 19 nicklas 335       {
5619 20 Sep 19 nicklas 336         chgrp = session.getJobFileAsString(jobName, "chgrp.out", "UTF-8");
5619 20 Sep 19 nicklas 337       }
5619 20 Sep 19 nicklas 338       catch (RuntimeException ex)
5619 20 Sep 19 nicklas 339       {} // The file only exists if there are external samples that has been mapped to groups
5619 20 Sep 19 nicklas 340
5486 12 Jun 19 nicklas 341       cfg = Reggie.getConfig(session.getHost().getId());
5486 12 Jun 19 nicklas 342   
6421 23 Sep 21 nicklas 343       Reads total = parseDemultiplexMetrics(sc, metrics, skippedTiles, trimmomatic, fragments, readlength, files, chgrp);
5486 12 Jun 19 nicklas 344       String msg = Values.formatNumber(total.reads/1000000f, 1) + "M reads; ";
5486 12 Jun 19 nicklas 345       msg += Values.formatNumber(total.passedFilter/1000000f, 1) + "M passed filter; ";
5486 12 Jun 19 nicklas 346       msg += Values.formatNumber(total.passedTrimmomatic[1]/1000000f, 1) + "M passed trimmomatic; ";
5486 12 Jun 19 nicklas 347       if (total.warnings.size() > 0)
5486 12 Jun 19 nicklas 348       {
5486 12 Jun 19 nicklas 349         msg += total.warnings.size() + " warnings!";
5486 12 Jun 19 nicklas 350       }
5486 12 Jun 19 nicklas 351       return msg;
5486 12 Jun 19 nicklas 352     }
5486 12 Jun 19 nicklas 353     
6421 23 Sep 21 nicklas 354     private Reads parseDemultiplexMetrics(SessionControl sc, String metrics, String skippedTiles, String trimmomatic, String align, String readlength, String files, String chgrp)
5486 12 Jun 19 nicklas 355     {
5486 12 Jun 19 nicklas 356       Map<String, Reads> sumReads = new HashMap<String, Reads>();
5619 20 Sep 19 nicklas 357       Map<String, String> chgrpWarnings = ScriptUtil.parseChgrpErrors(chgrp);
5619 20 Sep 19 nicklas 358
5486 12 Jun 19 nicklas 359       // Parse the demultiplex_metrics file
5486 12 Jun 19 nicklas 360       FlatFileParser ffp = new FlatFileParser();
5486 12 Jun 19 nicklas 361       ffp.setIgnoreRegexp(Pattern.compile("#.*"));
5536 28 Jun 19 nicklas 362       ffp.setDataHeaderRegexp(Pattern.compile("BARCODE\t.*"));
5486 12 Jun 19 nicklas 363       ffp.setDataSplitterRegexp(Pattern.compile("\t"));
5486 12 Jun 19 nicklas 364       ffp.setSectionRegexp(Pattern.compile("#\\s\\[(.*)\\]"));
5486 12 Jun 19 nicklas 365       ffp.setHeaderRegexp(Pattern.compile("#\\s(\\w+)=(.*)"));
5486 12 Jun 19 nicklas 366       ffp.setInputStream(new ByteArrayInputStream(metrics.getBytes(Charset.forName("UTF-8"))), "UTF-8");
5486 12 Jun 19 nicklas 367       ffp.setMinDataColumns(10);
5486 12 Jun 19 nicklas 368   
5486 12 Jun 19 nicklas 369       Set<String> demuxNames = new HashSet<String>();
5486 12 Jun 19 nicklas 370       try
5486 12 Jun 19 nicklas 371       {
5486 12 Jun 19 nicklas 372         while (ffp.hasMoreSections())
5486 12 Jun 19 nicklas 373         {
5486 12 Jun 19 nicklas 374           FlatFileParser.Line section = ffp.nextSection();
5486 12 Jun 19 nicklas 375           String demuxName = section.name();
5486 12 Jun 19 nicklas 376           demuxNames.add(demuxName);
5486 12 Jun 19 nicklas 377           
5486 12 Jun 19 nicklas 378           Reads sumDemux = sumReads.get(demuxName);
5486 12 Jun 19 nicklas 379           if (sumDemux == null)
5486 12 Jun 19 nicklas 380           {
5486 12 Jun 19 nicklas 381             sumDemux = new Reads(demuxName);
5486 12 Jun 19 nicklas 382             sumReads.put(demuxName, sumDemux);
5486 12 Jun 19 nicklas 383           }
5486 12 Jun 19 nicklas 384           
5486 12 Jun 19 nicklas 385           ffp.parseHeaders();
5486 12 Jun 19 nicklas 386           int laneNo = Values.getInt(ffp.getHeader("Lane"));
5486 12 Jun 19 nicklas 387           String poolName = ffp.getHeader("Pool");
5486 12 Jun 19 nicklas 388           int barcodeCol = ffp.getColumnHeaderIndex("BARCODE");
5486 12 Jun 19 nicklas 389           int barcodeNameCol = ffp.getColumnHeaderIndex("BARCODE_NAME");
5486 12 Jun 19 nicklas 390           int libCol = ffp.getColumnHeaderIndex("LIBRARY_NAME");
5486 12 Jun 19 nicklas 391           int numReadsCol = ffp.getColumnHeaderIndex("READS");
5486 12 Jun 19 nicklas 392           int passedFilterCol = ffp.getColumnHeaderIndex("PF_READS");
5486 12 Jun 19 nicklas 393           int passedFilterPctCol = ffp.getColumnHeaderIndex("PF_PCT_MATCHES");
5486 12 Jun 19 nicklas 394           ffp.setUseNullIfEmpty(true);
5486 12 Jun 19 nicklas 395           
5486 12 Jun 19 nicklas 396           if (logger.isDebugEnabled())
5486 12 Jun 19 nicklas 397           {
5486 12 Jun 19 nicklas 398             logger.debug("Section: " + demuxName + "; lane: " + laneNo + "; pool: " + poolName);
5486 12 Jun 19 nicklas 399             logger.debug("Columns indexes: LIBRARY_NAME=" + libCol + "; READS="+numReadsCol + "; PF_READS="+passedFilterCol+"; PF_PCT_MATCHES="+passedFilterPctCol);
5486 12 Jun 19 nicklas 400           }
5486 12 Jun 19 nicklas 401   
5486 12 Jun 19 nicklas 402           while (ffp.hasMoreData())
5486 12 Jun 19 nicklas 403           {
5486 12 Jun 19 nicklas 404             FlatFileParser.Data line = ffp.nextData();
5486 12 Jun 19 nicklas 405             String libName = line.getString(libCol);
5486 12 Jun 19 nicklas 406             long numReads = Values.getLong(line.getString(numReadsCol), -1);
5486 12 Jun 19 nicklas 407             long numPassedFilter = Values.getLong(line.getString(passedFilterCol), 0);
5486 12 Jun 19 nicklas 408             float passedFilterPct = Values.getFloat(line.getString(passedFilterPctCol), 0) * 100;
5486 12 Jun 19 nicklas 409             
5486 12 Jun 19 nicklas 410             if (logger.isDebugEnabled())
5486 12 Jun 19 nicklas 411             {
5486 12 Jun 19 nicklas 412               logger.debug(libName + "; " + numReads + "; " + numPassedFilter +"; " + passedFilterPct);
5486 12 Jun 19 nicklas 413             }
5486 12 Jun 19 nicklas 414             
5486 12 Jun 19 nicklas 415             if (numReads >= 0)
5486 12 Jun 19 nicklas 416             {
5486 12 Jun 19 nicklas 417               if (libName == null)
5486 12 Jun 19 nicklas 418               {
5486 12 Jun 19 nicklas 419                 libName = demuxName+".N";
5486 12 Jun 19 nicklas 420                 if (passedFilterPct > MAX_UNMAPPED_PERCENT)
5486 12 Jun 19 nicklas 421                 {
5486 12 Jun 19 nicklas 422                   sumDemux.addWarning("PF_NNNN_PCT is " + Values.formatNumber(passedFilterPct, 2) + "% on lane " + laneNo + " (" + poolName + ")");
5486 12 Jun 19 nicklas 423                 }
5486 12 Jun 19 nicklas 424               }
5486 12 Jun 19 nicklas 425               else if ("IGNORED".equals(libName))
5486 12 Jun 19 nicklas 426               {
5486 12 Jun 19 nicklas 427                 libName = demuxName+".I";
5486 12 Jun 19 nicklas 428               }
5486 12 Jun 19 nicklas 429               else if ("UNUSED".equals(libName))
5486 12 Jun 19 nicklas 430               {
5486 12 Jun 19 nicklas 431                 libName = demuxName+".U";
5486 12 Jun 19 nicklas 432                 if (passedFilterPct > MAX_UNUSED_PERCENT)
5486 12 Jun 19 nicklas 433                 {
5486 12 Jun 19 nicklas 434                   sumDemux.addWarning("Unused barcode '" + line.getString(barcodeCol) + "' (" + line.getString(barcodeNameCol) + ") has " + Values.formatNumber(passedFilterPct, 2) + "% PF_READS on lane " + laneNo + " (" + poolName + ")");
5486 12 Jun 19 nicklas 435                 }
5486 12 Jun 19 nicklas 436               }
5486 12 Jun 19 nicklas 437               else
5486 12 Jun 19 nicklas 438               {
5486 12 Jun 19 nicklas 439                 if (passedFilterPct < MIN_LIBRARY_PERCENT)
5486 12 Jun 19 nicklas 440                 {
5486 12 Jun 19 nicklas 441                   sumDemux.addWarning("Library '" + libName + "' (" + line.getString(barcodeNameCol) + ") has " +Values.formatNumber(passedFilterPct, 2) + "% PF_READS on lane " + laneNo + " (" + poolName + ")");
5486 12 Jun 19 nicklas 442                 }
5486 12 Jun 19 nicklas 443               }
5486 12 Jun 19 nicklas 444               
5486 12 Jun 19 nicklas 445               Reads sum = sumReads.get(libName);
5486 12 Jun 19 nicklas 446               if (sum == null) 
5486 12 Jun 19 nicklas 447               {
5486 12 Jun 19 nicklas 448                 sum = new Reads(libName);
5486 12 Jun 19 nicklas 449                 sumReads.put(libName, sum);
5486 12 Jun 19 nicklas 450               }
5486 12 Jun 19 nicklas 451               sum.add(numReads, numPassedFilter);
5486 12 Jun 19 nicklas 452               sumDemux.add(numReads, numPassedFilter);
5486 12 Jun 19 nicklas 453             }
5619 20 Sep 19 nicklas 454             
5619 20 Sep 19 nicklas 455             if (chgrpWarnings.containsKey(libName)) 
5619 20 Sep 19 nicklas 456             {
5619 20 Sep 19 nicklas 457               sumDemux.addWarning(libName+": "+chgrpWarnings.get(libName));
5619 20 Sep 19 nicklas 458             }
5619 20 Sep 19 nicklas 459
5486 12 Jun 19 nicklas 460           }
5486 12 Jun 19 nicklas 461         }
5486 12 Jun 19 nicklas 462       }
5486 12 Jun 19 nicklas 463       catch (IOException ex)
5486 12 Jun 19 nicklas 464       {
5486 12 Jun 19 nicklas 465         logger.error(ex.getMessage(), ex);
5486 12 Jun 19 nicklas 466         throw new RuntimeException(ex);
5486 12 Jun 19 nicklas 467       }
5486 12 Jun 19 nicklas 468       
5486 12 Jun 19 nicklas 469       // Parse the skipped_tiles.txt file
5486 12 Jun 19 nicklas 470       Pattern sectionPattern = Pattern.compile("\\[(.*)\\]");
5486 12 Jun 19 nicklas 471       Pattern barcodeFilePattern = Pattern.compile(".*(\\d+)_(.+)_barcode.txt");
5486 12 Jun 19 nicklas 472       int lineNo = 0;
5486 12 Jun 19 nicklas 473       Reads currentDemux = null;
5486 12 Jun 19 nicklas 474       for (String line : skippedTiles.split("\n"))
5486 12 Jun 19 nicklas 475       {
5486 12 Jun 19 nicklas 476         lineNo++;
5486 12 Jun 19 nicklas 477         Matcher m = sectionPattern.matcher(line);
5486 12 Jun 19 nicklas 478         if (m.matches())
5486 12 Jun 19 nicklas 479         {
5486 12 Jun 19 nicklas 480           String demuxName = m.group(1);
5486 12 Jun 19 nicklas 481           currentDemux = sumReads.get(demuxName);
5486 12 Jun 19 nicklas 482           if (currentDemux == null)
5486 12 Jun 19 nicklas 483           {
5486 12 Jun 19 nicklas 484             logger.error("At line " + lineNo + ": Found skipped tiles section for '" + demuxName + "' but not demultiplex metrics");
5486 12 Jun 19 nicklas 485           }
5486 12 Jun 19 nicklas 486           continue;
5486 12 Jun 19 nicklas 487         }
5486 12 Jun 19 nicklas 488         m = barcodeFilePattern.matcher(line);
5486 12 Jun 19 nicklas 489         if (m.matches())
5486 12 Jun 19 nicklas 490         {
5486 12 Jun 19 nicklas 491           if (currentDemux == null)
5486 12 Jun 19 nicklas 492           {
5486 12 Jun 19 nicklas 493             logger.error("At line " + lineNo + ": Found skipped tiles data but has not found a demux name");
5486 12 Jun 19 nicklas 494           }
5486 12 Jun 19 nicklas 495           else
5486 12 Jun 19 nicklas 496           {
5486 12 Jun 19 nicklas 497             int laneNo = Values.getInt(m.group(1));
5486 12 Jun 19 nicklas 498             String tileNo = m.group(2);
5486 12 Jun 19 nicklas 499             currentDemux.addSkippedTile(laneNo, tileNo);
5486 12 Jun 19 nicklas 500             if (logger.isDebugEnabled())
5486 12 Jun 19 nicklas 501             {
5486 12 Jun 19 nicklas 502               logger.debug("Skipped tile: " + currentDemux.libName + "; lane=" + laneNo + "; tile=" + tileNo);
5486 12 Jun 19 nicklas 503             }
5486 12 Jun 19 nicklas 504           }
5486 12 Jun 19 nicklas 505         }
5486 12 Jun 19 nicklas 506       }
5486 12 Jun 19 nicklas 507       
5486 12 Jun 19 nicklas 508       // Parse the trimmomatic.out file
5486 12 Jun 19 nicklas 509       Pattern dataPattern = Pattern.compile(".*Both Surviving:\\s+(\\d+).*");
5486 12 Jun 19 nicklas 510       Reads currentLib = null;
5486 12 Jun 19 nicklas 511       lineNo = 0;
5486 12 Jun 19 nicklas 512       int trimmomaticStep = 0;
5486 12 Jun 19 nicklas 513       for (String line : trimmomatic.split("\n"))
5486 12 Jun 19 nicklas 514       {
5486 12 Jun 19 nicklas 515         lineNo++;
5486 12 Jun 19 nicklas 516         Matcher m = sectionPattern.matcher(line);
5486 12 Jun 19 nicklas 517         if (m.matches())
5486 12 Jun 19 nicklas 518         {
5486 12 Jun 19 nicklas 519           String libName = m.group(1);
5486 12 Jun 19 nicklas 520           currentLib = sumReads.get(libName);
5486 12 Jun 19 nicklas 521           trimmomaticStep = 0;
5486 12 Jun 19 nicklas 522           if (currentLib == null)
5486 12 Jun 19 nicklas 523           {
5486 12 Jun 19 nicklas 524             logger.error("At line " + lineNo + ": Found trimmomatic section for lib '" + libName + "' but not demultiplex metrics");
5486 12 Jun 19 nicklas 525           }
5486 12 Jun 19 nicklas 526           continue;
5486 12 Jun 19 nicklas 527         }
5486 12 Jun 19 nicklas 528         m = dataPattern.matcher(line);
5486 12 Jun 19 nicklas 529         if (m.matches())
5486 12 Jun 19 nicklas 530         {
5486 12 Jun 19 nicklas 531           if (currentLib == null)
5486 12 Jun 19 nicklas 532           {
5486 12 Jun 19 nicklas 533             logger.error("At line " + lineNo + ": Found trimmomatic data but has not found a library name");
5486 12 Jun 19 nicklas 534           }
5486 12 Jun 19 nicklas 535           else
5486 12 Jun 19 nicklas 536           {
5486 12 Jun 19 nicklas 537             currentLib.passedTrimmomatic[trimmomaticStep] = Values.getLong(m.group(1), -1);
5486 12 Jun 19 nicklas 538             trimmomaticStep++;
5486 12 Jun 19 nicklas 539             if (logger.isDebugEnabled())
5486 12 Jun 19 nicklas 540             {
5486 12 Jun 19 nicklas 541               logger.debug("Trimmomatic: " + currentLib.libName + "; " + currentLib.passedTrimmomatic);
5486 12 Jun 19 nicklas 542             }
5486 12 Jun 19 nicklas 543             if (trimmomaticStep == 2)
5486 12 Jun 19 nicklas 544             {
5486 12 Jun 19 nicklas 545               // Prepare for next sample
5486 12 Jun 19 nicklas 546               trimmomaticStep = 0;
5486 12 Jun 19 nicklas 547               currentLib = null;
5486 12 Jun 19 nicklas 548             }
5486 12 Jun 19 nicklas 549           }
5486 12 Jun 19 nicklas 550         }
5486 12 Jun 19 nicklas 551       }
5486 12 Jun 19 nicklas 552       
5486 12 Jun 19 nicklas 553       // Parse fragments.out file
5486 12 Jun 19 nicklas 554       dataPattern = Pattern.compile("(\\d+)\\t(\\d+\\.?\\d*)\\t(\\d+\\.?\\d*)");
5486 12 Jun 19 nicklas 555       currentLib = null;
5486 12 Jun 19 nicklas 556       lineNo = 0;
5486 12 Jun 19 nicklas 557       for (String line : align.split("\n"))
5486 12 Jun 19 nicklas 558       {
5486 12 Jun 19 nicklas 559         lineNo++;
5486 12 Jun 19 nicklas 560         Matcher m = sectionPattern.matcher(line);
5486 12 Jun 19 nicklas 561         if (m.matches())
5486 12 Jun 19 nicklas 562         {
5486 12 Jun 19 nicklas 563           String libName = m.group(1);
5486 12 Jun 19 nicklas 564           currentLib = sumReads.get(libName);
5486 12 Jun 19 nicklas 565           if (currentLib == null)
5486 12 Jun 19 nicklas 566           {
5486 12 Jun 19 nicklas 567             logger.error("At line " + lineNo + ": Found align section for lib '" + libName + "' but not demultiplex metrics");
5486 12 Jun 19 nicklas 568           }
5486 12 Jun 19 nicklas 569           continue;
5486 12 Jun 19 nicklas 570         }
5486 12 Jun 19 nicklas 571         m = dataPattern.matcher(line);
5486 12 Jun 19 nicklas 572         if (m.matches())
5486 12 Jun 19 nicklas 573         {
5486 12 Jun 19 nicklas 574           if (currentLib == null)
5486 12 Jun 19 nicklas 575           {
5486 12 Jun 19 nicklas 576             logger.error("At line " + lineNo + ": Found align data but has not found a library name");
5486 12 Jun 19 nicklas 577           }
5486 12 Jun 19 nicklas 578           else
5486 12 Jun 19 nicklas 579           {
5486 12 Jun 19 nicklas 580             currentLib.fragmentSizeCount = Values.getInt(m.group(1), -1);
5486 12 Jun 19 nicklas 581             currentLib.fragmentSizeAvg = Values.getInt(m.group(2), -1);
5486 12 Jun 19 nicklas 582             currentLib.fragmentSizeStd = Values.getInt(m.group(3), -1);
5486 12 Jun 19 nicklas 583             if (logger.isDebugEnabled())
5486 12 Jun 19 nicklas 584             {
5486 12 Jun 19 nicklas 585               logger.debug("Align: " + currentLib.libName + "; " + currentLib.fragmentSizeCount + "; " + currentLib.fragmentSizeAvg + "; " + currentLib.fragmentSizeStd);
5486 12 Jun 19 nicklas 586             }
5486 12 Jun 19 nicklas 587             currentLib = null;
5486 12 Jun 19 nicklas 588           }
5486 12 Jun 19 nicklas 589         }
5486 12 Jun 19 nicklas 590       }
5486 12 Jun 19 nicklas 591       
6421 23 Sep 21 nicklas 592       // Parse readlength.out file
6421 23 Sep 21 nicklas 593       dataPattern = Pattern.compile("(\\d+)\\t(\\d+\\.?\\d*)");
6421 23 Sep 21 nicklas 594       currentLib = null;
6421 23 Sep 21 nicklas 595       lineNo = 0;
6421 23 Sep 21 nicklas 596       int readNo = -1;
6421 23 Sep 21 nicklas 597       for (String line : readlength.split("\n"))
6421 23 Sep 21 nicklas 598       {
6421 23 Sep 21 nicklas 599         lineNo++;
6421 23 Sep 21 nicklas 600         Matcher m = sectionPattern.matcher(line);
6421 23 Sep 21 nicklas 601         if (m.matches())
6421 23 Sep 21 nicklas 602         {
6421 23 Sep 21 nicklas 603           String libName = m.group(1);
6421 23 Sep 21 nicklas 604           currentLib = sumReads.get(libName);
6436 07 Oct 21 nicklas 605           readNo = -1; // Reset READ counter
6421 23 Sep 21 nicklas 606           if (currentLib == null)
6421 23 Sep 21 nicklas 607           {
6421 23 Sep 21 nicklas 608             logger.error("At line " + lineNo + ": Found readlength section for lib '" + libName + "' but not demultiplex metrics");
6421 23 Sep 21 nicklas 609           }
6421 23 Sep 21 nicklas 610           continue;
6421 23 Sep 21 nicklas 611         }
6421 23 Sep 21 nicklas 612         m = dataPattern.matcher(line);
6421 23 Sep 21 nicklas 613         if (m.matches())
6421 23 Sep 21 nicklas 614         {
6421 23 Sep 21 nicklas 615           readNo++;
6421 23 Sep 21 nicklas 616           if (currentLib == null)
6421 23 Sep 21 nicklas 617           {
6421 23 Sep 21 nicklas 618             logger.error("At line " + lineNo + ": Found readlength data but has not found a library name");
6421 23 Sep 21 nicklas 619           }
6421 23 Sep 21 nicklas 620           else if (readNo >= currentLib.readlengthCount.length)
6421 23 Sep 21 nicklas 621           {
6421 23 Sep 21 nicklas 622             logger.error("At line " + lineNo + ": Found readlength data #"+(readNo+1)+" for library " + currentLib.libName);
6421 23 Sep 21 nicklas 623           }
6421 23 Sep 21 nicklas 624           else
6421 23 Sep 21 nicklas 625           {
6436 07 Oct 21 nicklas 626             currentLib.readlengthCount[readNo] = Values.getLong(m.group(1), null);
6436 07 Oct 21 nicklas 627             currentLib.readlengthAvg[readNo] = Values.getInteger(m.group(2), null);
6421 23 Sep 21 nicklas 628             if (logger.isDebugEnabled())
6421 23 Sep 21 nicklas 629             {
6421 23 Sep 21 nicklas 630               logger.debug("Readlength: " + currentLib.libName + "[R" + (readNo+1)+ "]; " + currentLib.readlengthCount[readNo] + "; " + currentLib.readlengthAvg[readNo]);
6421 23 Sep 21 nicklas 631             }
6421 23 Sep 21 nicklas 632           }
6421 23 Sep 21 nicklas 633         }
6421 23 Sep 21 nicklas 634       }
6421 23 Sep 21 nicklas 635
6421 23 Sep 21 nicklas 636       
5486 12 Jun 19 nicklas 637       // Parse the files.out file
5486 12 Jun 19 nicklas 638       currentLib = null;
5486 12 Jun 19 nicklas 639       lineNo = 0;
5486 12 Jun 19 nicklas 640       for (String line : files.split("\n"))
5486 12 Jun 19 nicklas 641       {
5486 12 Jun 19 nicklas 642         lineNo++;
5486 12 Jun 19 nicklas 643         Matcher m = sectionPattern.matcher(line);
5486 12 Jun 19 nicklas 644         if (m.matches())
5486 12 Jun 19 nicklas 645         {
5486 12 Jun 19 nicklas 646           String libName = m.group(1);
5486 12 Jun 19 nicklas 647           currentLib = sumReads.get(libName);
5486 12 Jun 19 nicklas 648           if (currentLib == null)
5486 12 Jun 19 nicklas 649           {
5486 12 Jun 19 nicklas 650             logger.error("At line " + lineNo + ": Found files section for lib '" + libName + "' but not demultiplex metrics");
5486 12 Jun 19 nicklas 651           }
5486 12 Jun 19 nicklas 652           continue;
5486 12 Jun 19 nicklas 653         }
5486 12 Jun 19 nicklas 654         else
5486 12 Jun 19 nicklas 655         {
5486 12 Jun 19 nicklas 656           if (currentLib == null)
5486 12 Jun 19 nicklas 657           {
5486 12 Jun 19 nicklas 658             logger.error("At line " + lineNo + ": Found file data but has not found a library name");
5486 12 Jun 19 nicklas 659           }
5486 12 Jun 19 nicklas 660           else
5486 12 Jun 19 nicklas 661           {
5486 12 Jun 19 nicklas 662             currentLib.addFile(line);
5486 12 Jun 19 nicklas 663             if (logger.isDebugEnabled())
5486 12 Jun 19 nicklas 664             {
5486 12 Jun 19 nicklas 665               logger.debug("File: " + currentLib.libName + "; " + line);
5486 12 Jun 19 nicklas 666             }
5486 12 Jun 19 nicklas 667           }
5486 12 Jun 19 nicklas 668         }
5486 12 Jun 19 nicklas 669       }
5486 12 Jun 19 nicklas 670       
5486 12 Jun 19 nicklas 671       DbControl dc = null;
5486 12 Jun 19 nicklas 672       Reads total = new Reads(null);
5486 12 Jun 19 nicklas 673       if (logger.isDebugEnabled())
5486 12 Jun 19 nicklas 674       {
5486 12 Jun 19 nicklas 675         logger.debug("Got read information for " + sumReads.size() + " libraries");
5486 12 Jun 19 nicklas 676       }
5486 12 Jun 19 nicklas 677       try
5486 12 Jun 19 nicklas 678       {
6599 22 Feb 22 nicklas 679         dc = sc.newDbControl("Reggie: Demux completed handler");
5486 12 Jun 19 nicklas 680         // Save metric file to BASE
5486 12 Jun 19 nicklas 681         Directory metricsDir = Directory.getNew(dc, new Path(DEMULTIPLEX_METRICS_DIR, Path.Type.DIRECTORY));
5486 12 Jun 19 nicklas 682         
5486 12 Jun 19 nicklas 683         for (String demuxName : demuxNames)
5486 12 Jun 19 nicklas 684         {
5486 12 Jun 19 nicklas 685           Reads demuxTotal = sumReads.remove(demuxName);
5486 12 Jun 19 nicklas 686           Reads demuxN = sumReads.remove(demuxName+".N");
5486 12 Jun 19 nicklas 687           Reads demuxUnused = sumReads.remove(demuxName+".U");
5486 12 Jun 19 nicklas 688           Reads demuxIgnore = sumReads.remove(demuxName+".I");
5486 12 Jun 19 nicklas 689           
5486 12 Jun 19 nicklas 690           DemuxedSequences demux = DemuxedSequences.getByName(dc, demuxName);
5486 12 Jun 19 nicklas 691           DerivedBioAssay dx = demux.getItem();
5486 12 Jun 19 nicklas 692           Annotationtype.READS.setAnnotationValue(dc, dx, demuxTotal.reads);
5486 12 Jun 19 nicklas 693           Annotationtype.PF_READS.setAnnotationValue(dc, dx, demuxTotal.passedFilter);
5486 12 Jun 19 nicklas 694           
5486 12 Jun 19 nicklas 695           Annotationtype.PF_NNNN_PCT.setAnnotationValue(dc, dx, demuxN == null ? 0 : 100f * demuxN.passedFilter / demuxTotal.passedFilter);
5486 12 Jun 19 nicklas 696           Annotationtype.PF_UNUSED_PCT.setAnnotationValue(dc, dx, demuxUnused == null ? 0 : 100f * demuxUnused.passedFilter / demuxTotal.passedFilter);
5486 12 Jun 19 nicklas 697           
5486 12 Jun 19 nicklas 698           if (demuxTotal.skippedTiles.size() > 0)
5486 12 Jun 19 nicklas 699           {
5486 12 Jun 19 nicklas 700             Annotationtype.SKIPPED_TILES.setAnnotationValues(dc, dx, new ArrayList<String>(demuxTotal.skippedTiles));
5486 12 Jun 19 nicklas 701             if (demuxTotal.skippedTiles.size() >= MAX_SKIPPED_TILES)
5486 12 Jun 19 nicklas 702             {
5486 12 Jun 19 nicklas 703               demuxTotal.addWarning(demuxTotal.skippedTiles.size() + " tiles skipped due to 0-size barcode file");
5486 12 Jun 19 nicklas 704             }
5486 12 Jun 19 nicklas 705           }
5486 12 Jun 19 nicklas 706           
5486 12 Jun 19 nicklas 707           if (demuxTotal.warnings.size() > 0)
5486 12 Jun 19 nicklas 708           {
5486 12 Jun 19 nicklas 709             Annotationtype.DEMUX_WARNINGS.setAnnotationValues(dc, dx, demuxTotal.warnings);
5486 12 Jun 19 nicklas 710             total.warnings.addAll(demuxTotal.warnings);
5486 12 Jun 19 nicklas 711           }
5486 12 Jun 19 nicklas 712           
5486 12 Jun 19 nicklas 713           File metricsFile = File.getFile(dc, metricsDir, demuxName+".csv", true);
5486 12 Jun 19 nicklas 714           metricsFile.setMimeType("text/plain");
5486 12 Jun 19 nicklas 715           metricsFile.setCharacterSet("UTF-8");
5486 12 Jun 19 nicklas 716           if (!metricsFile.isInDatabase())
5486 12 Jun 19 nicklas 717           {
5486 12 Jun 19 nicklas 718             dc.saveItem(metricsFile);
5486 12 Jun 19 nicklas 719           }
5486 12 Jun 19 nicklas 720           metricsFile.upload(new ByteArrayInputStream(metrics.getBytes(Charset.forName("UTF-8"))), false);
5486 12 Jun 19 nicklas 721           
5486 12 Jun 19 nicklas 722           AnyToAny dxMetrics = AnyToAny.getNew(dc, dx, metricsFile, "DemultiplexMetrics", true);
5486 12 Jun 19 nicklas 723           dc.saveItem(dxMetrics);
5486 12 Jun 19 nicklas 724         }
5486 12 Jun 19 nicklas 725         
5486 12 Jun 19 nicklas 726         DataFileType fastqData = Datafiletype.FASTQ.load(dc);
5486 12 Jun 19 nicklas 727         ItemSubtype fastqType = fastqData.getGenericType();
5486 12 Jun 19 nicklas 728         FileServer projectArchive = Fileserver.PROJECT_ARCHIVE.load(dc);
5486 12 Jun 19 nicklas 729         FileServer externalArchive = Fileserver.EXTERNAL_ARCHIVE.load(dc);
5486 12 Jun 19 nicklas 730         
5486 12 Jun 19 nicklas 731         for (Reads r : sumReads.values())
5486 12 Jun 19 nicklas 732         {
5486 12 Jun 19 nicklas 733           if (logger.isDebugEnabled())
5486 12 Jun 19 nicklas 734           {
5486 12 Jun 19 nicklas 735             logger.debug(r.libName + "; " + r.reads + "; " + r.passedFilter + "; " + r.passedTrimmomatic[1]);
5486 12 Jun 19 nicklas 736           }
5486 12 Jun 19 nicklas 737           
5486 12 Jun 19 nicklas 738           MergedSequences merged = MergedSequences.getByName(dc, r.libName);
5486 12 Jun 19 nicklas 739           if (merged != null)
5486 12 Jun 19 nicklas 740           {
5486 12 Jun 19 nicklas 741             DerivedBioAssay m = merged.getItem();
5486 12 Jun 19 nicklas 742             Software mergeSoftware = m.getSoftware();
5486 12 Jun 19 nicklas 743             String mergeParameterSet = (String)Annotationtype.PARAMETER_SET.getAnnotationValue(dc, mergeSoftware);
5486 12 Jun 19 nicklas 744             int bowtie_fragment_count_limit = Values.getInt(cfg.getConfig("demux/bowtie-fragment-count-limit", mergeParameterSet, "20000"));
5486 12 Jun 19 nicklas 745   
5486 12 Jun 19 nicklas 746             Annotationtype.READS.setAnnotationValue(dc, m, r.reads);
5486 12 Jun 19 nicklas 747             Annotationtype.PF_READS.setAnnotationValue(dc, m, r.passedFilter);
5486 12 Jun 19 nicklas 748             Annotationtype.ADAPTER_READS.setAnnotationValue(dc, m, r.passedFilter - r.passedTrimmomatic[0]);
5486 12 Jun 19 nicklas 749             Annotationtype.PT_READS.setAnnotationValue(dc, m, r.passedTrimmomatic[1]);
5486 12 Jun 19 nicklas 750             Annotationtype.FRAGMENT_SIZE_AVG.setAnnotationValue(dc, m, r.fragmentSizeCount < bowtie_fragment_count_limit ? -1 : r.fragmentSizeAvg);
5486 12 Jun 19 nicklas 751             Annotationtype.FRAGMENT_SIZE_STDEV.setAnnotationValue(dc, m, r.fragmentSizeCount < bowtie_fragment_count_limit ? -1 : r.fragmentSizeStd);
6436 07 Oct 21 nicklas 752             if (r.readlengthCount[0] != null && r.readlengthCount[0] >= bowtie_fragment_count_limit && r.readlengthAvg[0] != null)
6436 07 Oct 21 nicklas 753             {
6436 07 Oct 21 nicklas 754               Annotationtype.READLENGTH_AVG_R1.setAnnotationValue(dc, m, r.readlengthAvg[0]);
6436 07 Oct 21 nicklas 755             }
6436 07 Oct 21 nicklas 756             if (r.readlengthCount[1] != null && r.readlengthCount[1] >= bowtie_fragment_count_limit && r.readlengthAvg[1] != null)
6436 07 Oct 21 nicklas 757             {
6436 07 Oct 21 nicklas 758               Annotationtype.READLENGTH_AVG_R2.setAnnotationValue(dc, m, r.readlengthAvg[1]);
6436 07 Oct 21 nicklas 759             }
5486 12 Jun 19 nicklas 760             // Create FASTQ file links
5553 12 Aug 19 nicklas 761             boolean useExternalProjectArchive = Reggie.isExternalItem(merged.getName());
5486 12 Jun 19 nicklas 762             FileServer fileArchive = useExternalProjectArchive ? externalArchive : projectArchive;
5486 12 Jun 19 nicklas 763             String analysisDir = useExternalProjectArchive ? Reggie.EXTERNAL_ANALYSIS_DIR : Reggie.SECONDARY_ANALYSIS_DIR;
5486 12 Jun 19 nicklas 764             
5486 12 Jun 19 nicklas 765             String dataFilesFolder = (String)Annotationtype.DATA_FILES_FOLDER.getAnnotationValue(dc, m);
5486 12 Jun 19 nicklas 766             String baseFolder = Reggie.convertDataFilesFolderToBaseFolder(dataFilesFolder);
5486 12 Jun 19 nicklas 767             Directory localDataDir = Directory.getNew(dc, new Path(analysisDir+baseFolder, Path.Type.DIRECTORY));
5486 12 Jun 19 nicklas 768             for (String fileName : r.files)
5486 12 Jun 19 nicklas 769             {
5486 12 Jun 19 nicklas 770               File f = File.getFile(dc, localDataDir, fileName.substring(fileName.lastIndexOf("/")+1), true);
5486 12 Jun 19 nicklas 771               f.setItemSubtype(fastqType);
5486 12 Jun 19 nicklas 772               f.setFileServer(fileArchive);
5486 12 Jun 19 nicklas 773               f.setDescription(r.reads + " READS; " + r.passedFilter + " PF_READS; " + r.passedTrimmomatic[1] + " PT_READS");
6421 23 Sep 21 nicklas 774               if (fileName.contains(".fastq"))
6421 23 Sep 21 nicklas 775               {
6421 23 Sep 21 nicklas 776                 f.setDescription(f.getDescription()+"; ReadLengthAvg="+r.readlengthAvg[fileName.contains("R1.fastq")?0:1]);
6421 23 Sep 21 nicklas 777               }
5486 12 Jun 19 nicklas 778               String fileUrl = "sftp://" + fileArchive.getHost() + dataFilesFolder + "/" + f.getName();
5486 12 Jun 19 nicklas 779               try
5486 12 Jun 19 nicklas 780               {
5486 12 Jun 19 nicklas 781                 f.setUrl(fileUrl, true);
5486 12 Jun 19 nicklas 782               }
5486 12 Jun 19 nicklas 783               catch (RuntimeException ex)
5486 12 Jun 19 nicklas 784               {
5486 12 Jun 19 nicklas 785                 f.setUrl(fileUrl, false);
5486 12 Jun 19 nicklas 786               }
5486 12 Jun 19 nicklas 787               if (!f.isInDatabase())
5486 12 Jun 19 nicklas 788               {
5486 12 Jun 19 nicklas 789                 dc.saveItem(f);
5486 12 Jun 19 nicklas 790               }
5486 12 Jun 19 nicklas 791               FileSetMember member = m.getFileSet().addMember(f, fastqData);
5486 12 Jun 19 nicklas 792             }
5486 12 Jun 19 nicklas 793             
5486 12 Jun 19 nicklas 794             total.reads += r.reads;
5486 12 Jun 19 nicklas 795             total.passedFilter += r.passedFilter;
5486 12 Jun 19 nicklas 796             total.passedTrimmomatic[0] += r.passedTrimmomatic[0];
5486 12 Jun 19 nicklas 797             total.passedTrimmomatic[1] += r.passedTrimmomatic[1];
5486 12 Jun 19 nicklas 798           }
5486 12 Jun 19 nicklas 799         }
5486 12 Jun 19 nicklas 800         dc.commit();
5486 12 Jun 19 nicklas 801       }
5486 12 Jun 19 nicklas 802       finally
5486 12 Jun 19 nicklas 803       {
5486 12 Jun 19 nicklas 804         if (dc != null) dc.close();
5486 12 Jun 19 nicklas 805       }
5486 12 Jun 19 nicklas 806       
5486 12 Jun 19 nicklas 807       return total;
5486 12 Jun 19 nicklas 808     }
5486 12 Jun 19 nicklas 809   }
5486 12 Jun 19 nicklas 810   
5486 12 Jun 19 nicklas 811   
5486 12 Jun 19 nicklas 812   private static class Reads
5486 12 Jun 19 nicklas 813   {
5486 12 Jun 19 nicklas 814     final String libName;
5486 12 Jun 19 nicklas 815     final List<String> warnings;
5486 12 Jun 19 nicklas 816     final List<String> files;
5486 12 Jun 19 nicklas 817     final Set<String> skippedTiles;
5486 12 Jun 19 nicklas 818     long reads = 0;
5486 12 Jun 19 nicklas 819     long passedFilter = 0;
5486 12 Jun 19 nicklas 820     long[] passedTrimmomatic = new long[2];
5486 12 Jun 19 nicklas 821     int fragmentSizeAvg = -1;
5486 12 Jun 19 nicklas 822     int fragmentSizeStd = -1;
5486 12 Jun 19 nicklas 823     int fragmentSizeCount = -1;
6436 07 Oct 21 nicklas 824     Integer[] readlengthAvg = new Integer[2];
6436 07 Oct 21 nicklas 825     Long[] readlengthCount = new Long[2];
5486 12 Jun 19 nicklas 826     
5486 12 Jun 19 nicklas 827     Reads(String libName)
5486 12 Jun 19 nicklas 828     {
5486 12 Jun 19 nicklas 829       this.libName = libName;
5486 12 Jun 19 nicklas 830       this.warnings = new ArrayList<String>();
5486 12 Jun 19 nicklas 831       this.files = new ArrayList<String>();
5486 12 Jun 19 nicklas 832       this.skippedTiles = new TreeSet<String>();
5486 12 Jun 19 nicklas 833     }
5486 12 Jun 19 nicklas 834     
5486 12 Jun 19 nicklas 835     void add(long reads, long passedFilter)
5486 12 Jun 19 nicklas 836     {
5486 12 Jun 19 nicklas 837       this.reads += reads;
5486 12 Jun 19 nicklas 838       this.passedFilter += passedFilter;
5486 12 Jun 19 nicklas 839     }
5486 12 Jun 19 nicklas 840     
5486 12 Jun 19 nicklas 841     void addWarning(String warning)
5486 12 Jun 19 nicklas 842     {
5486 12 Jun 19 nicklas 843       this.warnings.add(warning);
5486 12 Jun 19 nicklas 844     }
5486 12 Jun 19 nicklas 845     
5486 12 Jun 19 nicklas 846     void addFile(String file)
5486 12 Jun 19 nicklas 847     {
5486 12 Jun 19 nicklas 848       this.files.add(file);
5486 12 Jun 19 nicklas 849     }
5486 12 Jun 19 nicklas 850     
5486 12 Jun 19 nicklas 851     void addSkippedTile(int lane, String tileNo)
5486 12 Jun 19 nicklas 852     {
5486 12 Jun 19 nicklas 853       skippedTiles.add(lane + ":" + tileNo);
5486 12 Jun 19 nicklas 854     }
5486 12 Jun 19 nicklas 855     
5486 12 Jun 19 nicklas 856     @Override
5486 12 Jun 19 nicklas 857     public String toString()
5486 12 Jun 19 nicklas 858     {
5486 12 Jun 19 nicklas 859       return libName+";"+reads+";"+passedFilter;
5486 12 Jun 19 nicklas 860     }
5486 12 Jun 19 nicklas 861   }
5486 12 Jun 19 nicklas 862
5486 12 Jun 19 nicklas 863 }