ReportBuilder.scala 6.01 KB
Newer Older
1
2
package nl.lumc.sasc.biopet.core.report

3
import java.io._
4
import java.net.URL
5

6
import nl.lumc.sasc.biopet.core.{ BiopetJavaCommandLineFunction, ToolCommand }
7
import nl.lumc.sasc.biopet.core.summary.Summary
8
import org.apache.commons.io.IOUtils
9
import org.broadinstitute.gatk.utils.commandline.Input
10
import org.fusesource.scalate.{ TemplateSource, TemplateEngine }
11
import scala.collection.JavaConversions._
12
13
14
15

/**
 * Created by pjvan_thof on 3/27/15.
 */
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
trait ReportBuilderExtension extends BiopetJavaCommandLineFunction {

  val builder: ReportBuilder

  @Input(required = true)
  var summaryFile: File = _

  var outputDir: File = _

  var args: Map[String, String] = Map()

  override def beforeGraph: Unit = {
    super.beforeGraph
    jobOutputFile = new File(outputDir, ".report.log.out")
    javaMainClass = builder.getClass.getName.takeWhile(_ != '$')
  }

  override def commandLine: String = {
    super.commandLine +
      required("--summary", summaryFile) +
      required("--outputDir", outputDir) +
      args.map(x => required(x._1, x._2)).mkString
  }
}

41
42
trait ReportBuilder extends ToolCommand {

43
  case class Args(summary: File = null, outputDir: File = null, pageArgs: Map[String, String] = Map()) extends AbstractArgs
44
45

  class OptParser extends AbstractOptParser {
46
    opt[File]('s', "summary") required () maxOccurs 1 valueName "<file>" action { (x, c) =>
47
48
      c.copy(summary = x)
    }
49
    opt[File]('o', "outputDir") required () maxOccurs 1 valueName "<file>" action { (x, c) =>
50
51
      c.copy(outputDir = x)
    }
52
53
54
    opt[Map[String, String]]('a', "args") action { (x, c) =>
      c.copy(pageArgs = c.pageArgs ++ x)
    }
55
56
57
58
59
60
61
62
  }

  private var setSummary: Summary = _

  final def summary = setSummary

  def pageArgs: Map[String, Any] = Map()

Peter van 't Hof's avatar
Peter van 't Hof committed
63
64
65
  private var done = 0
  private var total = 0

66
67
68
69
70
71
72
73
74
  def main(args: Array[String]): Unit = {
    logger.info("Start")

    val argsParser = new OptParser
    val cmdArgs: Args = argsParser.parse(args, Args()) getOrElse sys.exit(1)

    require(cmdArgs.outputDir.exists(), "Output dir does not exist")
    require(cmdArgs.outputDir.isDirectory, "Output dir is not a directory")

75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
    logger.info("Copy Base files")

    // Copying out the static files from this

    val extOutputDir: File = new File(cmdArgs.outputDir, "ext")
    val resourceDir: String = "/nl/lumc/sasc/biopet/core/report/ext/"
    val extFiles = List(
      "css/bootstrap_dashboard.css",
      "css/bootstrap.min.css",
      "css/bootstrap-theme.min.css",
      "css/sortable-theme-bootstrap.css",
      "js/jquery.min.js",
      "js/sortable.min.js",
      "js/bootstrap.min.js",
      "fonts/glyphicons-halflings-regular.woff",
      "fonts/glyphicons-halflings-regular.ttf",
      "fonts/glyphicons-halflings-regular.woff2"
    )

    for (resource <- extFiles.par) {
      ReportBuilder.copyStreamToFile(getClass.getResourceAsStream(resourceDir + resource), new File(extOutputDir, resource), true)
96
    }
97

Peter van 't Hof's avatar
Peter van 't Hof committed
98
    logger.info("Parsing summary")
99
100
    setSummary = new Summary(cmdArgs.summary)

Peter van 't Hof's avatar
Peter van 't Hof committed
101
102
103
    total = countPages(indexPage)
    logger.info(total + " pages to be generated")

Peter van 't Hof's avatar
Peter van 't Hof committed
104
    logger.info("Generate pages")
Peter van 't Hof's avatar
Peter van 't Hof committed
105
    val jobs = generatePage(summary, indexPage, cmdArgs.outputDir,
106
107
      args = pageArgs ++ cmdArgs.pageArgs ++
        Map("summary" -> summary, "reportName" -> reportName, "indexPage" -> indexPage))
108

Peter van 't Hof's avatar
Peter van 't Hof committed
109
    logger.info(jobs + " Done")
110
111
112
113
114
115
  }

