padmet/
spec.rs

1//! The main object to access the data of a PADMet file
2
3/* std use */
4use std::collections::HashMap;
5use std::collections::HashSet;
6use std::io;
7use std::path::Path;
8
9/* crate use */
10
11/* project use */
12use crate::io::read_lines;
13use crate::node::Node;
14use crate::policy::Policy;
15use crate::relation::Relation;
16
17#[derive(Debug, Clone)]
18pub struct PadmetSpec {
19    pub dic_of_relations_in: HashMap<String, Vec<Relation>>,
20    pub dic_of_relations_out: HashMap<String, Vec<Relation>>,
21    pub dic_of_nodes: HashMap<String, Node>,
22    pub policy: Policy,
23    pub info: HashMap<String, HashMap<String, String>>,
24}
25
26#[derive(PartialEq)]
27pub enum PadmetSection {
28    Default,
29    Policy,
30    DatabaseInformation,
31    Nodes,
32    Relations,
33}
34
35impl PadmetSpec {
36    pub fn new(
37        dic_of_relations_in: HashMap<String, Vec<Relation>>,
38        dic_of_relations_out: HashMap<String, Vec<Relation>>,
39        dic_of_nodes: HashMap<String, Node>,
40        policy: Policy,
41        info: HashMap<String, HashMap<String, String>>,
42    ) -> Self {
43        PadmetSpec {
44            dic_of_relations_in,
45            dic_of_relations_out,
46            dic_of_nodes,
47            policy,
48            info,
49        }
50    }
51
52    pub fn from_file<P>(filename: P) -> io::Result<PadmetSpec>
53    where
54        P: AsRef<Path>,
55    {
56        let mut dic_of_relations_in: HashMap<String, Vec<Relation>> = HashMap::new();
57        let mut dic_of_relations_out: HashMap<String, Vec<Relation>> = HashMap::new();
58        let mut dic_of_nodes: HashMap<String, Node> = HashMap::new();
59        let mut policy: Policy = Policy::new();
60        let mut info: HashMap<String, HashMap<String, String>> = HashMap::new();
61
62        let mut padmet_section: PadmetSection = PadmetSection::Default;
63        let lines = read_lines(filename)?;
64        let mut current_data: Option<String> = None;
65        for line in lines.map_while(Result::ok) {
66            if !line.is_empty() {
67                // Check in which section of the PADMet file we are.
68                if line == "Data Base informations" {
69                    padmet_section = PadmetSection::DatabaseInformation;
70                } else if line == "Policy" {
71                    padmet_section = PadmetSection::Policy;
72                } else if line == "Nodes" {
73                    padmet_section = PadmetSection::Nodes;
74                } else if line == "Relations" {
75                    padmet_section = PadmetSection::Relations;
76                // Behave differently depending on the section
77                } else if padmet_section == PadmetSection::DatabaseInformation {
78                    if !line.contains("\t") {
79                        let line = line.replace(":", "");
80                        current_data = Some(line.clone());
81                        info.insert(line.clone(), HashMap::new());
82                    } else {
83                        let line = line.replace("\t", "");
84                        let mut parts = line.split(":");
85                        let key = parts.next().expect("Error parsing PADMet on Data Base informations section. Key not found.").to_owned();
86                        let value = parts.next().expect("Error parsing PADMet on Data Base informations section. Value not found.").to_owned();
87                        if let Some(ref data_key) = current_data {
88                            let hashmap = info
89                                .get_mut(data_key)
90                                .expect("Expect key to be already present in the info hashmap.");
91                            hashmap.insert(key, value);
92                        }
93                    }
94                } else if padmet_section == PadmetSection::Policy {
95                    // TODO
96                } else if padmet_section == PadmetSection::Nodes {
97                    let mut parts = line.split("\t");
98                    let node_type: String = parts.next().expect("Expect node_type").to_owned();
99                    let node_id: String = parts.next().expect("Expect node_id").to_owned();
100                    let mut node_misc: HashMap<String, Vec<String>> = HashMap::new();
101                    let misc_items: Vec<String> = parts.map(|e| e.to_owned()).collect();
102                    let mut i = 0;
103                    while i + 1 < misc_items.len() {
104                        let key = misc_items[i].clone();
105                        let value = misc_items[i + 1].clone();
106                        if let Some(misc_data) = node_misc.get_mut(&key) {
107                            misc_data.push(value);
108                        } else {
109                            node_misc.insert(key, vec![value]);
110                        }
111                        i += 2;
112                    }
113                    // Insert node
114                    let node = Node::new(node_type.clone(), node_id.clone(), node_misc);
115                    dic_of_nodes.insert(node_id.clone(), node);
116                } else if padmet_section == PadmetSection::Relations {
117                    let mut parts = line.split("\t");
118                    let relation_id_in: String =
119                        parts.next().expect("Expect relation in node").to_owned();
120                    let relation_type: String =
121                        parts.next().expect("Expect relation type").to_owned();
122                    let relation_id_out: String =
123                        parts.next().expect("Expect relation out node").to_owned();
124                    let mut relation_misc: HashMap<String, Vec<String>> = HashMap::new();
125                    let misc_items: Vec<String> = parts.map(|e| e.to_owned()).collect();
126                    let mut i = 0;
127                    while i + 1 < misc_items.len() {
128                        let key = &misc_items[i];
129                        let value = &misc_items[i + 1];
130                        if let Some(misc_data) = relation_misc.get_mut(key) {
131                            misc_data.push(value.clone());
132                        } else {
133                            relation_misc.insert(key.clone(), vec![value.clone()]);
134                        }
135                        i += 2;
136                    }
137                    // Insert relations
138                    let relation = Relation::new(
139                        relation_id_in.clone(),
140                        relation_id_out.clone(),
141                        relation_type,
142                        relation_misc,
143                    );
144
145                    if !dic_of_relations_in.contains_key(&relation_id_in) {
146                        dic_of_relations_in.insert(relation_id_in.clone(), Vec::new());
147                    }
148                    dic_of_relations_in
149                        .get_mut(&relation_id_in)
150                        .expect("Key should be already be created.")
151                        .push(relation.clone());
152                    if !dic_of_relations_out.contains_key(&relation_id_out) {
153                        dic_of_relations_out.insert(relation_id_out.clone(), Vec::new());
154                    }
155                    dic_of_relations_out
156                        .get_mut(&relation_id_out)
157                        .expect("Key should be already be created.")
158                        .push(relation.clone());
159                }
160            }
161        }
162
163        let padmet_object = PadmetSpec::new(
164            dic_of_relations_in,
165            dic_of_relations_out,
166            dic_of_nodes,
167            policy,
168            info,
169        );
170        Ok(padmet_object)
171    }
172
173    /// Get a set of all relations in the padmet object
174    pub fn get_all_relations(&self) -> Vec<Relation> {
175        let mut all_relations: Vec<Relation> = Vec::new();
176        for relations in self.dic_of_relations_in.values() {
177            for relation in relations {
178                all_relations.push(relation.clone());
179            }
180        }
181        for relations in self.dic_of_relations_out.values() {
182            for relation in relations {
183                all_relations.push(relation.clone());
184            }
185        }
186        // Deduplicate ?
187        all_relations
188    }
189
190    /// Get the list of relations of a certain type, coming from a certain node
191    pub fn get_relations_type_id_in(
192        &self,
193        relation_type: &String,
194        relation_id_in: &String,
195    ) -> Vec<Relation> {
196        if let Some(relations) = self.dic_of_relations_in.get(relation_id_in) {
197            let relations_of_type: Vec<Relation> = relations
198                .iter()
199                .filter(|&r| r.relation_type == *relation_type)
200                .cloned()
201                .collect();
202            return relations_of_type;
203        }
204        Vec::new()
205    }
206
207    /// Get the list of relations of a certain type, leading to a certain node
208    pub fn get_relations_type_id_out(
209        &self,
210        relation_type: &String,
211        relation_id_out: &String,
212    ) -> Vec<Relation> {
213        if let Some(relations) = self.dic_of_relations_out.get(relation_id_out) {
214            let relations_of_type: Vec<Relation> = relations
215                .iter()
216                .filter(|&r| r.relation_type == *relation_type)
217                .cloned()
218                .collect();
219            return relations_of_type;
220        }
221        Vec::new()
222    }
223
224    /// Get relation from relation_id_in to relation_id_out nodes and with a specific relation_type
225    pub fn get_relations(
226        self,
227        relation_id_in: &String,
228        relation_type: &String,
229        relation_id_out: &String,
230    ) -> Vec<Relation> {
231        if let Some(relations) = self.dic_of_relations_in.get(relation_id_in) {
232            let relations_filtered = relations
233                .iter()
234                .filter(|&r| r.relation_type == *relation_type && r.id_out == *relation_id_out)
235                .cloned()
236                .collect();
237            return relations_filtered;
238        }
239        Vec::new()
240    }
241
242    pub fn get_nodes_of_type(&self, node_type: &str) -> Vec<Node> {
243        self.dic_of_nodes
244            .values()
245            .filter(|node| node.node_type == node_type)
246            .cloned()
247            .collect()
248    }
249
250    /// Get all the compounds from a padmet instance.
251    pub fn get_compounds(&self) -> Vec<Node> {
252        self.get_nodes_of_type("compound")
253    }
254
255    /// Get all the reactions from a padmet instance
256    pub fn get_reactions(&self) -> Vec<Node> {
257        self.get_nodes_of_type("reaction")
258    }
259
260    /// Get all the reactions from a padmet instance
261    pub fn get_genes(&self) -> Vec<Node> {
262        self.get_nodes_of_type("gene")
263    }
264
265    /// Get all the pathways from a padmet instance
266    pub fn get_pathways(&self) -> Vec<Node> {
267        self.get_nodes_of_type("pathway")
268    }
269
270    /// Get all the pathways from a padmet instance with their set of reactions.
271    pub fn get_pathways_reactions(&self) -> HashMap<String, HashSet<String>> {
272        let pathways = self.get_pathways();
273        let mut pathway_to_reactions: HashMap<String, HashSet<String>> = HashMap::new();
274        for pathway in pathways {
275            let reactions_relations =
276                self.get_relations_type_id_out(&"is_in_pathway".to_owned(), &(pathway.node_id));
277            let reactions_id: Vec<String> = reactions_relations
278                .into_iter()
279                .map(|relation| relation.id_in)
280                .collect();
281            let reaction_set: HashSet<String> = HashSet::from_iter(reactions_id.iter().cloned());
282            pathway_to_reactions.insert(pathway.node_id, reaction_set);
283        }
284        pathway_to_reactions
285    }
286}
287
288#[cfg(test)]
289mod tests {
290
291    use std::path::PathBuf;
292
293    use super::*;
294
295    #[test]
296    fn test_open_padmet() {
297        let padmet_test_file_1: PathBuf =
298            PathBuf::from("vendor/padmet/tests/test_data/padmet/padmet_1.padmet");
299        let padmet_object: PadmetSpec = PadmetSpec::from_file(padmet_test_file_1).unwrap();
300        let nodes = &padmet_object.dic_of_nodes;
301        dbg!(nodes);
302        let reactions = padmet_object.get_reactions();
303        assert!(reactions.len() > 0);
304
305        let pathways = padmet_object.get_pathways();
306        assert!(pathways.len() > 0);
307    }
308    
309    #[test]
310    fn test_get_reactions_of_pathway() {
311                let padmet_test_file_1: PathBuf =
312            PathBuf::from("vendor/padmet/tests/test_data/padmet/padmet_1.padmet");
313        let padmet_object: PadmetSpec = PadmetSpec::from_file(padmet_test_file_1).unwrap();
314        let pathways = padmet_object.get_pathways();
315        let pathway_reactions = padmet_object.get_pathways_reactions();
316        assert_eq!(pathways.len(), pathway_reactions.len());
317        assert!(pathway_reactions.get("FAO-PWY").expect("pathway FAO-PWY should exist in the test padmet file").len() > 0);
318    }
319
320}