|
@@ -11,11 +11,14 @@ pub mod theme;
|
|
|
#[cfg(test)]
|
|
#[cfg(test)]
|
|
|
mod tests {
|
|
mod tests {
|
|
|
use crate::{
|
|
use crate::{
|
|
|
- gene_model::load_gene_model_from_gff3, loliplot::{Lollipop, SvgConfig}, loliplot_parser::{parse_domains_by_gene_name, Rgb}, mutation::{dels_to_ranges, mutations_to_lollipops, parse_kind_colors, parse_mutations}
|
|
|
|
|
|
|
+ circ::TranslocData,
|
|
|
|
|
+ gene_model::load_gene_model_from_gff3,
|
|
|
|
|
+ loliplot_parser::{collapse_domain_names, parse_domains_by_gene_name},
|
|
|
|
|
+ mutation::{dels_to_ranges, mutations_to_lollipops, parse_kind_colors, parse_mutations},
|
|
|
};
|
|
};
|
|
|
use cytoband::{read_ranges, svg_chromosome, AdditionalRect, RectPosition};
|
|
use cytoband::{read_ranges, svg_chromosome, AdditionalRect, RectPosition};
|
|
|
use report::compile_typst_report;
|
|
use report::compile_typst_report;
|
|
|
- use std::{collections::HashMap, fs::File, path::Path};
|
|
|
|
|
|
|
+ use std::{collections::HashMap, path::Path};
|
|
|
|
|
|
|
|
use crate::{
|
|
use crate::{
|
|
|
circ::{pack_tracks, read_arcs, read_gene_labels, read_translocs},
|
|
circ::{pack_tracks, read_arcs, read_gene_labels, read_translocs},
|
|
@@ -70,7 +73,7 @@ mod tests {
|
|
|
cytoband::Lollipop {
|
|
cytoband::Lollipop {
|
|
|
position: 5000000,
|
|
position: 5000000,
|
|
|
above: true,
|
|
above: true,
|
|
|
- letter: 'A',
|
|
|
|
|
|
|
+ letter: ' ',
|
|
|
color: String::from("red"),
|
|
color: String::from("red"),
|
|
|
},
|
|
},
|
|
|
cytoband::Lollipop {
|
|
cytoband::Lollipop {
|
|
@@ -360,6 +363,92 @@ mod tests {
|
|
|
Ok(())
|
|
Ok(())
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ #[test]
|
|
|
|
|
+ fn gene_translocations() -> anyhow::Result<()> {
|
|
|
|
|
+ let gene_name = "NOTCH1";
|
|
|
|
|
+ let translocs = read_translocs("/data/tmp_trl.tsv")?;
|
|
|
|
|
+
|
|
|
|
|
+ // Select translocations having one partner with gene_name
|
|
|
|
|
+ let translocs: Vec<TranslocData> = translocs
|
|
|
|
|
+ .into_iter()
|
|
|
|
|
+ .filter(|t| t.left_gene == gene_name || t.right_gene == gene_name)
|
|
|
|
|
+ .collect();
|
|
|
|
|
+
|
|
|
|
|
+ // Chromosomes to render
|
|
|
|
|
+ let mut chroms: Vec<String> = Vec::new();
|
|
|
|
|
+ for chr_id in 0..24 {
|
|
|
|
|
+ let chr_name = if chr_id <= 21 {
|
|
|
|
|
+ format!("chr{}", chr_id + 1)
|
|
|
|
|
+ } else if chr_id == 22 {
|
|
|
|
|
+ "chrX".to_string()
|
|
|
|
|
+ } else {
|
|
|
|
|
+ "chrY".to_string()
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ let present = translocs
|
|
|
|
|
+ .iter()
|
|
|
|
|
+ .any(|t| t.left_chr == chr_name || t.right_chr == chr_name);
|
|
|
|
|
+ if present && !chroms.contains(&chr_name) {
|
|
|
|
|
+ chroms.push(chr_name);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ let cfg = circ::CircosConfig {
|
|
|
|
|
+ r: 250.0,
|
|
|
|
|
+ ..Default::default()
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ let mut circos = circ::Circos::new_with_chrom(&chroms, cfg)?;
|
|
|
|
|
+
|
|
|
|
|
+ // LABELS
|
|
|
|
|
+ let mut hm: HashMap<String, (usize, u32, usize)> = HashMap::new();
|
|
|
|
|
+
|
|
|
|
|
+ translocs.iter().for_each(|t| {
|
|
|
|
|
+ let left_chrom_id = *circos.chroms_ids.get(&t.left_chr).unwrap();
|
|
|
|
|
+ hm.entry(t.left_gene.to_string())
|
|
|
|
|
+ .and_modify(|e| e.2 += 1)
|
|
|
|
|
+ .or_insert((left_chrom_id, t.left_pos, 1));
|
|
|
|
|
+
|
|
|
|
|
+ let right_chrom_id = *circos.chroms_ids.get(&t.right_chr).unwrap();
|
|
|
|
|
+ hm.entry(t.right_gene.to_string())
|
|
|
|
|
+ .and_modify(|e| e.2 += 1)
|
|
|
|
|
+ .or_insert((right_chrom_id, t.right_pos, 1));
|
|
|
|
|
+ });
|
|
|
|
|
+ circos.add_labels(
|
|
|
|
|
+ &hm.iter()
|
|
|
|
|
+ .map(|(gene, (id, pos, n))| (format!("{gene} (n={n})"), *id, *pos))
|
|
|
|
|
+ .collect::<Vec<(String, usize, u32)>>(),
|
|
|
|
|
+ );
|
|
|
|
|
+
|
|
|
|
|
+ // ARCS
|
|
|
|
|
+ translocs.iter().for_each(|t| {
|
|
|
|
|
+ let left_chrom_id = *circos.chroms_ids.get(&t.left_chr).unwrap();
|
|
|
|
|
+ let right_chrom_id = *circos.chroms_ids.get(&t.right_chr).unwrap();
|
|
|
|
|
+ circos.tracks[circos.ref_track.unwrap()].add_cord(
|
|
|
|
|
+ circ::Attach::Data {
|
|
|
|
|
+ set_idx: left_chrom_id,
|
|
|
|
|
+ start: t.left_pos,
|
|
|
|
|
+ end: None,
|
|
|
|
|
+ },
|
|
|
|
|
+ circ::Attach::Data {
|
|
|
|
|
+ set_idx: right_chrom_id,
|
|
|
|
|
+ start: t.right_pos,
|
|
|
|
|
+ end: None,
|
|
|
|
|
+ },
|
|
|
|
|
+ circos.config.r - (circos.config.r * 0.1),
|
|
|
|
|
+ 0.3,
|
|
|
|
|
+ "red".to_string(),
|
|
|
|
|
+ 2,
|
|
|
|
|
+ );
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ circos.save_to_file(
|
|
|
|
|
+ &format!("/data/genes_results/{gene_name}_translocations.svg"),
|
|
|
|
|
+ 100.0,
|
|
|
|
|
+ )?;
|
|
|
|
|
+ Ok(())
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
#[test]
|
|
#[test]
|
|
|
fn deletions() -> anyhow::Result<()> {
|
|
fn deletions() -> anyhow::Result<()> {
|
|
|
let mut circos = circ::Circos::new();
|
|
let mut circos = circ::Circos::new();
|
|
@@ -756,18 +845,22 @@ mod tests {
|
|
|
// let id = "NM_000314.8"; // PTEN
|
|
// let id = "NM_000314.8"; // PTEN
|
|
|
// let id = "NM_001015877.2"; // PHF6
|
|
// let id = "NM_001015877.2"; // PHF6
|
|
|
// let id = "NM_001005361.3"; // DNM2
|
|
// let id = "NM_001005361.3"; // DNM2
|
|
|
- let mut pten_cfg = SvgConfig::default();
|
|
|
|
|
- pten_cfg.range_max_lanes = 20;
|
|
|
|
|
|
|
+ // let id = "NM_002185.5"; // IL7R
|
|
|
|
|
+ let pten_cfg = loliplot::SvgConfig {
|
|
|
|
|
+ range_max_lanes: 20,
|
|
|
|
|
+ ..Default::default()
|
|
|
|
|
+ };
|
|
|
|
|
|
|
|
// let gff3 = Path::new("./notch_gff.gff3");
|
|
// let gff3 = Path::new("./notch_gff.gff3");
|
|
|
let gff3 = Path::new("/data/ref/hs1/chm13v2.0_RefSeq_Liftoff_v5.1.gff3");
|
|
let gff3 = Path::new("/data/ref/hs1/chm13v2.0_RefSeq_Liftoff_v5.1.gff3");
|
|
|
let r = load_gene_model_from_gff3(gff3, id)?;
|
|
let r = load_gene_model_from_gff3(gff3, id)?;
|
|
|
let gene_name = r.gene_id.clone();
|
|
let gene_name = r.gene_id.clone();
|
|
|
|
|
|
|
|
- let domains = parse_domains_by_gene_name(
|
|
|
|
|
|
|
+ let mut domains = parse_domains_by_gene_name(
|
|
|
"/data/ref/hs1/my_protein_domains_hs1_lifted_v2.4.0_sorted.gff3",
|
|
"/data/ref/hs1/my_protein_domains_hs1_lifted_v2.4.0_sorted.gff3",
|
|
|
&gene_name,
|
|
&gene_name,
|
|
|
)?;
|
|
)?;
|
|
|
|
|
+ collapse_domain_names(&mut domains);
|
|
|
let colors = parse_kind_colors("/data/colors.tsv")?;
|
|
let colors = parse_kind_colors("/data/colors.tsv")?;
|
|
|
let muts = parse_mutations(format!("/data/genes_results/{gene_name}_muts.tsv"))?;
|
|
let muts = parse_mutations(format!("/data/genes_results/{gene_name}_muts.tsv"))?;
|
|
|
println!("{}", muts.len());
|
|
println!("{}", muts.len());
|
|
@@ -779,11 +872,6 @@ mod tests {
|
|
|
|
|
|
|
|
let lols = mutations_to_lollipops(&muts, &colors);
|
|
let lols = mutations_to_lollipops(&muts, &colors);
|
|
|
|
|
|
|
|
- println!("{:?}", lols);
|
|
|
|
|
- println!("{}", lols.len());
|
|
|
|
|
- lols.iter()
|
|
|
|
|
- .filter(|l| l.kind == "SNV_MISS")
|
|
|
|
|
- .for_each(|l| println!("{l:?}"));
|
|
|
|
|
let svg = gene_model_to_svg(&r, &domains, &lols, &dels, &pten_cfg); // write svg to file
|
|
let svg = gene_model_to_svg(&r, &domains, &lols, &dels, &pten_cfg); // write svg to file
|
|
|
std::fs::write(
|
|
std::fs::write(
|
|
|
format!("/data/genes_results/{gene_name}_struct_muts.svg"),
|
|
format!("/data/genes_results/{gene_name}_struct_muts.svg"),
|
|
@@ -798,4 +886,51 @@ mod tests {
|
|
|
compile_typst_report("LEVASSEUR").unwrap();
|
|
compile_typst_report("LEVASSEUR").unwrap();
|
|
|
// write_report("/data/test.pdf");
|
|
// write_report("/data/test.pdf");
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
|
|
+ #[test]
|
|
|
|
|
+ fn cdkn2a_del() -> anyhow::Result<()> {
|
|
|
|
|
+ let gene_name = "CDKN2A";
|
|
|
|
|
+ let colors = parse_kind_colors("/data/colors.tsv")?;
|
|
|
|
|
+
|
|
|
|
|
+ let mut dels = dels_to_ranges(
|
|
|
|
|
+ format!("/data/genes_results/{gene_name}_dels.tsv"),
|
|
|
|
|
+ &colors,
|
|
|
|
|
+ loliplot::LollipopSide::Bottom, // or Bottom — your choice for this track
|
|
|
|
|
+ )?;
|
|
|
|
|
+ dels.sort_by_key(|d| std::cmp::Reverse(d.end.saturating_sub(d.start)));
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+ println!("{}", dels.len());
|
|
|
|
|
+ let additional_rects: Vec<AdditionalRect> = dels
|
|
|
|
|
+ .into_iter()
|
|
|
|
|
+ .filter(|d| d.end.saturating_sub(d.start) > 1_000_000)
|
|
|
|
|
+ .enumerate()
|
|
|
|
|
+ .map(|(i, d)| AdditionalRect {
|
|
|
|
|
+ start: d.start as u32,
|
|
|
|
|
+ end: d.end as u32,
|
|
|
|
|
+ color: d.color.to_string(),
|
|
|
|
|
+ position: RectPosition::Below(i as u32),
|
|
|
|
|
+ })
|
|
|
|
|
+ .collect();
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+ let lollipops = vec![cytoband::Lollipop {
|
|
|
|
|
+ position: 21989169,
|
|
|
|
|
+ above: true,
|
|
|
|
|
+ letter: ' ',
|
|
|
|
|
+ color: String::from("red"),
|
|
|
|
|
+ }];
|
|
|
|
|
+
|
|
|
|
|
+ svg_chromosome(
|
|
|
|
|
+ "chr9",
|
|
|
|
|
+ 2000,
|
|
|
|
|
+ 100,
|
|
|
|
|
+ "/data/ref/hs1/cytoBandMapped.bed",
|
|
|
|
|
+ "/data/chr1.svg",
|
|
|
|
|
+ &additional_rects,
|
|
|
|
|
+ &lollipops,
|
|
|
|
|
+ )?;
|
|
|
|
|
+
|
|
|
|
|
+ Ok(())
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|