MultiSampleQScript.scala 6.15 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/**
 * Biopet is built on top of GATK Queue for building bioinformatic
 * pipelines. It is mainly intended to support LUMC SHARK cluster which is running
 * SGE. But other types of HPC that are supported by GATK Queue (such as PBS)
 * should also be able to execute Biopet tools and pipelines.
 *
 * Copyright 2014 Sequencing Analysis Support Core - Leiden University Medical Center
 *
 * Contact us at: sasc@lumc.nl
 *
 * A dual licensing mode is applied. The source code within this project that are
 * not part of GATK Queue is freely available for non-commercial use under an AGPL
 * license; For commercial users or users who do not want to follow the AGPL
 * license, please contact us to obtain a separate license.
 */
16
17
package nl.lumc.sasc.biopet.core

18
19
import java.io.File

Peter van 't Hof's avatar
Peter van 't Hof committed
20
import nl.lumc.sasc.biopet.core.summary.{ Summarizable, SummaryQScript }
Peter van 't Hof's avatar
Peter van 't Hof committed
21
import nl.lumc.sasc.biopet.utils.{ Logging, ConfigUtils }
22
import org.broadinstitute.gatk.utils.commandline.Argument
23

Peter van 't Hof's avatar
Peter van 't Hof committed
24
/** This trait creates a structured way of use multisample pipelines */
25
26
27
trait MultiSampleQScript extends SummaryQScript {
  qscript =>

28
  @Argument(doc = "Only Sample", shortName = "s", required = false, fullName = "sample")
29
  private[core] val onlySamples: List[String] = Nil
Peter van 't Hof's avatar
Peter van 't Hof committed
30

31
  require(globalConfig.map.contains("samples"), "No Samples found in config")
Peter van 't Hof's avatar
Peter van 't Hof committed
32

Peter van 't Hof's avatar
Peter van 't Hof committed
33
  /** Sample class with basic functions build in */
34
  abstract class AbstractSample(val sampleId: String) extends Summarizable {
Peter van 't Hof's avatar
Peter van 't Hof committed
35
    /** Overrules config of qscript with default sample */
Peter van 't Hof's avatar
Peter van 't Hof committed
36
    val config = new ConfigFunctions(defaultSample = sampleId)
Peter van 't Hof's avatar
Peter van 't Hof committed
37

Peter van 't Hof's avatar
Peter van 't Hof committed
38
    /** Library class with basic functions build in */
39
    abstract class AbstractLibrary(val libId: String) extends Summarizable {
Peter van 't Hof's avatar
Peter van 't Hof committed
40
      /** Overrules config of qscript with default sample and default library */
41
      val config = new ConfigFunctions(defaultSample = sampleId, defaultLibrary = libId)
42

Peter van 't Hof's avatar
Peter van 't Hof committed
43
      /** Name overules the one from qscript */
44
45
46
47
      def addSummarizable(summarizable: Summarizable, name: String): Unit = {
        qscript.addSummarizable(summarizable, name, Some(sampleId), Some(libId))
      }

Peter van 't Hof's avatar
Peter van 't Hof committed
48
      /** Adds the library jobs */
Peter van 't Hof's avatar
Peter van 't Hof committed
49
      final def addAndTrackJobs(): Unit = {
Peter van 't Hof's avatar
Peter van 't Hof committed
50
        if (nameRegex.findFirstIn(libId) == None)
Peter van 't Hof's avatar
Peter van 't Hof committed
51
          Logging.addError(s"Library '$libId' does not conform to '$nameRegex'")
52
        currentSample = Some(sampleId)
53
        currentLib = Some(libId)
Peter van 't Hof's avatar
Peter van 't Hof committed
54
        addJobs()
55
        qscript.addSummarizable(this, "pipeline", Some(sampleId), Some(libId))
Peter van 't Hof's avatar
Peter van 't Hof committed
56
        currentLib = None
57
58
59
        currentSample = None
      }

Peter van 't Hof's avatar
Peter van 't Hof committed
60
      /** Creates a library file with given suffix */
61
      def createFile(suffix: String): File = new File(libDir, sampleId + "-" + libId + suffix)
Peter van 't Hof's avatar
Peter van 't Hof committed
62
63

