Thomas há 1 ano atrás
pai
commit
44dd4ff144
7 ficheiros alterados com 229 adições e 34 exclusões
  1. 123 0
      src/callers/clairs.rs
  2. 34 30
      src/callers/deep_variant.rs
  3. 1 1
      src/callers/mod.rs
  4. 56 0
      src/commands/bcftools.rs
  5. 1 0
      src/commands/mod.rs
  6. 12 2
      src/lib.rs
  7. 2 1
      src/runners.rs

+ 123 - 0
src/callers/clairs.rs

@@ -0,0 +1,123 @@
+use crate::{
+    commands::bcftools::{Bcftools, BcftoolsConfig},
+    runners::DockerRun,
+};
+use std::{fs, path::Path};
+use tracing::info;
+
+#[derive(Debug)]
+pub struct ClairSConfig {
+    pub result_dir: String,
+    pub reference: String,
+    pub threads: u8,
+    pub platform: String,
+}
+
+impl Default for ClairSConfig {
+    fn default() -> Self {
+        Self {
+            result_dir: "/data/longreads_basic_pipe".to_string(),
+            reference: "/data/ref/hs1/chm13v2.0.fa".to_string(),
+            threads: 155,
+            platform: "ont_r10_dorado_sup_5khz".to_string(),
+        }
+    }
+}
+
+#[derive(Debug)]
+pub struct ClairS {
+    pub id: String,
+    pub result_dir: String,
+    pub output_vcf: String,
+    pub output_indel: String,
+    pub vcf_passed: String,
+    pub indel_vcf_passed: String,
+    pub diag_bam: String,
+    pub mrd_bam: String,
+    pub config: ClairSConfig,
+    pub log: String,
+}
+
+impl ClairS {
+    pub fn new(id: &str, diag_bam: &str, mrd_bam: &str, config: ClairSConfig) -> Self {
+        let result_dir = format!("{}/{}/diag/ClairS", config.result_dir, id);
+        let output_vcf = format!("{result_dir}/output.vcf.gz");
+        let output_indel = format!("{result_dir}/indel.vcf.gz");
+        let vcf_passed = format!("{result_dir}/{id}_diag_clairs_PASSED.vcf.gz",);
+        let indel_vcf_passed = format!("{result_dir}/${id}_diag_clairs_indel_PASSED.vcf.gz");
+
+        Self {
+            id: id.to_string(),
+            result_dir,
+            output_vcf,
+            vcf_passed,
+            diag_bam: diag_bam.to_string(),
+            mrd_bam: mrd_bam.to_string(),
+            config,
+            output_indel,
+            log: String::default(),
+            indel_vcf_passed,
+        }
+    }
+
+    pub fn run(&mut self) {
+        // Create out dir
+        if !Path::new(&self.result_dir).exists() {
+            fs::create_dir_all(&self.result_dir).expect("Failed to create output directory");
+        } else {
+            info!("ClairS output directory exists");
+        }
+
+        // Run Docker command if output VCF doesn't exist
+        if !Path::new(&self.output_vcf).exists() {
+            let docker_run = DockerRun::run(&[
+                "run",
+                "-d",
+                "-v",
+                "/data:/data",
+                "-v",
+                &format!("{}:{}", self.result_dir, self.result_dir),
+                "hkubal/clairs:latest",
+                "/opt/bin/run_clairs",
+                "--tumor_bam_fn",
+                &self.diag_bam,
+                "--normal_bam_fn",
+                &self.mrd_bam,
+                "--ref_fn",
+                &self.config.reference,
+                "--threads",
+                &self.config.threads.to_string(),
+                "--platform",
+                &self.config.platform,
+                "--enable_indel_calling",
+                "--include_all_ctgs",
+                "--output_dir",
+                &self.result_dir,
+                "-s",
+                &format!("{}_diag", self.id),
+            ]);
+            docker_run.wait();
+            self.log = docker_run.log();
+        } else {
+            info!("ClairS output vcf already exists");
+        }
+
+        // Keep PASS
+        if !Path::new(&self.vcf_passed).exists() {
+            Bcftools::new(
+                &self.output_vcf,
+                &self.vcf_passed,
+                BcftoolsConfig::default(),
+            )
+            .keep_pass();
+        }
+        if !Path::new(&self.indel_vcf_passed).exists() {
+            Bcftools::new(
+                &self.output_indel,
+                &self.indel_vcf_passed,
+                BcftoolsConfig::default(),
+            )
+            .keep_pass();
+        }
+    }
+}

