use std::sync::Arc;
use std::fs;
use std::fs::File;
use std::io::Read;
use std::io::BufRead;
use std::io::BufReader;
use std::collections::HashMap;
use crate::fileformat::ShardRandomFileExtractor;
use crate::fileformat::ZipBascetShardReader;
use crate::fileformat::shard::ShardCellDictionary;
use crate::fileformat::SparseCountMatrix;
use anyhow::Result;
use clap::Args;
use std::
path::PathBuf
;
pub const DEFAULT_PATH_TEMP: &str = "temp";
pub const DEFAULT_THREADS_READ: usize = 1;
pub const DEFAULT_THREADS_WRITE: usize = 10;
pub const DEFAULT_THREADS_WORK: usize = 1;
#[derive(Args)]
pub struct QueryKmcCMD {
#[arg(short = 'i', value_parser= clap::value_parser!(PathBuf))]
pub path_in: PathBuf,
#[arg(short = 't', value_parser= clap::value_parser!(PathBuf), default_value = DEFAULT_PATH_TEMP)]
pub path_tmp: PathBuf,
#[arg(short = 'o', value_parser = clap::value_parser!(PathBuf))]
pub path_out: PathBuf,
#[arg(short = 'f', value_parser = clap::value_parser!(PathBuf))]
pub path_features: PathBuf,
#[arg(long, value_parser = clap::value_parser!(usize), default_value_t = DEFAULT_THREADS_READ)]
threads_read: usize,
#[arg(long, value_parser = clap::value_parser!(usize), default_value_t = DEFAULT_THREADS_WORK)]
threads_work: usize,
}
impl QueryKmcCMD {
pub fn try_execute(&mut self) -> Result<()> {
let params = QueryKmcParams {
path_tmp: self.path_tmp.clone(),
path_input: self.path_in.clone(),
path_output: self.path_out.clone(),
path_features: self.path_features.clone(),
threads_work: self.threads_work,
};
let _ = QueryKmc::run(
&Arc::new(params)
);
log::info!("Query has finished succesfully");
Ok(())
}
}
pub struct QueryKmcParams {
pub path_input: std::path::PathBuf,
pub path_tmp: std::path::PathBuf,
pub path_output: std::path::PathBuf,
pub path_features: std::path::PathBuf,
pub threads_work: usize,
}
pub struct QueryKmc {
}
impl QueryKmc {
pub fn run(
params: &Arc<QueryKmcParams>
) -> anyhow::Result<()> {
let mut mm = SparseCountMatrix::new();
if params.path_tmp.exists() {
anyhow::bail!("Temporary directory '{}' exists already. For safety reasons, this is not allowed. Specify as a subdirectory of an existing directory", params.path_tmp.display());
} else {
println!("Using tempdir {}", params.path_tmp.display());
if fs::create_dir_all(¶ms.path_tmp).is_err() {
panic!("Failed to create temporary directory");
};
}
let mut features_reference: HashMap<String, usize> = HashMap::new();
let file_features_ref = File::open(¶ms.path_features).unwrap();
let bufreader_features_ref = BufReader::new(&file_features_ref);
let mut kmer_size = 0;
for (feature_index, rline) in bufreader_features_ref.lines().enumerate() { if let Ok(feature) = rline { features_reference.insert(feature.to_string(), feature_index);
mm.add_feature(&feature.to_string());
kmer_size = feature.len();
} else {
println!("one feature line nope");
}
}
if kmer_size==0 {
anyhow::bail!("Feature file has no features");
} else {
println!("Read {} features. Detected kmer-length of {}", features_reference.len(), kmer_size);
}
let mut file_input = ZipBascetShardReader::new(¶ms.path_input).expect("Failed to open input file");
let list_cells = file_input.get_cell_ids().expect("Failed to get content listing for input file");
for cell_id in list_cells {
println!("doing cell {}", cell_id);
let cell_index = mm.add_cell(&cell_id);
let list_files = file_input.get_files_for_cell(&cell_id).expect("Could not get list of files for cell");
let f1="kmc_dump.txt".to_string();
if list_files.contains(&f1) {
let path_f1 = params.path_tmp.join(format!("cell_{}.kmc_dump.txt", cell_id).to_string());
file_input.extract_as(&cell_id, &f1, &path_f1).unwrap();
let file_features_ref = File::open(&path_f1).unwrap();
let mut reader = BufReader::new(&file_features_ref);
count_from_dump(
cell_index,
&features_reference,
&mut mm,
&mut reader
);
} else {
println!("No kmc_dump.txt present; File list: {:?}", list_files);
}
}
println!("Storing count table to {}", params.path_output.display());
mm.save_to_anndata(¶ms.path_output).expect("Failed to save to HDF5 file");
println!("Cleaning up temp files");
Ok(())
}
}
pub fn count_from_dump(
cell_index: usize,
features_reference: &HashMap<String, usize>,
mm: &mut SparseCountMatrix,
reader: &mut BufReader<impl Read>
){
for (_feature_index, rline) in reader.lines().enumerate() {
if let Ok(line) = rline {
let mut splitter = line.split("\t");
let feature = splitter.next().expect("Could not parse KMER sequence from cell db");
if let Some(feature_index) = features_reference.get(feature) {
let cnt = splitter.next().expect("Could not parse KMER count from cell db").
parse::<u32>().expect("Count for kmer is not a u32");
mm.add_value(cell_index, *feature_index, cnt);
}
} else {
println!("line failed");
}
}
}