      /** Returns library directory */
Peter van 't Hof's avatar
Peter van 't Hof committed
64
      def libDir = new File(sampleDir, "lib_" + libId)
65

Peter van 't Hof's avatar
Peter van 't Hof committed
66
      /** Function that add library jobs */
Peter van 't Hof's avatar
Peter van 't Hof committed
67
      protected def addJobs()
68
    }
Peter van 't Hof's avatar
Peter van 't Hof committed
69

Peter van 't Hof's avatar
Peter van 't Hof committed
70
    /** Library type, need implementation in pipeline */
71
    type Library <: AbstractLibrary
Peter van 't Hof's avatar
Peter van 't Hof committed
72

Peter van 't Hof's avatar
Peter van 't Hof committed
73
74
    /** Stores all libraries */
    val libraries: Map[String, Library] = libIds.map(id => id -> makeLibrary(id)).toMap
75

Peter van 't Hof's avatar
Peter van 't Hof committed
76
77
78
79
80
    /**
     * Factory method for Library class
     * @param id SampleId
     * @return Sample class
     */
Peter van 't Hof's avatar
Peter van 't Hof committed
81
    def makeLibrary(id: String): Library
Peter van 't Hof's avatar
Peter van 't Hof committed
82

Peter van 't Hof's avatar
Peter van 't Hof committed
83
84
    /** returns a set with library names */
    protected def libIds: Set[String] = {
85
      ConfigUtils.getMapFromPath(globalConfig.map, List("samples", sampleId, "libraries")).getOrElse(Map()).keySet
86
    }
Peter van 't Hof's avatar
Peter van 't Hof committed
87

Peter van 't Hof's avatar
Peter van 't Hof committed
88
    /** Name overules the one from qscript */
89
90
91
92
    def addSummarizable(summarizable: Summarizable, name: String): Unit = {
      qscript.addSummarizable(summarizable, name, Some(sampleId))
    }

Peter van 't Hof's avatar
Peter van 't Hof committed
93
    /** Adds sample jobs */
Peter van 't Hof's avatar
Peter van 't Hof committed
94
    final def addAndTrackJobs(): Unit = {
Peter van 't Hof's avatar
Peter van 't Hof committed
95
      if (nameRegex.findFirstIn(sampleId) == None)
Peter van 't Hof's avatar
Peter van 't Hof committed
96
        Logging.addError(s"Sample '$sampleId' does not conform to '$nameRegex'")
97
      currentSample = Some(sampleId)
Peter van 't Hof's avatar
Peter van 't Hof committed
98
      addJobs()
99
      qscript.addSummarizable(this, "pipeline", Some(sampleId))
100
101
      currentSample = None
    }
Peter van 't Hof's avatar
Peter van 't Hof committed
102

Peter van 't Hof's avatar
Peter van 't Hof committed
103
    /** Function to add sample jobs */
Peter van 't Hof's avatar
Peter van 't Hof committed
104
    protected def addJobs()
Peter van 't Hof's avatar
Peter van 't Hof committed
105

Peter van 't Hof's avatar
Peter van 't Hof committed
106
    /** function add all libraries in one call */
107
    protected final def addPerLibJobs(): Unit = {
108
      for ((libId, library) <- libraries) {
Peter van 't Hof's avatar
Peter van 't Hof committed
109
        library.addAndTrackJobs()
110
      }
bow's avatar
bow committed
111
112
    }

113
    /** Creates a sample file with given suffix */
Peter van 't Hof's avatar
Peter van 't Hof committed
114
115
116
    def createFile(suffix: String) = new File(sampleDir, sampleId + suffix)

    /** Returns sample directory */
Peter van 't Hof's avatar
Peter van 't Hof committed
117
    def sampleDir = new File(outputDir, "samples" + File.separator + sampleId)
118
119
  }

Peter van 't Hof's avatar
Peter van 't Hof committed
120
  /** Sample type, need implementation in pipeline */
Peter van 't Hof's avatar
Peter van 't Hof committed
121
122
  type Sample <: AbstractSample

Peter van 't Hof's avatar
Peter van 't Hof committed
123
124
  /**
   * Factory method for Sample class
125
   * @param id SampleId
Peter van 't Hof's avatar
Peter van 't Hof committed
126
127
   * @return Sample class
   */
128
  def makeSample(id: String): Sample
Peter van 't Hof's avatar
Peter van 't Hof committed
129

Peter van 't Hof's avatar
Peter van 't Hof committed
130
131
  /** Stores all samples */
  val samples: Map[String, Sample] = sampleIds.map(id => id -> makeSample(id)).toMap
132

133
  /** Returns a list of all sampleIDs */
134
  protected def sampleIds: Set[String] = ConfigUtils.any2map(globalConfig.map("samples")).keySet
135

Peter van 't Hof's avatar
Peter van 't Hof committed
136
  protected lazy val nameRegex = """^[a-zA-Z][a-zA-Z0-9-_]+[a-zA-Z0-9]$""".r
Peter van 't Hof's avatar
Peter van 't Hof committed
137

Peter van 't Hof's avatar
Peter van 't Hof committed
138
  /** Runs addAndTrackJobs method for each sample */
Peter van 't Hof's avatar
Peter van 't Hof committed
139
  final def addSamplesJobs() {
140
    if (onlySamples.isEmpty || samples.forall(x => onlySamples.contains(x._1))) {
141
142
143
      samples.foreach { case (sampleId, sample) => sample.addAndTrackJobs() }
      addMultiSampleJobs()
    } else onlySamples.foreach(sampleId => samples.get(sampleId) match {
144
145
146
      case Some(sample) => sample.addAndTrackJobs()
      case None         => logger.warn("sampleId '" + sampleId + "' not found")
    })
147
148
  }

149
  /**
Peter van 't Hof's avatar
Peter van 't Hof committed
150
   * Method where the multisample jobs should be added, this will be executed only when running the -sample argument is not given.
151
152
153
   */
  def addMultiSampleJobs()

Peter van 't Hof's avatar
Peter van 't Hof committed
154
  /** Stores sample state */
155
156
  private var currentSample: Option[String] = None

Peter van 't Hof's avatar
Peter van 't Hof committed
157
158
159
160
  /** Stores library state */
  private var currentLib: Option[String] = None

  /** Prefix full path with sample and library for jobs that's are created in current state */
161
  override def configFullPath: List[String] = {
162
    val sample = currentSample match {
Peter van 't Hof's avatar
Peter van 't Hof committed
163
164
165
      case Some(s) => "samples" :: s :: Nil
      case _       => Nil
    }
166
    val lib = currentLib match {
Peter van 't Hof's avatar
Peter van 't Hof committed
167
168
169
      case Some(l) => "libraries" :: l :: Nil
      case _       => Nil
    }
170
    sample ::: lib ::: super.configFullPath
171
  }
172
}