Thomas 1 年之前
父节点
当前提交
c24cbb45b8
共有 4 个文件被更改,包括 192 次插入16 次删除
  1. 3 2
      src/lib.rs
  2. 36 4
      src/report.rs
  3. 4 0
      typst_template/min_template.typ
  4. 149 10
      typst_template/template.typ

+ 3 - 2
src/lib.rs

@@ -10,7 +10,7 @@ mod tests {
         generate_chromosome_svg, read_ranges, svg_chromosome, AdditionalRect, Lollipop,
         RectPosition,
     };
-    use report::write_report;
+    use report::{compile_typst_report, write_report};
 
     use super::*;
 
@@ -89,6 +89,7 @@ mod tests {
 
     #[test]
     fn pdf() {
-        write_report("/data/test.pdf");
+        compile_typst_report("DAHAN").unwrap();
+        // write_report("/data/test.pdf");
     }
 }

+ 36 - 4
src/report.rs

@@ -1,4 +1,5 @@
-use typst::eval::Tracer;
+use std::process::Command;
+
 use typst::foundations::{Bytes, Dict, Smart};
 use typst::text::Font;
 use typst_as_lib::TypstTemplate;
@@ -7,11 +8,11 @@ pub fn write_report(path: &str) {
     let font = include_bytes!("../typst_template/fonts/unicode.futurab.ttf");
     let font = Font::new(Bytes::from_static(font), 0).expect("Could not parse font!");
 
-    let template = include_str!("../typst_template/template.typ");
+    let template = include_str!("../typst_template/min_template.typ");
 
     let template = TypstTemplate::new(vec![font], template)
-        .with_static_source_file_resolver(vec!["/"])
-        .with_package_file_resolver(Default::default(), Some(ureq::AgentBuilder::new().build()));
+        // .with_static_source_file_resolver(vec!["/"])
+        .with_package_file_resolver(Default::default(), None);
 
     let mut tracer = Default::default();
 
@@ -23,3 +24,34 @@ pub fn write_report(path: &str) {
     let pdf = typst_pdf::pdf(&doc, Smart::Auto, None);
     std::fs::write(path, pdf).expect("Could not write pdf.");
 }
+
+pub fn compile_typst_report(id: &str) -> Result<(), std::io::Error> {
+    let output = Command::new("typst")
+        .arg("compile")
+        .arg("--input")
+        .arg(format!("id={id}"))
+        .arg("--input")
+        .arg(format!("base=/data/longreads_basic_pipe/{id}"))
+        .arg("--root")
+        .arg("/")
+        .arg("/home/prom/Documents/Programmes/pandora_lib_graph/typst_template/template.typ")
+        .arg(format!(
+            "/data/longreads_basic_pipe/{id}/diag/report/{id}_report.pdf",
+        ))
+        .output()?;
+
+    if output.status.success() {
+        println!("Typst compilation successful for ID: {}", id);
+        Ok(())
+    } else {
+        let error_message = String::from_utf8_lossy(&output.stderr);
+        eprintln!(
+            "Typst compilation failed for ID: {}. Error: {}",
+            id, error_message
+        );
+        Err(std::io::Error::new(
+            std::io::ErrorKind::Other,
+            "Typst compilation failed",
+        ))
+    }
+}

+ 4 - 0
typst_template/min_template.typ

@@ -0,0 +1,4 @@
+#import "@preview/oxifmt:0.2.0"
+#import "@preview/cetz:0.2.2"
+// #import "@preview/fletcher:0.5.1" as fletcher: diagram, node, edge
+// #import "@preview/codelst:2.0.1": sourcecode

+ 149 - 10
typst_template/template.typ

@@ -1,11 +1,10 @@
-#import sys: inputs
 #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"), green: rgb("#29bf12"),
 )
 
 #import "@preview/fletcher:0.5.1" as fletcher: diagram, node, edge
-// #import "@preview/metro:0.3.0": *
-// #import "@preview/cetz:0.2.2"
+#import "@preview/metro:0.3.0": *
+#import "@preview/cetz:0.2.2"
 
 #set page(paper: "a4", fill: cr_colors.light_grey, footer: locate(loc => [
   #set text(10pt)
@@ -13,16 +12,18 @@
     align(right, counter(page).display("1 / 1", both: true))
   }
 ]))
-
+#show heading: set text(font: "Futura")
 #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)
+#show image: set text(font: "FreeSans")
+
+#set text(size: 16pt, 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",
 )
