TemplateTool.scala 5.15 KB
Newer Older
1 2
package nl.lumc.sasc.biopet.core

Peter van 't Hof's avatar
Peter van 't Hof committed
3
import java.io.{ File, PrintWriter }
4

5
import nl.lumc.sasc.biopet.utils.summary.Summary
Peter van 't Hof's avatar
Peter van 't Hof committed
6
import nl.lumc.sasc.biopet.utils.{ ConfigUtils, Question, ToolCommand }
Peter van 't Hof's avatar
Peter van 't Hof committed
7 8

import scala.io.Source
9 10

/**
Peter van 't Hof's avatar
Peter van 't Hof committed
11 12
 * Created by pjvanthof on 17/12/2016.
 */
13 14
trait TemplateTool extends ToolCommand {
  import TemplateTool._
Peter van 't Hof's avatar
Peter van 't Hof committed
15 16 17 18
  case class Args(outputConfig: File = null,
                  runScript: Option[File] = None,
                  expert: Boolean = false,
                  template: Option[File] = None) extends AbstractArgs
19 20

  class OptParser extends AbstractOptParser {
Peter van 't Hof's avatar
Peter van 't Hof committed
21 22 23 24 25 26 27 28 29 30
    opt[File]('o', "outputConfig") required () valueName "<file>" action { (x, c) =>
      c.copy(outputConfig = x)
    } text "Path to output config"
    opt[File]('s', "outputScript") valueName "<file>" action { (x, c) =>
      c.copy(runScript = Some(x))
    } text "Path to output script"
    opt[File]('t', "template") valueName "<file>" action { (x, c) =>
      c.copy(template = Some(x))
    } text "Path to template, default it try to fetch this from 'BIOPET_SCRIPT_TEMPLATE'"
    opt[Unit]("expert") action { (x, c) =>
31
      c.copy(expert = true)
Peter van 't Hof's avatar
Peter van 't Hof committed
32
    }
33 34 35
  }

  /**
Peter van 't Hof's avatar
Peter van 't Hof committed
36 37 38 39
   * Program will split fastq file in multiple fastq files
   *
   * @param args the command line arguments
   */
40 41 42 43
  def main(args: Array[String]): Unit = {
    val argsParser = new OptParser
    val cmdArgs: Args = argsParser.parse(args, Args()) getOrElse (throw new IllegalArgumentException)

Peter van 't Hof's avatar
Peter van 't Hof committed
44 45
    cmdArgs.runScript.foreach(writeScript(_, cmdArgs.outputConfig, sampleConfigs, cmdArgs.template))

46 47
    val standard: Map[String, Any] = Map("output_dir" -> Question.string("Output directory",
      validation = List(isAbsolutePath, parentIsWritable)))
48 49
    val config = pipelineMap(standard, cmdArgs.expert)

Peter van 't Hof's avatar
Peter van 't Hof committed
50 51 52
    val configWriter = new PrintWriter(cmdArgs.outputConfig)
    configWriter.println(ConfigUtils.mapToYaml(config))
    configWriter.close()
53 54
  }

Peter van 't Hof's avatar
Peter van 't Hof committed
55 56 57 58 59
  def writeScript(outputFile: File, config: File, samples: List[File], t: Option[File]): Unit = {
    val template = t match {
      case Some(f) => f
      case _ => sys.env.get("BIOPET_SCRIPT_TEMPLATE") match {
        case Some(file) => new File(file)
Peter van 't Hof's avatar
Peter van 't Hof committed
60
        case _          => throw new IllegalArgumentException("No template found on argument or 'BIOPET_SCRIPT_TEMPLATE'")
Peter van 't Hof's avatar
Peter van 't Hof committed
61 62 63 64 65 66 67 68 69 70 71 72 73 74 75
      }
    }

    val templateReader = Source.fromFile(template)
    val scriptWriter = new PrintWriter(outputFile)

    val biopetArgs: String = (config :: samples).map(_.getAbsolutePath).mkString("-config ", " \\\n-config ", "")
    templateReader.getLines().mkString("\n").format(pipelineName, biopetArgs).foreach(scriptWriter.print)
    templateReader.close()
    scriptWriter.close()
    outputFile.setExecutable(true, false)
  }

  def pipelineName: String

76 77
  def pipelineMap(map: Map[String, Any], expert: Boolean): Map[String, Any]

Peter van 't Hof's avatar
Peter van 't Hof committed
78
  def sampleConfigs: List[File] = Nil
79

80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109
}

object TemplateTool {
  def isAbsolutePath(value: String): Boolean = {
    if (new File(value).isAbsolute) true else {
      println(s"'$value' must be a absulute path")
      false
    }
  }

  def mustExist(value: String): Boolean = {
    if (new File(value).exists()) true else {
      println(s"'$value' does not exist")
      false
    }
  }

  def parentIsWritable(value: String): Boolean = {
    val parent = new File(value).getParentFile
    if (!parent.exists()) {
      println(s"$parent does not exist")
      false
    } else if (!parent.canRead) {
      println(s"No premision to read $parent")
      false
    } else if (!parent.canWrite) {
      println(s"No premision to write $parent")
      false
    } else true
  }
110 111 112 113 114 115 116 117 118 119 120 121 122 123 124

  def askSampleConfigs(currentList: List[File] = Nil): List[File] = {
    val configFile = new File(Question.string("Sample config file", validation = List(mustExist, isAbsolutePath)))
    val configMap = new Summary(configFile)
    println(s"${configMap.samples.size} samples found in config " +
      s"with in total ${configMap.libraries.map(_._2.size).sum} libraries for '$configFile'")
    if (Question.boolean("Is this correct?")) {
      if (Question.boolean("Add more sample configs?")) askSampleConfigs(configFile :: currentList)
      else {
        val files = configFile :: currentList
        if (files.size > 1) {
          val configs = files.map(ConfigUtils.fileToConfigMap(_))
          val sizes = configs.map(new Summary(_)).map(x => (x.samples.size, x.libraries.map(_._2.size).sum))
          val samples = sizes.map(_._1).sum
          val libs = sizes.map(_._2).sum
Peter van 't Hof's avatar
Peter van 't Hof committed
125
          val mergedConfig = new Summary(configs.foldLeft(Map[String, Any]())((a, b) => ConfigUtils.mergeMaps(a, b)))
126 127 128 129 130 131 132 133 134 135 136 137 138 139
          val mergesSamples = mergedConfig.samples.size
          val mergesLibraries = mergedConfig.libraries.map(_._2.size).sum
          if (mergesSamples != samples) println("WARNING: Overlapping samples detected")
          if (mergesLibraries != libs) println("WARNING: Overlapping libraries detected")
          println(s"$mergesSamples samples found in merged config with in total $mergesLibraries libraries")
          if (Question.boolean("Is this correct?")) files
          else {
            println("Resetting sample configs")
            askSampleConfigs()
          }
        } else files
      }
    } else askSampleConfigs(currentList)
  }
140
}