use std::collections::HashMap;
use std::collections::HashSet;
use std::io;
use std::path::Path;
use crate::io::read_lines;
use crate::node::Node;
use crate::relation::Relation;
#[derive(Debug, Clone)]
pub struct PadmetSpec {
pub dic_of_relations_in: HashMap<String, Vec<Relation>>,
pub dic_of_relations_out: HashMap<String, Vec<Relation>>,
pub dic_of_nodes: HashMap<String, Node>,
pub info: HashMap<String, HashMap<String, String>>,
}
#[derive(PartialEq)]
pub enum PadmetSection {
Default,
Policy,
DatabaseInformation,
Nodes,
Relations,
}
impl PadmetSpec {
pub fn new(
dic_of_relations_in: HashMap<String, Vec<Relation>>,
dic_of_relations_out: HashMap<String, Vec<Relation>>,
dic_of_nodes: HashMap<String, Node>,
info: HashMap<String, HashMap<String, String>>,
) -> Self {
PadmetSpec {
dic_of_relations_in,
dic_of_relations_out,
dic_of_nodes,
info,
}
}
pub fn from_file<P>(filename: P) -> io::Result<PadmetSpec>
where
P: AsRef<Path>,
{
let mut dic_of_relations_in: HashMap<String, Vec<Relation>> = HashMap::new();
let mut dic_of_relations_out: HashMap<String, Vec<Relation>> = HashMap::new();
let mut dic_of_nodes: HashMap<String, Node> = HashMap::new();
let mut info: HashMap<String, HashMap<String, String>> = HashMap::new();
let mut padmet_section: PadmetSection = PadmetSection::Default;
let lines = read_lines(filename)?;
let mut current_data: Option<String> = None;
for line in lines.map_while(Result::ok) {
if !line.is_empty() {
if line == "Data Base informations" {
padmet_section = PadmetSection::DatabaseInformation;
} else if line == "Policy" {
padmet_section = PadmetSection::Policy;
} else if line == "Nodes" {
padmet_section = PadmetSection::Nodes;
} else if line == "Relations" {
padmet_section = PadmetSection::Relations;
} else if padmet_section == PadmetSection::DatabaseInformation {
if !line.contains("\t") {
let line = line.replace(":", "");
current_data = Some(line.clone());
info.insert(line.clone(), HashMap::new());
} else {
let line = line.replace("\t", "");
let mut parts = line.split(":");
let key = parts.next().expect("Error parsing PADMet on Data Base informations section. Key not found.").to_owned();
let value = parts.next().expect("Error parsing PADMet on Data Base informations section. Value not found.").to_owned();
if let Some(ref data_key) = current_data {
let hashmap = info
.get_mut(data_key)
.expect("Expect key to be already present in the info hashmap.");
hashmap.insert(key, value);
}
}
} else if padmet_section == PadmetSection::Policy {
} else if padmet_section == PadmetSection::Nodes {
let mut parts = line.split("\t");
let node_type: String = parts.next().expect("Expect node_type").to_owned();
let node_id: String = parts.next().expect("Expect node_id").to_owned();
let mut node_misc: HashMap<String, Vec<String>> = HashMap::new();
let misc_items: Vec<String> = parts.map(|e| e.to_owned()).collect();
let mut i = 0;
while i + 1 < misc_items.len() {
let key = misc_items[i].clone();
let value = misc_items[i + 1].clone();
if let Some(misc_data) = node_misc.get_mut(&key) {
misc_data.push(value);
} else {
node_misc.insert(key, vec![value]);
}
i += 2;
}
let node = Node::new(node_type.clone(), node_id.clone(), node_misc);
dic_of_nodes.insert(node_id.clone(), node);
} else if padmet_section == PadmetSection::Relations {
let mut parts = line.split("\t");
let relation_id_in: String =
parts.next().expect("Expect relation in node").to_owned();
let relation_type: String =
parts.next().expect("Expect relation type").to_owned();
let relation_id_out: String =
parts.next().expect("Expect relation out node").to_owned();
let mut relation_misc: HashMap<String, Vec<String>> = HashMap::new();
let misc_items: Vec<String> = parts.map(|e| e.to_owned()).collect();
let mut i = 0;
while i + 1 < misc_items.len() {
let key = &misc_items[i];
let value = &misc_items[i + 1];
if let Some(misc_data) = relation_misc.get_mut(key) {
misc_data.push(value.clone());
} else {
relation_misc.insert(key.clone(), vec![value.clone()]);
}
i += 2;
}
let relation = Relation::new(
relation_id_in.clone(),
relation_id_out.clone(),
relation_type,
relation_misc,
);
if !dic_of_relations_in.contains_key(&relation_id_in) {
dic_of_relations_in.insert(relation_id_in.clone(), Vec::new());
}
dic_of_relations_in
.get_mut(&relation_id_in)
.expect("Key should be already be created.")
.push(relation.clone());
if !dic_of_relations_out.contains_key(&relation_id_out) {
dic_of_relations_out.insert(relation_id_out.clone(), Vec::new());
}
dic_of_relations_out
.get_mut(&relation_id_out)
.expect("Key should be already be created.")
.push(relation.clone());
}
}
}
let padmet_object = PadmetSpec::new(
dic_of_relations_in,
dic_of_relations_out,
dic_of_nodes,
info,
);
Ok(padmet_object)
}
pub fn get_all_relations(&self) -> Vec<Relation> {
let mut all_relations: Vec<Relation> = Vec::new();
for relations in self.dic_of_relations_in.values() {
for relation in relations {
all_relations.push(relation.clone());
}
}
for relations in self.dic_of_relations_out.values() {
for relation in relations {
all_relations.push(relation.clone());
}
}
all_relations
}
pub fn get_relations_type_id_in(
&self,
relation_type: &String,
relation_id_in: &String,
) -> Vec<Relation> {
if let Some(relations) = self.dic_of_relations_in.get(relation_id_in) {
let relations_of_type: Vec<Relation> = relations
.iter()
.filter(|&r| r.relation_type == *relation_type)
.cloned()
.collect();
return relations_of_type;
}
Vec::new()
}
pub fn get_relations_type_id_out(
&self,
relation_type: &String,
relation_id_out: &String,
) -> Vec<Relation> {
if let Some(relations) = self.dic_of_relations_out.get(relation_id_out) {
let relations_of_type: Vec<Relation> = relations
.iter()
.filter(|&r| r.relation_type == *relation_type)
.cloned()
.collect();
return relations_of_type;
}
Vec::new()
}
pub fn get_relations(
self,
relation_id_in: &String,
relation_type: &String,
relation_id_out: &String,
) -> Vec<Relation> {
if let Some(relations) = self.dic_of_relations_in.get(relation_id_in) {
let relations_filtered = relations
.iter()
.filter(|&r| r.relation_type == *relation_type && r.id_out == *relation_id_out)
.cloned()
.collect();
return relations_filtered;
}
Vec::new()
}
pub fn get_nodes_of_type(&self, node_type: &str) -> Vec<Node> {
self.dic_of_nodes
.values()
.filter(|node| node.node_type == node_type)
.cloned()
.collect()
}
pub fn get_compounds(&self) -> Vec<Node> {
self.get_nodes_of_type("compound")
}
pub fn get_reactions(&self) -> Vec<Node> {
self.get_nodes_of_type("reaction")
}
pub fn get_genes(&self) -> Vec<Node> {
self.get_nodes_of_type("gene")
}
pub fn get_pathways(&self) -> Vec<Node> {
self.get_nodes_of_type("pathway")
}
pub fn get_pathways_reactions(&self) -> HashMap<String, HashSet<String>> {
let pathways = self.get_pathways();
let mut pathway_to_reactions: HashMap<String, HashSet<String>> = HashMap::new();
for pathway in pathways {
let reactions_relations =
self.get_relations_type_id_out(&"is_in_pathway".to_owned(), &(pathway.node_id));
let reactions_id: Vec<String> = reactions_relations
.into_iter()
.map(|relation| relation.id_in)
.collect();
let reaction_set: HashSet<String> = HashSet::from_iter(reactions_id.iter().cloned());
pathway_to_reactions.insert(pathway.node_id, reaction_set);
}
pathway_to_reactions
}
}
#[cfg(test)]
mod tests {
use std::path::PathBuf;
use super::*;
#[test]
fn test_open_padmet() {
let padmet_test_file_1: PathBuf =
PathBuf::from("vendor/padmet/tests/test_data/padmet/padmet_1.padmet");
let padmet_object: PadmetSpec = PadmetSpec::from_file(padmet_test_file_1).unwrap();
let nodes = &padmet_object.dic_of_nodes;
dbg!(nodes);
let reactions = padmet_object.get_reactions();
assert!(reactions.len() > 0);
let pathways = padmet_object.get_pathways();
assert!(pathways.len() > 0);
}
#[test]
fn test_get_reactions_of_pathway() {
let padmet_test_file_1: PathBuf =
PathBuf::from("vendor/padmet/tests/test_data/padmet/padmet_1.padmet");
let padmet_object: PadmetSpec = PadmetSpec::from_file(padmet_test_file_1).unwrap();
let pathways = padmet_object.get_pathways();
let pathway_reactions = padmet_object.get_pathways_reactions();
assert_eq!(pathways.len(), pathway_reactions.len());
assert!(
pathway_reactions
.get("FAO-PWY")
.expect("pathway FAO-PWY should exist in the test padmet file")
.len()
> 0
);
}
}