@@ -187,7 +188,7 @@
   let data = json(path)
   set text(8pt)
   diagram(
-    spacing: (8pt, 25pt), node-fill: gradient.radial(cr_colors.light_grey, cr_colors.blue, radius: 300%), node-stroke: cr_colors.dark_blue + 1pt, edge-stroke: 1pt, node-inset: 8pt, node(
+    spacing: (8pt, 25pt), node-fill: gradient.radial(cr_colors.light_grey, cr_colors.blue, radius: 300%), node-stroke: cr_colors.dark_blue + 1pt, edge-stroke: 1pt, mark-scale: 70%, node-inset: 8pt, node(
       (0.2, 0), [Variants MRD: #num(data.vcf_stats.n_tumoral_init)], corner-radius: 2pt, extrude: (0, 3), name: <input_mrd>,
     ), node(
       (1.8, 0), [Variants Diag: #num(data.vcf_stats.n_constit_init)], corner-radius: 2pt, extrude: (0, 3), name: <input_diag>,
@@ -215,7 +216,7 @@
     set text(8pt)
 
   diagram(
-    spacing: (8pt, 25pt), node-fill: gradient.radial(cr_colors.light_grey, cr_colors.blue, radius: 300%), node-inset: 8pt, node-stroke: cr_colors.dark_blue + 1pt, edge-stroke: 1pt, node(
+    spacing: (8pt, 25pt), node-fill: gradient.radial(cr_colors.light_grey, cr_colors.blue, radius: 300%), node-inset: 8pt, node-stroke: cr_colors.dark_blue + 1pt, mark-scale: 70%, edge-stroke: 1pt, node(
       (0.75, 0), [Variants not in MRD VCF: #num(data.bam_stats.n_lasting)], corner-radius: 2pt, extrude: (0, 3), name: <input_mrd>,
     ), edge(<input_mrd>, <depth>, "-|>"), node((0.75, 1), [MRD alignement depth ?], shape: diamond, name: <depth>), edge(<depth>, <low_depth>, "-|>", [< 4]), node(
       (0, 2), [Low MRD depth: #num(data.bam_stats.n_low_mrd_depth)], shape: parallelogram, name: <low_depth>,
@@ -252,6 +253,75 @@
       )
   })
 }
+#let truncate(text, max-length) = {
+  if text.len() <= max-length {
+    text
+  } else {
+    text.slice(0, max-length - 3) + "..."
+  }
+}
+#let to-string(content) = {
+  if content.has("text") {
+    content.text
+  } else if content.has("children") {
+    content.children.map(to-string).join("")
+  } else if content.has("body") {
+    to-string(content.body)
+  } else if content == [ ] {
+    " "
+  }
+}
+#let format-number(num) = {
+  let s = str(num).split("").filter(item => item != "")
+  let result = ""
+  let len = s.len()
+  for (i, char) in s.enumerate() {
+    result += char
+    if (i < len - 1 and calc.rem((len - i - 1), 3) == 0) {
+      result += ","
+    }
+  }
+  result
+}
+
+#let card(d) = {
+  let position_fmt = format-number(d.position)
+  let title_bg_color = rgb("#f9fafb00")
+  rect(
+    width: 100%,
+    fill: cr_colors.light_grey,
+    stroke: cr_colors.dark_grey,
+    inset: 0em,
+    
+    radius: 12pt, 
+    [
+      #pad(top: 0.5em, bottom: -0.5em, box(
+        width: 100%,
+        fill: title_bg_color,
+        align(center,
+          text(weight: "bold")[
+            #d.contig:#position_fmt #d.reference#sym.quote.angle.r.single#truncate(d.alternative, 25)
+          ]
+        )
+      ))
+      #line(length: 100%)
+      
+
+      #lorem(50)
+    ]
+  )
+}
+// #let variant(d) = {
+//   card(d)
+// }
+
+#let variants(path) = {
+  let data = json(path)
+  for var in data {
+    card(var)
+  }
+}
+
 
 #set heading(numbering: (..numbers) => {
   if numbers.pos().len() >= 2 and numbers.pos().len() <= 3 {
@@ -263,7 +333,76 @@
 
 #outline(title: "Table of Contents", depth: 3)
 #pagebreak()
-
 == Identity
+#sys.inputs.id
+
+== Alignement
+#grid(
+  columns: (1fr, 1fr), gutter: 3pt, [
+    ==== Diagnostic sample
+    #set text(size: 11pt)
+    #reportBam(
+      sys.inputs.base + "/diag/" + sys.inputs.id + "_diag_hs1_info.json",
+    )
+  ], [
+    ==== MRD sample
+    #set text(size: 11pt)
+    #reportBam(
+      sys.inputs.base + "/mrd/" + sys.inputs.id + "_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(
+    sys.inputs.base + "/diag/" + sys.inputs.id + "_diag_hs1_info.json", sys.inputs.base + "/mrd/" + sys.inputs.id + "_mrd_hs1_info.json",
+  )
+]
+
+=== Coverage by chromosome
+==== Proportion at given depth by chromosome
+#reportCoverage(sys.inputs.base + "/diag/report/data/scan/" + sys.inputs.id)
+#set footnote(numbering: n => { " " })
+#footnote[Values computed by Pandora development version]
+
+== Variants
+=== Variants calling
+#pagebreak()
+==== VCF filters
+#pad(
+  top: 0.8cm, align(center, scale(x: 100%, y: 100%, reflow: true, variantsFlow(
+    sys.inputs.base + "/diag/report/data/" + sys.inputs.id + "_variants_stats.json",
+  ))),
+)
+==== BAM filters
+#pad(top: 0.8cm,
+  align(center, scale(x: 100%, y: 100%, reflow: true, bamFilter(
+    sys.inputs.base + "/diag/report/data/" + sys.inputs.id + "_variants_stats.json",
+  ))),
+)
+#pagebreak()
+
+=== Somatic variants
+==== Callers
+#v(0.5cm)
+#image(sys.inputs.base + "/diag/report/data/" + sys.inputs.id + "_barcharts_callers.svg")
+==== Consequences (VEP)
+#v(0.5cm)
+#image(sys.inputs.base + "/diag/report/data/" + sys.inputs.id + "_barcharts_consequences.svg")
+==== NCBI features
+#v(0.5cm)
+#image(sys.inputs.base + "/diag/report/data/" + sys.inputs.id + "_barcharts_ncbi.svg")
+#pagebreak()
+
+=== Selected Variants
+#variants(sys.inputs.base + "/diag/report/data/" + sys.inputs.id + "_annot_variants.json")
+
+#pagebreak()
+== Conclusion
 
-Camara