|
@@ -0,0 +1,220 @@
|
|
|
|
|
+#let cr_colors = (
|
|
|
|
|
+ dark_grey: rgb("#333333"), beige: rgb("#fdf0d5"), light_grey: rgb("#eeeeee"), dark_red: rgb("#780000"), red: rgb("#c1121f"), blue: rgb("#669bbc"), dark_blue: rgb("#003049"),
|
|
|
|
|
+)
|
|
|
|
|
+
|
|
|
|
|
+#import "@local/svg-emoji:0.1.0": setup-emoji, noto, github
|
|
|
|
|
+#show: setup-emoji.with(font: noto)
|
|
|
|
|
+
|
|
|
|
|
+#set page(paper: "a4", fill: cr_colors.light_grey)
|
|
|
|
|
+
|
|
|
|
|
+#show heading.where(level: 1): it => [
|
|
|
|
|
+ #set align(center)
|
|
|
|
|
+ #set text(fill: cr_colors.dark_blue)
|
|
|
|
|
+ #it.body
|
|
|
|
|
+ #v(18pt)
|
|
|
|
|
+]
|
|
|
|
|
+#show image: set text(font: "FreeSans")
|
|
|
|
|
+
|
|
|
|
|
+#set text(size: 16pt, font: "Futura", fill: cr_colors.dark_blue)
|
|
|
|
|
+#let contigs = (
|
|
|
|
|
+ "chr1", "chr2", "chr3", "chr4", "chr5", "chr6", "chr7", "chr8", "chr9", "chr10", "chr11", "chr12", "chr13", "chr14", "chr15", "chr16", "chr17", "chr18", "chr19", "chr20", "chr21", "chr22", "chrX", "chrY",
|
|
|
|
|
+)
|
|
|
|
|
+#let parseCustomDate(dateString) = {
|
|
|
|
|
+ let parts = dateString.split("T")
|
|
|
|
|
+ let datePart = parts.at(0).replace("-", "/")
|
|
|
|
|
+ let timePart = parts.at(1).split(":")
|
|
|
|
|
+ let hour = timePart.at(0)
|
|
|
|
|
+ let minute = timePart.at(1)
|
|
|
|
|
+
|
|
|
|
|
+ return datePart + " " + hour + "h" + minute
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+#let formatString(input) = {
|
|
|
|
|
+ let words = input.split("_")
|
|
|
|
|
+ let capitalizedWords = words.map(word => {
|
|
|
|
|
+ if word.len() > 0 {
|
|
|
|
|
+ upper(word.first()) + word.slice(1)
|
|
|
|
|
+ } else {
|
|
|
|
|
+ word
|
|
|
|
|
+ }
|
|
|
|
|
+ })
|
|
|
|
|
+ capitalizedWords.join(" ")
|
|
|
|
|
+}
|
|
|
|
|
+#let si-fmt(val, precision: 1, sep: "\u{202F}", binary: false) = {
|
|
|
|
|
+ let factor = if binary { 1024 } else { 1000 }
|
|
|
|
|
+ let gt1_suffixes = ("k", "M", "G", "T", "P", "E", "Z", "Y")
|
|
|
|
|
+ let lt1_suffixes = ("m", "μ", "n", "p", "f", "a", "z", "y")
|
|
|
|
|
+ let scale = ""
|
|
|
|
|
+ let unit = ""
|
|
|
|
|
+
|
|
|
|
|
+ if type(val) == content {
|
|
|
|
|
+ if val.has("text") {
|
|
|
|
|
+ val = val.text
|
|
|
|
|
+ } else if val.has("children") {
|
|
|
|
|
+ val = val.children.map(content => content.text).join()
|
|
|
|
|
+ } else {
|
|
|
|
|
+ panic(val.children.map(content => content.text).join())
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ // if val contains a unit, split it off
|
|
|
|
|
+ if type(val) == str {
|
|
|
|
|
+ unit = val.find(regex("(\D+)$"))
|
|
|
|
|
+ val = float(val.split(unit).at(0))
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if calc.abs(val) > 1 {
|
|
|
|
|
+ for suffix in gt1_suffixes {
|
|
|
|
|
+ if calc.abs(val) < factor {
|
|
|
|
|
+ break
|
|
|
|
|
+ }
|
|
|
|
|
+ val /= factor
|
|
|
|
|
+ scale += " " + suffix
|
|
|
|
|
+ }
|
|
|
|
|
+ } else if val != 0 and calc.abs(val) < 1 {
|
|
|
|
|
+ for suffix in lt1_suffixes {
|
|
|
|
|
+ if calc.abs(val) > 1 {
|
|
|
|
|
+ break
|
|
|
|
|
+ }
|
|
|
|
|
+ val *= factor
|
|
|
|
|
+ scale += " " + suffix
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ let formatted = str(calc.round(val, digits: precision))
|
|
|
|
|
+
|
|
|
|
|
+ formatted + sep + scale.split().at(-1, default: "") + unit
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+#let reportCoverage(prefix) = {
|
|
|
|
|
+ image(prefix + "_global.svg", width: 100%)
|
|
|
|
|
+ for contig in contigs {
|
|
|
|
|
+ heading(level: 4, contig)
|
|
|
|
|
+ let path = prefix + "_" + contig
|
|
|
|
|
+ image(path + "_chromosome.svg")
|
|
|
|
|
+ let data = json(path + "_stats.json")
|
|
|
|
|
+ grid(
|
|
|
|
|
+ columns: (1fr, 2fr), gutter: 3pt, align(
|
|
|
|
|
+ left + horizon,
|
|
|
|
|
+ )[
|
|
|
|
|
+ #set text(size: 12pt)
|
|
|
|
|
+ #table(
|
|
|
|
|
+ stroke: none, columns: (auto, 1fr), gutter: 3pt, [Mean], [#calc.round(data.mean, digits: 2)], [Standard dev.], [#calc.round(data.std_dev, digits: 2)], ..data.breaks_values.map(r => ([#r.at(0)], [#calc.round(r.at(1) * 100, digits: 1)%])).flatten(),
|
|
|
|
|
+ )
|
|
|
|
|
+ ], align(right, image(path + "_distrib.svg", width: 100%)),
|
|
|
|
|
+ )
|
|
|
|
|
+
|
|
|
|
|
+ parbreak()
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+#let reportBam(path) = {
|
|
|
|
|
+ let data = json(path)
|
|
|
|
|
+ table(
|
|
|
|
|
+ gutter: 3pt, stroke: none, columns: (auto, 1fr), ..for (key, value) in data {
|
|
|
|
|
+ if key != "cramino" and key != "composition" and key != "path" and key != "modified" {
|
|
|
|
|
+ ([ #formatString(key) ], [ #value ])
|
|
|
|
|
+ } else if key == "modified" {
|
|
|
|
|
+ ([ Modified Date (UTC) ], [ #parseCustomDate(value) ])
|
|
|
|
|
+ } else if key == "composition" {
|
|
|
|
|
+ ([ Run(s) ], [
|
|
|
|
|
+ #for (i, v) in value.enumerate() {
|
|
|
|
|
+ if i > 0 [ \ ]
|
|
|
|
|
+ [#v.at(0).slice(0, 5): #calc.round(v.at(1), digits: 0)%]
|
|
|
|
|
+ }
|
|
|
|
|
+ ])
|
|
|
|
|
+ } else if key == "cramino" {
|
|
|
|
|
+ for (k, v) in value {
|
|
|
|
|
+ if k == "normalized_read_count_per_chromosome" {} else if k != "path" and k != "checksum" and k != "creation_time" and k != "file_name" {
|
|
|
|
|
+ let k = formatString(k)
|
|
|
|
|
+ let v = if type(v) == "integer" { si-fmt(v) } else { v }
|
|
|
|
|
+ ([ #k ], [ #v ])
|
|
|
|
|
+ } else {
|
|
|
|
|
+ ()
|
|
|
|
|
+ }
|
|
|
|
|
+ }.flatten()
|
|
|
|
|
+ } else {
|
|
|
|
|
+ ()
|
|
|
|
|
+ }
|
|
|
|
|
+ }.flatten(),
|
|
|
|
|
+ )
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+#let formatedReadCount(path) = {
|
|
|
|
|
+ let data = json(path)
|
|
|
|
|
+ let data = data.cramino.normalized_read_count_per_chromosome
|
|
|
|
|
+ let res = ()
|
|
|
|
|
+ for contig in contigs {
|
|
|
|
|
+ res.push(data.at(contig))
|
|
|
|
|
+ }
|
|
|
|
|
+ res.push(data.at("chrM"))
|
|
|
|
|
+ return res
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+#let printReadCount(diag_path, mrd_path) = {
|
|
|
|
|
+ let index = 14;
|
|
|
|
|
+ let c = contigs
|
|
|
|
|
+ c.push("chrM")
|
|
|
|
|
+ let diag = formatedReadCount(diag_path)
|
|
|
|
|
+ let mrd = formatedReadCount(mrd_path)
|
|
|
|
|
+ c.insert(0, "")
|
|
|
|
|
+ diag.insert(0, "diag")
|
|
|
|
|
+ mrd.insert(0, "mrd")
|
|
|
|
|
+ let arrays1 = (c.slice(0, index), diag.slice(0, index), mrd.slice(0, index))
|
|
|
|
|
+ table(columns: arrays1.at(0).len(), ..arrays1.map(arr => arr.map(item => [#item])).flatten())
|
|
|
|
|
+
|
|
|
|
|
+ let arrays2 = (c.slice(index), diag.slice(index), mrd.slice(index))
|
|
|
|
|
+ arrays2.at(0).insert(0, "")
|
|
|
|
|
+ arrays2.at(1).insert(0, "diag")
|
|
|
|
|
+ arrays2.at(2).insert(0, "mrd")
|
|
|
|
|
+
|
|
|
|
|
+ table(columns: arrays2.at(0).len(), ..arrays2.map(arr => arr.map(item => [#item])).flatten())
|
|
|
|
|
+}
|
|
|
|
|
+#set heading(numbering: "1.")
|
|
|
|
|
+#heading(level: 1, outlined: false)[Compte Rendu]
|
|
|
|
|
+
|
|
|
|
|
+#outline(
|
|
|
|
|
+ title: "Table of Contents",
|
|
|
|
|
+ depth: 3
|
|
|
|
|
+)
|
|
|
|
|
+#pagebreak()
|
|
|
|
|
+== Identity
|
|
|
|
|
+Camara
|
|
|
|
|
+
|
|
|
|
|
+== Alignement
|
|
|
|
|
+#grid(
|
|
|
|
|
+ columns: (1fr, 1fr), gutter: 3pt, [
|
|
|
|
|
+ === Diagnostic sample
|
|
|
|
|
+ #set text(size: 11pt)
|
|
|
|
|
+ #reportBam(
|
|
|
|
|
+ "/Turbine-pool/LongReads/report/BECERRA/report/data/BECERRA_diag_hs1_info.json",
|
|
|
|
|
+ )
|
|
|
|
|
+ ], [
|
|
|
|
|
+ === MRD sample
|
|
|
|
|
+ #set text(size: 11pt)
|
|
|
|
|
+ #reportBam(
|
|
|
|
|
+ "/Turbine-pool/LongReads/report/BECERRA/report/data/BECERRA_mrd_hs1_info.json",
|
|
|
|
|
+ )
|
|
|
|
|
+ #set footnote(numbering: n => { " " })
|
|
|
|
|
+ #footnote[Values computed by #link("https://github.com/wdecoster/cramino")[cramino] v0.14.5
|
|
|
|
|
+ ]
|
|
|
|
|
+ ],
|
|
|
|
|
+)
|
|
|
|
|
+
|
|
|
|
|
+#pagebreak()
|
|
|
|
|
+=== Normalized read count by chromosome
|
|
|
|
|
+#[
|
|
|
|
|
+ #set text(size: 10pt)
|
|
|
|
|
+ #printReadCount("/Turbine-pool/LongReads/report/BECERRA/report/data/BECERRA_diag_hs1_info.json", "/Turbine-pool/LongReads/report/BECERRA/report/data/BECERRA_mrd_hs1_info.json")
|
|
|
|
|
+]
|
|
|
|
|
+
|
|
|
|
|
+=== Coverage by chromosome
|
|
|
|
|
+==== Proportion at given depth by chromosome
|
|
|
|
|
+#reportCoverage("/Turbine-pool/LongReads/report/BECERRA/report/data/scan/BECERRA")
|
|
|
|
|
+#set footnote(numbering: n => { " " })
|
|
|
|
|
+#footnote[Values computed by Pandora development version]
|
|
|
|
|
+#pagebreak()
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+#lorem(150)
|
|
|
|
|
+❌
|
|
|
|
|
+#emoji.rocket
|