  def indexPage: ReportPage

  def reportName: String

Peter van 't Hof's avatar
Peter van 't Hof committed
116
117
118
119
  def countPages(page: ReportPage): Int = {
    page.subPages.map(x => countPages(x._2)).fold(1)(_ + _)
  }

120
121
122
123
  def generatePage(summary: Summary,
                   page: ReportPage,
                   outputDir: File,
                   path: List[String] = Nil,
Peter van 't Hof's avatar
Peter van 't Hof committed
124
                   args: Map[String, Any] = Map()): Int = {
125

Peter van 't Hof's avatar
Peter van 't Hof committed
126
127
    val pageOutputDir = new File(outputDir, path.mkString(File.separator))
    pageOutputDir.mkdirs()
128
129
130
131
132
133
134
    val rootPath = "./" + Array.fill(path.size)("../").mkString("")
    val pageArgs = args ++ page.args ++
      Map("page" -> page,
        "path" -> path,
        "outputDir" -> pageOutputDir,
        "rootPath" -> rootPath
      )
135

Peter van 't Hof's avatar
Peter van 't Hof committed
136
137
138
139
    val output = ReportBuilder.renderTemplate("/nl/lumc/sasc/biopet/core/report/main.ssp",
      pageArgs ++ Map("args" -> pageArgs))

    val file = new File(pageOutputDir, "index.html")
140
141
142
143
144
    val writer = new PrintWriter(file)
    writer.println(output)
    writer.close()

    // Generating subpages
Peter van 't Hof's avatar
Peter van 't Hof committed
145
    val jobs = for ((name, subPage) <- page.subPages.par) yield {
146
147
      generatePage(summary, subPage, outputDir, path ::: name :: Nil, pageArgs)
    }
Peter van 't Hof's avatar
Peter van 't Hof committed
148
149
    done += 1
    if (done % 100 == 0) logger.info(done + " Done, " + (done.toDouble / total * 100) + "%")
Peter van 't Hof's avatar
Peter van 't Hof committed
150
    jobs.fold(0)(_ + _) + 1
151
152
  }
}
Peter van 't Hof's avatar
Peter van 't Hof committed
153
154
155
156
157

object ReportBuilder {

  protected val engine = new TemplateEngine()

158
159
  private var templateCache: Map[String, File] = Map()

160
  def renderTemplate(location: String, args: Map[String, Any]): String = {
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
    val templateFile: File = templateCache.get(location) match {
      case Some(template) => template
      case _ => {
        val tempFile = File.createTempFile("ssp-template", new File(location).getName)
        copyStreamToFile(getClass.getResourceAsStream(location), tempFile)
        templateCache += location -> tempFile
        tempFile
      }
    }
    engine.layout(TemplateSource.fromFile(templateFile), args)
  }

  //TODO: move to utils
  def copyFile(in: File, out: File, createDirs: Boolean = false): Unit = {
    copyStreamToFile(new FileInputStream(in), out, createDirs)
  }

  def copyStreamToFile(in: InputStream, out: File, createDirs: Boolean = false): Unit = {
    if (createDirs) out.getParentFile.mkdirs()
    val os = new FileOutputStream(out)

    org.apache.commons.io.IOUtils.copy(in, os)
    os.close()
    in.close()
  }

  def copyDir(inputDir: File, externalDir: File): Unit = {
    require(inputDir.isDirectory)
    externalDir.mkdirs()
    for (srcFile <- inputDir.listFiles) {
      if (srcFile.isDirectory) copyDir(new File(inputDir, srcFile.getName), new File(externalDir, srcFile.getName))
      else {
        val newFile = new File(externalDir, srcFile.getName)
        copyFile(srcFile, newFile)
      }
    }
Peter van 't Hof's avatar
Peter van 't Hof committed
197
198
  }
}