+ 34 - 30
src/callers/deep_variant.rs

@@ -1,16 +1,11 @@
 use std::{
     fs,
-    io::{BufRead, BufReader},
     path::Path,
-    process::{Child, Command, Stdio},
-    sync::{Arc, Mutex},
-    thread,
+    process::{Command, Stdio},
 };
-
-use duct::cmd;
 use log::{info, warn};
 
-use crate::runners::{self, DockerRun};
+use crate::runners::DockerRun;
 
 #[derive(Debug)]
 pub struct DeepVariantConfig {
@@ -40,42 +35,55 @@ pub struct DeepVariant {
     pub id: String,
     pub time_point: String,
     pub bam: String,
+    pub output_dir: String,
+    pub output_vcf: String,
+    pub vcf_passed: String,
+    pub log: String,
     pub config: DeepVariantConfig,
 }
 
 impl DeepVariant {
     pub fn new(id: &str, time_point: &str, bam: &str, config: DeepVariantConfig) -> Self {
+        let output_dir = format!(
+            "{}/{}/{}/DeepVariant",
+            config.result_dir, id, time_point
+        );
+        let output_vcf = format!(
+            "{output_dir}/{}_{}_DeepVariant.vcf.gz",
+            id, time_point
+        );
+        let vcf_passed = format!(
+            "{}/{}_{}_DeepVariant_PASSED.vcf.gz",
+            output_dir, id,time_point
+        );
+
         Self {
             id: id.to_string(),
             time_point: time_point.to_string(),
             bam: bam.to_string(),
             config,
+            log: String::default(),
+            output_dir,
+            output_vcf,
+            vcf_passed,
         }
     }
 
-    pub fn run(self) -> Self {
+    pub fn run(mut self) -> Self {
         // Create out dir
-        let output_dir = format!(
-            "{}/{}/{}/DeepVariant",
-            self.config.result_dir, self.id, self.time_point
-        );
-        if !Path::new(&output_dir).exists() {
-            fs::create_dir_all(&output_dir).expect("Failed to create output directory");
+        if !Path::new(&self.output_dir).exists() {
+            fs::create_dir_all(&self.output_dir).expect("Failed to create output directory");
         }
-        let output_vcf = format!(
-            "{output_dir}/{}_{}_DeepVariant.vcf.gz",
-            self.id, self.time_point
-        );
-
+        
         // Run Docker command if output VCF doesn't exist
-        if !Path::new(&output_vcf).exists() {
+        if !Path::new(&self.output_vcf).exists() {
             let docker_run = DockerRun::run(&[
                 "run",
                 "-d",
                 "-v",
                 "/data:/data", // <---
                 "-v",
-                &format!("{}:/output", output_dir),
+                &format!("{}:/output", self.output_dir),
                 &format!("google/deepvariant:{}", self.config.bin_version),
                 "/opt/deepvariant/bin/run_deepvariant",
                 "--model_type=ONT_R104",
@@ -98,16 +106,12 @@ impl DeepVariant {
                 &format!("{}_{}", self.id, self.time_point),
             ]);
             docker_run.wait();
-            let log = docker_run.log();
-            println!("{log}");
+            self.log = docker_run.log();
         }
 
         // Keep PASS
-        let vcf_passed = format!(
-            "{}/{}_{}_DeepVariant_PASSED.vcf.gz",
-            output_dir, self.id, self.time_point
-        );
-        if !Path::new(&vcf_passed).exists() {
+        if !Path::new(&self.vcf_passed).exists() {
+            info!("Filtering PASS variants");
             let status = Command::new(&self.config.bcftools)
                 .args([
                     "view",
@@ -116,9 +120,9 @@ impl DeepVariant {
                     "20",
                     "-i",
                     "FILTER='PASS'",
-                    &output_vcf,
+                    &self.output_vcf,
                     "-o",
-                    &vcf_passed,
+                    &self.vcf_passed,
                 ])
                 .stdout(Stdio::inherit())
                 .stderr(Stdio::inherit())

+ 1 - 1
src/callers/mod.rs

@@ -1,3 +1,3 @@
-
 pub mod deep_variant;
+pub mod clairs;
 

+ 56 - 0
src/commands/bcftools.rs

@@ -0,0 +1,56 @@
+use std::process::{Command, Stdio};
+use log::warn;
+
+#[derive(Debug)]
+pub struct BcftoolsConfig {
+    pub bin: String,
+    pub threads: u8,
+}
+
+impl Default for BcftoolsConfig {
+    fn default() -> Self {
+        Self {
+            bin: "/data/tools/bcftools-1.18/bcftools".to_string(),
+            threads: 20,
+        }
+    }
+}
+
+#[derive(Debug)]
+pub struct Bcftools {
+    pub input: String,
+    pub output: String,
+    pub config: BcftoolsConfig,
+}
+
+impl Bcftools {
+    pub fn new(input: &str, output: &str, config: BcftoolsConfig) -> Self {
+        Bcftools {
+            input: input.to_string(),
+            output: output.to_string(),
+            config,
+        }
+    }
+    pub fn keep_pass(&self) {
+        let status = Command::new(&self.config.bin)
+            .args([
+                "view",
+                "--write-index",
+                "--threads",
+                &self.config.threads.to_string(),
+                "-i",
+                "FILTER='PASS'",
+                &self.input,
+                "-o",
+                &self.output,
+            ])
+            .stdout(Stdio::inherit())
+            .stderr(Stdio::inherit())
+            .status()
+            .expect("Failed to filter VCF with bcftools");
+
+        if !status.success() {
+            warn!("bcftools command failed with status: {}", status);
+        }
+    }
+}

+ 1 - 0
src/commands/mod.rs

@@ -1 +1,2 @@
 pub mod dorado;
+pub mod bcftools;

+ 12 - 2
src/lib.rs

@@ -136,7 +136,7 @@ mod tests {
     use self::{callers::deep_variant::DeepVariantConfig, commands::dorado};
 
     use super::*;
-    use crate::{bam::BamType, callers::deep_variant::DeepVariant};
+    use crate::{bam::BamType, callers::{clairs::{ClairS, ClairSConfig}, deep_variant::DeepVariant}};
 
     #[test]
     fn it_works() {
@@ -209,7 +209,17 @@ mod tests {
             result_dir: "/data/test".to_string(),
             ..DeepVariantConfig::default()
         };
-        let dv = DeepVariant::new("test_a", "diag", "/data/test_data/subset.bam", config).run();
+        let _dv = DeepVariant::new("test_a", "diag", "/data/test_data/subset.bam", config).run();
+        Ok(())
+    }
+
+    #[test_log::test]
+    fn clairs() -> anyhow::Result<()> {
+        let config = ClairSConfig {
+            result_dir: "/data/test".to_string(),
+            ..ClairSConfig::default()
+        };
+        ClairS::new("test_a", "/data/test_data/subset.bam", "/data/test_data/subset_mrd.bam", config).run();
         Ok(())
     }
 

+ 2 - 1
src/runners.rs

@@ -22,7 +22,6 @@ impl DockerRun {
             ..Default::default()
         };
 
-
         // Setup Ctrl-C handler
         {
             let container_id = Arc::clone(&docker_run.container_id);
@@ -74,8 +73,10 @@ impl DockerRun {
                 }
             }
         });
+
         docker_run
     }
+
     // Wait for the container to finish
     pub fn wait(&self) {
         if let Ok(container_id) = self.container_id.lock() {