1use crate::ctx::Ctx;
2use crate::model::ModelManager;
3use crate::model::Result;
4use crate::model::clone::{Clone, CloneBmc, CloneFilter};
5use crate::model::protein::{Protein, ProteinBmc, ProteinFilter};
6use crate::model::species::{Species, SpeciesBmc, SpeciesFilter};
7use crate::model::validation::{MinValidation, ValidationBmc, ValidationFilter};
8use modql::filter::ListOptions;
9use serde::{Deserialize, Serialize};
10use serde_json::json;
11use std::collections::{HashMap, hash_map::Entry};
12
13#[derive(Serialize, Deserialize, Debug, Clone, Default)]
14pub struct ViewClone {
15 pub id: i32,
16 #[serde(rename = "createdBy")]
17 pub created_by: i32,
18 #[serde(rename = "updatedBy")]
19 pub updated_at: chrono::DateTime<chrono::Utc>,
20 #[serde(rename = "proteinId")]
21 pub protein_id: i32,
22 #[serde(rename = "speciesId")]
23 pub species_id: Option<i32>,
24 pub name: String,
25 pub isotype: Option<String>,
26 pub epitope: Option<String>,
27 #[serde(rename = "isPhospho")]
28 pub is_phospho: bool,
29 #[serde(rename = "isPolyclonal")]
30 pub is_polyclonal: bool,
31 pub reactivity: Option<Vec<i32>>,
32 pub application: Option<serde_json::Value>,
33 pub meta: Option<serde_json::Value>,
34 pub protein: Protein,
35 pub species: Option<Species>,
36 pub validations: Vec<MinValidation>,
37}
38
39pub struct ViewCloneBmc;
40
41impl ViewCloneBmc {
42 pub async fn get(ctx: &Ctx, mm: &ModelManager, id: i32) -> Result<ViewClone> {
43 let clone = CloneBmc::get(ctx, mm, id).await?;
44 let protein = ProteinBmc::get(ctx, mm, clone.protein_id).await?;
45 let species = match clone.species_id {
46 Some(sid) => Some(SpeciesBmc::get(ctx, mm, sid).await?),
47 None => None,
48 };
49 let ret = ViewClone {
50 id: clone.id,
51 application: clone.application,
52 created_by: clone.created_by,
53 epitope: clone.epitope,
54 is_phospho: clone.is_phospho,
55 updated_at: clone.updated_at,
56 protein_id: clone.protein_id,
57 species_id: clone.species_id,
58 name: clone.name,
59 isotype: clone.isotype,
60 is_polyclonal: clone.is_polyclonal,
61 reactivity: clone.reactivity,
62 meta: clone.meta,
63 protein,
64 species,
65 validations: vec![],
66 };
67 Ok(ret)
68 }
69
70 pub async fn list(
71 ctx: &Ctx,
72 mm: &ModelManager,
73 group_id: Option<i32>,
74 filters: Option<Vec<CloneFilter>>,
75 list_options: Option<ListOptions>,
76 ) -> Result<Vec<ViewClone>> {
77 let mut protein_map = HashMap::new();
78 let mut species_map = HashMap::new();
79 if let Some(group_id) = group_id {
80 let protein_filters: Option<Vec<ProteinFilter>> = match serde_json::from_value(json!([
81 {
82 "group_id": {"$eq":group_id}
83 }
84 ])) {
85 Ok(ok) => Some(ok),
86 Err(_) => None,
87 };
88 let protein_op = ListOptions {
89 limit: Some(10_000),
90 ..Default::default()
91 };
92 let proteins: Vec<Protein> =
93 ProteinBmc::list(ctx, mm, protein_filters, Some(protein_op)).await?;
94 for protein in proteins {
95 protein_map.insert(protein.id, protein);
96 }
97
98 let species_filters: Option<Vec<SpeciesFilter>> = match serde_json::from_value(json!([
99 {
100 "group_id": {"$eq": group_id}
101 }
102 ])) {
103 Ok(ok) => Some(ok),
104 Err(_) => None,
105 };
106 let speciess: Vec<Species> = SpeciesBmc::list(ctx, mm, species_filters, None).await?;
107 for species in speciess {
108 species_map.insert(species.id, species);
109 }
110 }
111
112 let clones: Vec<Clone> = CloneBmc::list(ctx, mm, filters, list_options).await?;
113 let filters: Option<Vec<ValidationFilter>> =
114 match serde_json::from_value(json!([{"group_id": { "$eq": group_id}}])) {
115 Ok(o) => Some(o),
116 Err(_) => None,
117 };
118 let valop = ListOptions {
119 limit: Some(10_000),
120 ..Default::default()
121 };
122 let mut val_map: HashMap<i32, Vec<MinValidation>> = HashMap::new();
123 let min_validations = ValidationBmc::minlist(ctx, mm, filters, Some(valop)).await?;
124 for val in min_validations {
125 let o = match val_map.entry(val.clone_id) {
126 Entry::Occupied(o) => o.into_mut(),
127 Entry::Vacant(v) => v.insert(vec![]),
128 };
129 o.push(val);
130 }
131 let mut returns = vec![];
132 for clone in clones {
133 let protein = match protein_map.get(&{ clone.protein_id }) {
134 Some(v) => v.clone(),
135 None => Protein {
136 id: 0,
137 group_id: 0,
138 created_at: chrono::offset::Utc::now(),
139 created_by: 0,
140 description: None,
141 ..Default::default()
142 },
143 };
144 let mut species = None;
145
146 if let Some(species_id) = &clone.species_id {
147 species = species_map.get(species_id).cloned();
148 }
149 let validations = match val_map.get(&clone.id) {
150 Some(s) => (*s).clone(),
151 None => vec![],
152 };
153 returns.push(ViewClone {
154 id: clone.id,
155 application: clone.application,
156 created_by: clone.created_by,
157 epitope: clone.epitope,
158 is_phospho: clone.is_phospho,
159 updated_at: clone.updated_at,
160 protein_id: clone.protein_id,
161 species_id: clone.species_id,
162 name: clone.name,
163 isotype: clone.isotype,
164 is_polyclonal: clone.is_polyclonal,
165 reactivity: clone.reactivity,
166 meta: clone.meta,
167 protein,
168 species,
169 validations,
170 });
171 }
172
173 Ok(returns)
174 }
175}
176
177#[cfg(test)]
178mod tests {
179 use super::*;
180 use anyhow::Result;
181
182 #[ignore]
183 #[tokio::test]
184 async fn test_view_clone_list_all_ok() -> Result<()> {
185 let mm = ModelManager::new().await?;
186 let ctx = Ctx::root_ctx();
187
188 let clones = ViewCloneBmc::list(&ctx, &mm, Some(1000), None, None).await?;
189
190 let _clones: Vec<ViewClone> = clones
191 .into_iter()
192 .filter(|t| t.name.starts_with("test_list_all_ok-clone"))
193 .collect();
194
195 Ok(())
196 }
197}