Commit f8f8af38 authored by Peter van 't Hof's avatar Peter van 't Hof
Browse files

Start to convert report class

parent e371afc7
...@@ -63,7 +63,7 @@ class BamMetrics(val parent: Configurable) extends QScript ...@@ -63,7 +63,7 @@ class BamMetrics(val parent: Configurable) extends QScript
override def reportClass = { override def reportClass = {
val bammetricsReport = new BammetricsReport(this) val bammetricsReport = new BammetricsReport(this)
bammetricsReport.outputDir = new File(outputDir, "report") bammetricsReport.outputDir = new File(outputDir, "report")
bammetricsReport.summaryFile = summaryFile bammetricsReport.summaryDbFile = summaryDbFile
bammetricsReport.args = if (libId.isDefined) Map( bammetricsReport.args = if (libId.isDefined) Map(
"sampleId" -> sampleId.getOrElse("."), "sampleId" -> sampleId.getOrElse("."),
"libId" -> libId.getOrElse(".")) "libId" -> libId.getOrElse("."))
......
...@@ -14,6 +14,9 @@ ...@@ -14,6 +14,9 @@
*/ */
package nl.lumc.sasc.biopet.core.report package nl.lumc.sasc.biopet.core.report
import scala.concurrent.Await
import scala.concurrent.duration.Duration
/** /**
* This trait will generate a report with added function to generate sample and library pages for those existing in the summary. * This trait will generate a report with added function to generate sample and library pages for those existing in the summary.
* *
...@@ -22,7 +25,7 @@ package nl.lumc.sasc.biopet.core.report ...@@ -22,7 +25,7 @@ package nl.lumc.sasc.biopet.core.report
trait MultisampleReportBuilder extends ReportBuilder { trait MultisampleReportBuilder extends ReportBuilder {
/** Method to generate a single sample page */ /** Method to generate a single sample page */
def samplePage(sampleId: String, args: Map[String, Any]): ReportPage def samplePage(sampleId: Int, args: Map[String, Any]): ReportPage
/** Default list of samples, can be override */ /** Default list of samples, can be override */
def samplesSections: List[(String, ReportSection)] = { def samplesSections: List[(String, ReportSection)] = {
...@@ -32,7 +35,7 @@ trait MultisampleReportBuilder extends ReportBuilder { ...@@ -32,7 +35,7 @@ trait MultisampleReportBuilder extends ReportBuilder {
} }
/** Method to generate a single library page */ /** Method to generate a single library page */
def libraryPage(sampleId: String, libraryId: String, args: Map[String, Any]): ReportPage def libraryPage(sampleId: Int, libraryId: Int, args: Map[String, Any]): ReportPage
/** Default list of libraries, can be override */ /** Default list of libraries, can be override */
def librariesSections: List[(String, ReportSection)] = { def librariesSections: List[(String, ReportSection)] = {
...@@ -43,22 +46,25 @@ trait MultisampleReportBuilder extends ReportBuilder { ...@@ -43,22 +46,25 @@ trait MultisampleReportBuilder extends ReportBuilder {
/** Generate the samples page including a single sample page for each sample in the summary */ /** Generate the samples page including a single sample page for each sample in the summary */
def generateSamplesPage(args: Map[String, Any]): ReportPage = { def generateSamplesPage(args: Map[String, Any]): ReportPage = {
val samplePages = summary.samples val samples = Await.result(summary.getSamples(runId = Some(runId)), Duration.Inf)
val samplePages = samples.map(_.id)
.map(sampleId => sampleId -> samplePage(sampleId, args ++ Map("sampleId" -> Some(sampleId)))) .map(sampleId => sampleId -> samplePage(sampleId, args ++ Map("sampleId" -> Some(sampleId))))
.toList .toList
ReportPage(samplePages, samplesSections, args) ReportPage(samplePages.map(x => samples.find(_.name == x._1).get.name -> x._2), samplesSections, args)
} }
/** Generate the libraries page for a single sample with a subpage for eacht library */ /** Generate the libraries page for a single sample with a subpage for eacht library */
def generateLibraryPage(args: Map[String, Any]): ReportPage = { def generateLibraryPage(args: Map[String, Any]): ReportPage = {
val sampleId = args("sampleId") match { val sampleId = args("sampleId") match {
case Some(x) => x.toString case Some(x: Int) => x
case None => throw new IllegalStateException("Sample not found") case None => throw new IllegalStateException("Sample not found")
} }
val libPages = summary.libraries(sampleId) val libraries = Await.result(summary.getLibraries(runId = Some(runId), sampleId = Some(sampleId)), Duration.Inf)
val libPages = libraries.map(_.id)
.map(libId => libId -> libraryPage(sampleId, libId, args ++ Map("libId" -> Some(libId)))) .map(libId => libId -> libraryPage(sampleId, libId, args ++ Map("libId" -> Some(libId))))
.toList .toList
ReportPage(libPages, librariesSections, args) ReportPage(libPages.map(x => libraries.find(_.name == x._1).get.name -> x._2), librariesSections, args)
} }
} }
...@@ -17,10 +17,10 @@ package nl.lumc.sasc.biopet.core.report ...@@ -17,10 +17,10 @@ package nl.lumc.sasc.biopet.core.report
import java.io._ import java.io._
import nl.lumc.sasc.biopet.core.ToolCommandFunction import nl.lumc.sasc.biopet.core.ToolCommandFunction
import nl.lumc.sasc.biopet.utils.summary.Summary import nl.lumc.sasc.biopet.utils.summary.db.SummaryDb
import nl.lumc.sasc.biopet.utils.{ IoUtils, Logging, ToolCommand } import nl.lumc.sasc.biopet.utils.{IoUtils, Logging, ToolCommand}
import org.broadinstitute.gatk.utils.commandline.Input import org.broadinstitute.gatk.utils.commandline.Input
import org.fusesource.scalate.{ TemplateEngine, TemplateSource } import org.fusesource.scalate.{TemplateEngine, TemplateSource}
import scala.collection.mutable import scala.collection.mutable
import scala.language.postfixOps import scala.language.postfixOps
...@@ -38,7 +38,9 @@ trait ReportBuilderExtension extends ToolCommandFunction { ...@@ -38,7 +38,9 @@ trait ReportBuilderExtension extends ToolCommandFunction {
def toolObject = builder def toolObject = builder
@Input(required = true) @Input(required = true)
var summaryFile: File = _ var summaryDbFile: File = _
var runId: Option[Int] = None
/** OutputDir for the report */ /** OutputDir for the report */
var outputDir: File = _ var outputDir: File = _
...@@ -58,7 +60,8 @@ trait ReportBuilderExtension extends ToolCommandFunction { ...@@ -58,7 +60,8 @@ trait ReportBuilderExtension extends ToolCommandFunction {
/** Command to generate the report */ /** Command to generate the report */
override def cmdLine: String = { override def cmdLine: String = {
super.cmdLine + super.cmdLine +
required("--summary", summaryFile) + required("--summaryDb", summaryDbFile) +
optional("--runId", runId) +
required("--outputDir", outputDir) + required("--outputDir", outputDir) +
args.map(x => required("-a", x._1 + "=" + x._2)).mkString args.map(x => required("-a", x._1 + "=" + x._2)).mkString
} }
...@@ -66,8 +69,9 @@ trait ReportBuilderExtension extends ToolCommandFunction { ...@@ -66,8 +69,9 @@ trait ReportBuilderExtension extends ToolCommandFunction {
trait ReportBuilder extends ToolCommand { trait ReportBuilder extends ToolCommand {
case class Args(summary: File = null, case class Args(summaryDbFile: File = null,
outputDir: File = null, outputDir: File = null,
runId: Int = 0,
pageArgs: mutable.Map[String, Any] = mutable.Map()) extends AbstractArgs pageArgs: mutable.Map[String, Any] = mutable.Map()) extends AbstractArgs
class OptParser extends AbstractOptParser { class OptParser extends AbstractOptParser {
...@@ -78,8 +82,8 @@ trait ReportBuilder extends ToolCommand { ...@@ -78,8 +82,8 @@ trait ReportBuilder extends ToolCommand {
""".stripMargin """.stripMargin
) )
opt[File]('s', "summary") unbounded () required () maxOccurs 1 valueName "<file>" action { (x, c) => opt[File]('s', "summaryDb") unbounded () required () maxOccurs 1 valueName "<file>" action { (x, c) =>
c.copy(summary = x) c.copy(summaryDbFile = x)
} validate { } validate {
x => if (x.exists) success else failure("Summary JSON file not found!") x => if (x.exists) success else failure("Summary JSON file not found!")
} text "Biopet summary JSON file" } text "Biopet summary JSON file"
...@@ -88,17 +92,27 @@ trait ReportBuilder extends ToolCommand { ...@@ -88,17 +92,27 @@ trait ReportBuilder extends ToolCommand {
c.copy(outputDir = x) c.copy(outputDir = x)
} text "Output HTML report files to this directory" } text "Output HTML report files to this directory"
opt[Int]("runId") unbounded () maxOccurs 1 valueName "<int>" action { (x, c) =>
c.copy(runId = x)
}
opt[Map[String, String]]('a', "args") unbounded () action { (x, c) => opt[Map[String, String]]('a', "args") unbounded () action { (x, c) =>
c.copy(pageArgs = c.pageArgs ++ x) c.copy(pageArgs = c.pageArgs ++ x)
} }
} }
/** summary object internaly */ /** summary object internaly */
private var setSummary: Summary = _ private var setSummary: SummaryDb = _
/** Retrival of summary, read only */ /** Retrival of summary, read only */
final def summary = setSummary final def summary = setSummary
/** summary object internaly */
private var setRunId: Int = 0
/** Retrival of summary, read only */
final def runId = setRunId
/** default args that are passed to all page withing the report */ /** default args that are passed to all page withing the report */
def pageArgs: Map[String, Any] = Map() def pageArgs: Map[String, Any] = Map()
...@@ -165,7 +179,8 @@ trait ReportBuilder extends ToolCommand { ...@@ -165,7 +179,8 @@ trait ReportBuilder extends ToolCommand {
) )
logger.info("Parsing summary") logger.info("Parsing summary")
setSummary = new Summary(cmdArgs.summary) setSummary = SummaryDb.openSqliteSummary(cmdArgs.summaryDbFile)
setRunId = cmdArgs.runId
total = ReportBuilder.countPages(indexPage) total = ReportBuilder.countPages(indexPage)
logger.info(total + " pages to be generated") logger.info(total + " pages to be generated")
...@@ -175,7 +190,7 @@ trait ReportBuilder extends ToolCommand { ...@@ -175,7 +190,7 @@ trait ReportBuilder extends ToolCommand {
logger.info("Generate pages") logger.info("Generate pages")
val jobs = generatePage(summary, indexPage, cmdArgs.outputDir, val jobs = generatePage(summary, indexPage, cmdArgs.outputDir,
args = pageArgs ++ cmdArgs.pageArgs.toMap ++ args = pageArgs ++ cmdArgs.pageArgs.toMap ++
Map("summary" -> summary, "reportName" -> reportName, "indexPage" -> indexPage)) Map("summary" -> summary, "reportName" -> reportName, "indexPage" -> indexPage, "runId" -> cmdArgs.runId))
logger.info(jobs + " Done") logger.info(jobs + " Done")
} }
...@@ -196,7 +211,7 @@ trait ReportBuilder extends ToolCommand { ...@@ -196,7 +211,7 @@ trait ReportBuilder extends ToolCommand {
* @param args Args to add to this sub page, are args from current page are passed automaticly * @param args Args to add to this sub page, are args from current page are passed automaticly
* @return Number of pages including all subpages that are rendered * @return Number of pages including all subpages that are rendered
*/ */
def generatePage(summary: Summary, def generatePage(summary: SummaryDb,
page: ReportPage, page: ReportPage,
outputDir: File, outputDir: File,
path: List[String] = Nil, path: List[String] = Nil,
......
...@@ -36,10 +36,10 @@ class MultisampleReportBuilderTest extends TestNGSuite with Matchers { ...@@ -36,10 +36,10 @@ class MultisampleReportBuilderTest extends TestNGSuite with Matchers {
def reportName: String = "test" def reportName: String = "test"
def indexPage: ReportPage = ReportPage("Samples" -> generateSamplesPage(Map()) :: Nil, Nil, Map()) def indexPage: ReportPage = ReportPage("Samples" -> generateSamplesPage(Map()) :: Nil, Nil, Map())
def samplePage(sampleId: String, args: Map[String, Any]): ReportPage = def samplePage(sampleId: Int, args: Map[String, Any]): ReportPage =
ReportPage("Libraries" -> generateLibraryPage(Map("sampleId" -> Some(sampleId))) :: Nil, Nil, Map()) ReportPage("Libraries" -> generateLibraryPage(Map("sampleId" -> Some(sampleId))) :: Nil, Nil, Map())
def libraryPage(sampleId: String, libraryId: String, args: Map[String, Any]) = ReportPage(Nil, Nil, Map()) def libraryPage(sampleId: Int, libraryId: Int, args: Map[String, Any]) = ReportPage(Nil, Nil, Map())
} }
val tempDir = Files.createTempDir() val tempDir = Files.createTempDir()
......
...@@ -16,7 +16,7 @@ package nl.lumc.sasc.biopet.utils.config ...@@ -16,7 +16,7 @@ package nl.lumc.sasc.biopet.utils.config
import java.io.{ File, PrintWriter } import java.io.{ File, PrintWriter }
import nl.lumc.sasc.biopet.utils.{ConfigUtils, Logging} import nl.lumc.sasc.biopet.utils.{ ConfigUtils, Logging }
import nl.lumc.sasc.biopet.utils.ConfigUtils._ import nl.lumc.sasc.biopet.utils.ConfigUtils._
import scala.concurrent.Future import scala.concurrent.Future
......
...@@ -15,7 +15,7 @@ import java.sql.Date ...@@ -15,7 +15,7 @@ import java.sql.Date
* *
* Created by pjvanthof on 05/02/2017. * Created by pjvanthof on 05/02/2017.
*/ */
class SummaryDb(db: Database) extends Closeable { class SummaryDb(val db: Database) extends Closeable {
def close(): Unit = db.close() def close(): Unit = db.close()
...@@ -75,6 +75,11 @@ class SummaryDb(db: Database) extends Closeable { ...@@ -75,6 +75,11 @@ class SummaryDb(db: Database) extends Closeable {
getSamples(runId = Some(runId), name = Some(sampleName)).map(_.headOption.map(_.id)) getSamples(runId = Some(runId), name = Some(sampleName)).map(_.headOption.map(_.id))
} }
/** Return samplId of a specific runId + sampleName */
def getSampleName(sampleId: Int): Future[Option[Int]] = {
getSamples(sampleId = Some(sampleId)).map(_.headOption.map(_.id))
}
/** Return sample tags of a specific sample as a map */ /** Return sample tags of a specific sample as a map */
def getSampleTags(sampleId: Int): Future[Option[Map[String, Any]]] = { def getSampleTags(sampleId: Int): Future[Option[Map[String, Any]]] = {
db.run(samples.filter(_.id === sampleId).map(_.tags).result) db.run(samples.filter(_.id === sampleId).map(_.tags).result)
...@@ -184,7 +189,7 @@ class SummaryDb(db: Database) extends Closeable { ...@@ -184,7 +189,7 @@ class SummaryDb(db: Database) extends Closeable {
} }
/** Return a Query for [[Stats]] */ /** Return a Query for [[Stats]] */
private def statsFilter(runId: Option[Int] = None, pipelineId: Option[Int] = None, moduleId: Option[Option[Int]] = None, def statsFilter(runId: Option[Int] = None, pipelineId: Option[Int] = None, moduleId: Option[Option[Int]] = None,
sampleId: Option[Option[Int]] = None, libId: Option[Option[Int]] = None) = { sampleId: Option[Option[Int]] = None, libId: Option[Option[Int]] = None) = {
var f: Query[Stats, Stats#TableElementType, Seq] = stats var f: Query[Stats, Stats#TableElementType, Seq] = stats
runId.foreach(r => f = f.filter(_.runId === r)) runId.foreach(r => f = f.filter(_.runId === r))
...@@ -215,7 +220,7 @@ class SummaryDb(db: Database) extends Closeable { ...@@ -215,7 +220,7 @@ class SummaryDb(db: Database) extends Closeable {
} }
/** This return a [[Query]] for [[Settings]] */ /** This return a [[Query]] for [[Settings]] */
private def settingsFilter(runId: Option[Int] = None, pipelineId: Option[Int] = None, moduleId: Option[Option[Int]] = None, def settingsFilter(runId: Option[Int] = None, pipelineId: Option[Int] = None, moduleId: Option[Option[Int]] = None,
sampleId: Option[Option[Int]] = None, libId: Option[Option[Int]] = None) = { sampleId: Option[Option[Int]] = None, libId: Option[Option[Int]] = None) = {
var f: Query[Settings, Settings#TableElementType, Seq] = settings var f: Query[Settings, Settings#TableElementType, Seq] = settings
runId.foreach(r => f = f.filter(_.runId === r)) runId.foreach(r => f = f.filter(_.runId === r))
...@@ -249,7 +254,7 @@ class SummaryDb(db: Database) extends Closeable { ...@@ -249,7 +254,7 @@ class SummaryDb(db: Database) extends Closeable {
} }
/** Return a [[Query]] for [[Files]] */ /** Return a [[Query]] for [[Files]] */
private def filesFilter(runId: Option[Int] = None, pipelineId: Option[Int] = None, moduleId: Option[Option[Int]], def filesFilter(runId: Option[Int] = None, pipelineId: Option[Int] = None, moduleId: Option[Option[Int]],
sampleId: Option[Option[Int]] = None, libId: Option[Option[Int]] = None, sampleId: Option[Option[Int]] = None, libId: Option[Option[Int]] = None,
key: Option[String] = None) = { key: Option[String] = None) = {
var f: Query[Files, Files#TableElementType, Seq] = files var f: Query[Files, Files#TableElementType, Seq] = files
...@@ -287,7 +292,7 @@ class SummaryDb(db: Database) extends Closeable { ...@@ -287,7 +292,7 @@ class SummaryDb(db: Database) extends Closeable {
} }
/** Returns a [[Query]] for [[Executables]] */ /** Returns a [[Query]] for [[Executables]] */
private def executablesFilter(runId: Option[Int], toolName: Option[String]) = { def executablesFilter(runId: Option[Int], toolName: Option[String]) = {
var q: Query[Executables, Executables#TableElementType, Seq] = executables var q: Query[Executables, Executables#TableElementType, Seq] = executables
runId.foreach(r => q = q.filter(_.runId === r)) runId.foreach(r => q = q.filter(_.runId === r))
toolName.foreach(r => q = q.filter(_.toolName === r)) toolName.foreach(r => q = q.filter(_.toolName === r))
......
...@@ -103,7 +103,7 @@ class Carp(val parent: Configurable) extends QScript with MultisampleMappingTrai ...@@ -103,7 +103,7 @@ class Carp(val parent: Configurable) extends QScript with MultisampleMappingTrai
override def reportClass: Option[ReportBuilderExtension] = { override def reportClass: Option[ReportBuilderExtension] = {
val carp = new CarpReport(this) val carp = new CarpReport(this)
carp.outputDir = new File(outputDir, "report") carp.outputDir = new File(outputDir, "report")
carp.summaryFile = summaryFile carp.summaryDbFile = summaryDbFile
Some(carp) Some(carp)
} }
......
...@@ -65,7 +65,7 @@ class Flexiprep(val parent: Configurable) extends QScript with SummaryQScript wi ...@@ -65,7 +65,7 @@ class Flexiprep(val parent: Configurable) extends QScript with SummaryQScript wi
override def reportClass: Some[FlexiprepReport] = { override def reportClass: Some[FlexiprepReport] = {
val flexiprepReport = new FlexiprepReport(this) val flexiprepReport = new FlexiprepReport(this)
flexiprepReport.outputDir = new File(outputDir, "report") flexiprepReport.outputDir = new File(outputDir, "report")
flexiprepReport.summaryFile = summaryFile flexiprepReport.summaryDbFile = summaryDbFile
flexiprepReport.args = Map( flexiprepReport.args = Map(
"sampleId" -> sampleId.getOrElse("."), "sampleId" -> sampleId.getOrElse("."),
"libId" -> libId.getOrElse(".")) "libId" -> libId.getOrElse("."))
......
...@@ -33,7 +33,7 @@ class Gears(val parent: Configurable) extends QScript with MultiSampleQScript { ...@@ -33,7 +33,7 @@ class Gears(val parent: Configurable) extends QScript with MultiSampleQScript {
override def reportClass = { override def reportClass = {
val gearsReport = new GearsReport(this) val gearsReport = new GearsReport(this)
gearsReport.outputDir = new File(outputDir, "report") gearsReport.outputDir = new File(outputDir, "report")
gearsReport.summaryFile = summaryFile gearsReport.summaryDbFile = summaryDbFile
Some(gearsReport) Some(gearsReport)
} }
......
...@@ -73,7 +73,7 @@ class GearsSingle(val parent: Configurable) extends QScript with SummaryQScript ...@@ -73,7 +73,7 @@ class GearsSingle(val parent: Configurable) extends QScript with SummaryQScript
override def reportClass = { override def reportClass = {
val gears = new GearsSingleReport(this) val gears = new GearsSingleReport(this)
gears.outputDir = new File(outputDir, "report") gears.outputDir = new File(outputDir, "report")
gears.summaryFile = summaryFile gears.summaryDbFile = summaryDbFile
sampleId.foreach(gears.args += "sampleId" -> _) sampleId.foreach(gears.args += "sampleId" -> _)
libId.foreach(gears.args += "libId" -> _) libId.foreach(gears.args += "libId" -> _)
Some(gears) Some(gears)
......
...@@ -47,7 +47,7 @@ class Gentrap(val parent: Configurable) extends QScript ...@@ -47,7 +47,7 @@ class Gentrap(val parent: Configurable) extends QScript
override def reportClass: Option[ReportBuilderExtension] = { override def reportClass: Option[ReportBuilderExtension] = {
val report = new GentrapReport(this) val report = new GentrapReport(this)
report.outputDir = new File(outputDir, "report") report.outputDir = new File(outputDir, "report")
report.summaryFile = summaryFile report.summaryDbFile = summaryDbFile
Some(report) Some(report)
} }
......
...@@ -140,7 +140,7 @@ class Mapping(val parent: Configurable) extends QScript with SummaryQScript with ...@@ -140,7 +140,7 @@ class Mapping(val parent: Configurable) extends QScript with SummaryQScript with
override def reportClass: Some[MappingReport] = { override def reportClass: Some[MappingReport] = {
val mappingReport = new MappingReport(this) val mappingReport = new MappingReport(this)
mappingReport.outputDir = new File(outputDir, "report") mappingReport.outputDir = new File(outputDir, "report")
mappingReport.summaryFile = summaryFile mappingReport.summaryDbFile = summaryDbFile
mappingReport.args = Map( mappingReport.args = Map(
"sampleId" -> sampleId.getOrElse("."), "sampleId" -> sampleId.getOrElse("."),
"libId" -> libId.getOrElse(".")) "libId" -> libId.getOrElse("."))
......
...@@ -63,7 +63,7 @@ trait MultisampleMappingTrait extends MultiSampleQScript ...@@ -63,7 +63,7 @@ trait MultisampleMappingTrait extends MultiSampleQScript
override def reportClass: Option[ReportBuilderExtension] = { override def reportClass: Option[ReportBuilderExtension] = {
val report = new MultisampleMappingReport(this) val report = new MultisampleMappingReport(this)
report.outputDir = new File(outputDir, "report") report.outputDir = new File(outputDir, "report")
report.summaryFile = summaryFile report.summaryDbFile = summaryDbFile
Some(report) Some(report)
} }
......
...@@ -40,7 +40,7 @@ class Shiva(val parent: Configurable) extends QScript with MultisampleMappingTra ...@@ -40,7 +40,7 @@ class Shiva(val parent: Configurable) extends QScript with MultisampleMappingTra
override def reportClass: Option[ReportBuilderExtension] = { override def reportClass: Option[ReportBuilderExtension] = {
val shiva = new ShivaReport(this) val shiva = new ShivaReport(this)
shiva.outputDir = new File(outputDir, "report") shiva.outputDir = new File(outputDir, "report")
shiva.summaryFile = summaryFile shiva.summaryDbFile = summaryDbFile
Some(shiva) Some(shiva)
} }
......
...@@ -119,7 +119,7 @@ class TinyCap(val parent: Configurable) extends QScript ...@@ -119,7 +119,7 @@ class TinyCap(val parent: Configurable) extends QScript
override def reportClass: Option[ReportBuilderExtension] = { override def reportClass: Option[ReportBuilderExtension] = {
val report = new TinyCapReport(this) val report = new TinyCapReport(this)
report.outputDir = new File(outputDir, "report") report.outputDir = new File(outputDir, "report")
report.summaryFile = summaryFile report.summaryDbFile = summaryDbFile
Some(report) Some(report)
} }
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment