airlab_lib/model/
view_validation.rs

1#![allow(clippy::module_inception)]
2use crate::ctx::Ctx;
3use crate::model::ModelManager;
4use crate::model::Result;
5use crate::model::clone::{CloneBmc, CloneFilter};
6use crate::model::conjugate::{Conjugate, ConjugateBmc, ConjugateFilter};
7use crate::model::lot::LotFilter;
8use crate::model::member::{Member, MemberBmc, MemberFilter};
9use crate::model::protein::ProteinBmc;
10use crate::model::species::{Species, SpeciesBmc, SpeciesFilter};
11use crate::model::user::{User, UserBmc};
12use crate::model::validation::{Validation, ValidationBmc, ValidationFilter};
13use crate::model::validation_file::{ValidationFileBmc, ValidationFileFilter};
14use crate::model::view_clone::ViewClone;
15use crate::model::view_clone::ViewCloneBmc;
16use crate::model::view_lot::{ViewLot, ViewLotBmc};
17use modql::filter::ListOptions;
18use serde::{Deserialize, Serialize};
19use serde_json::json;
20use std::collections::HashMap;
21
22#[derive(Serialize, Deserialize, Debug, Clone, Default)]
23pub struct MinUser {
24    id: i32,
25    name: String,
26    #[serde(rename = "isAdmin")]
27    is_admin: bool,
28}
29
30#[derive(Serialize, Deserialize, Debug, Clone, Default)]
31pub struct MinSpecies {
32    id: i32,
33    name: String,
34}
35
36#[derive(Serialize, Deserialize, Debug, Clone, Default)]
37pub struct MinValidationFile {
38    id: i32,
39    name: String,
40}
41
42#[derive(Serialize, Deserialize, Debug, Clone, Default)]
43pub struct MinLot {
44    id: i32,
45    name: String,
46}
47
48#[derive(Serialize, Deserialize, Debug, Clone, Default)]
49pub struct MinConjugate {
50    id: i32,
51    #[serde(rename = "tubeNumber")]
52    tube_number: i32,
53}
54
55#[derive(Serialize, Deserialize, Debug, Clone, Default)]
56pub struct MinProtein {
57    id: i32,
58    name: String,
59}
60
61#[derive(Serialize, Deserialize, Debug, Clone, Default)]
62pub struct MinClone {
63    id: i32,
64    name: String,
65    protein: MinProtein,
66}
67
68#[derive(Serialize, Deserialize, Debug, Clone, Default)]
69pub struct MinViewValidation {
70    pub id: i32,
71    #[serde(rename = "groupId")]
72    pub group_id: i32,
73    pub application: i32,
74    #[serde(rename = "positiveControl")]
75    pub positive_control: Option<String>,
76    #[serde(rename = "negativeControl")]
77    pub negative_control: Option<String>,
78    #[serde(rename = "incubationConditions")]
79    pub incubation_conditions: Option<String>,
80    pub concentration: Option<String>,
81    #[serde(rename = "concentrationUnit")]
82    pub concentration_unit: Option<String>,
83    pub tissue: Option<String>,
84    pub fixation: Option<i32>,
85    #[serde(rename = "fixationNotes")]
86    pub fixation_notes: Option<String>,
87    pub notes: Option<String>,
88    pub status: i32,
89    #[serde(rename = "antigenRetrievalTemperature")]
90    pub antigen_retrieval_temperature: Option<String>,
91    #[serde(rename = "antigenRetrievalTime")]
92    pub antigen_retrieval_time: Option<String>,
93    #[serde(rename = "antigenRetrievalType")]
94    pub antigen_retrieval_type: Option<String>,
95    pub saponin: Option<bool>,
96    #[serde(rename = "saponinConcentration")]
97    pub saponin_concentration: Option<String>,
98    #[serde(rename = "methanolTreatment")]
99    pub methanol_treatment: Option<bool>,
100    #[serde(rename = "methanolTreatmentConcentration")]
101    pub methanol_treatment_concentration: Option<String>,
102    #[serde(rename = "surfaceStaining")]
103    pub surface_staining: Option<bool>,
104    #[serde(rename = "surfaceStainingConcentration")]
105    pub surface_staining_concentration: Option<String>,
106    #[serde(rename = "createdAt")]
107    pub created_at: chrono::DateTime<chrono::Utc>,
108    pub clone: MinClone,
109    pub lot: Option<MinLot>,
110    pub user: MinUser,
111    pub species: Option<MinSpecies>,
112    pub conjugate: Option<MinConjugate>,
113    #[serde(rename = "validationFiles")]
114    pub validation_files: Vec<MinValidationFile>,
115}
116
117#[derive(Serialize, Deserialize, Debug, Clone, Default)]
118pub struct ViewValidation {
119    pub id: i32,
120    pub tissue: Option<String>,
121    pub notes: Option<String>,
122    pub clone: ViewClone,
123    #[serde(rename = "antigenRetrievalTemperature")]
124    pub antigen_retrieval_temperature: Option<String>,
125    #[serde(rename = "antigenRetrievalTime")]
126    pub antigen_retrieval_time: Option<String>,
127    #[serde(rename = "antigenRetrievalType")]
128    pub antigen_retrieval_type: Option<String>,
129    pub application: i32,
130    #[serde(rename = "cloneId")]
131    pub clone_id: i32,
132    pub concentration: Option<String>,
133    #[serde(rename = "concentrationUnit")]
134    pub concentration_unit: Option<String>,
135    #[serde(rename = "conjugateId")]
136    pub conjugate_id: Option<i32>,
137    #[serde(rename = "createdAt")]
138    pub created_at: chrono::DateTime<chrono::Utc>,
139    #[serde(rename = "createdBy")]
140    pub created_by: i32,
141    pub fixation: Option<i32>,
142    #[serde(rename = "fixationNotes")]
143    pub fixation_notes: Option<String>,
144    #[serde(rename = "incubationConditions")]
145    pub incubation_conditions: Option<String>,
146    #[serde(rename = "isArchived")]
147    pub is_archived: bool,
148    #[serde(rename = "lotId")]
149    pub lot_id: Option<i32>,
150    #[serde(rename = "fileId")]
151    pub file_id: Option<i32>,
152    #[serde(rename = "methanolTreatment")]
153    pub methanol_treatment: Option<bool>,
154    #[serde(rename = "methanolTreatmentConcentration")]
155    pub methanol_treatment_concentration: Option<String>,
156    #[serde(rename = "negativeControl")]
157    pub negative_control: Option<String>,
158    #[serde(rename = "positiveControl")]
159    pub positive_control: Option<String>,
160    pub saponin: Option<bool>,
161    #[serde(rename = "saponinConcentration")]
162    pub saponin_concentration: Option<String>,
163    #[serde(rename = "speciesId")]
164    pub species_id: Option<i32>,
165    pub status: i32,
166    #[serde(rename = "surfaceStaining")]
167    pub surface_staining: Option<bool>,
168    #[serde(rename = "surfaceStainingConcentration")]
169    pub surface_staining_concentration: Option<String>,
170    #[serde(rename = "updatedAt")]
171    pub updated_at: chrono::DateTime<chrono::Utc>,
172    pub lot: Option<ViewLot>,
173    pub conjugate: Option<Conjugate>,
174    pub species: Option<Species>,
175    pub user: Option<User>,
176}
177
178pub struct ViewValidationBmc;
179
180impl ViewValidationBmc {
181    pub async fn get_min(
182        ctx: &Ctx,
183        mm: &ModelManager,
184        user_id: i32,
185        id: i32,
186    ) -> Result<MinViewValidation> {
187        let item = ValidationBmc::get(ctx, mm, id).await?;
188        let user: User = UserBmc::get(ctx, mm, user_id).await?;
189        let clone = CloneBmc::get(ctx, mm, item.clone_id).await?;
190        let protein = ProteinBmc::get(ctx, mm, clone.protein_id).await?;
191        let species = match clone.species_id {
192            Some(sid) => {
193                let species = SpeciesBmc::get(ctx, mm, sid).await?;
194                Some(MinSpecies {
195                    id: species.id,
196                    name: species.name,
197                })
198            }
199            None => None,
200        };
201        let conjugate = match item.conjugate_id {
202            Some(cid) => {
203                let conj = ConjugateBmc::get(ctx, mm, cid).await?;
204                Some(MinConjugate {
205                    id: conj.id,
206                    tube_number: conj.tube_number,
207                })
208            }
209            None => None,
210        };
211        let lot = match item.lot_id {
212            Some(lid) => {
213                let lot = ViewLotBmc::get(ctx, mm, lid).await?;
214                Some(MinLot {
215                    id: lot.id,
216                    name: lot.name,
217                })
218            }
219            None => None,
220        };
221        let filters: Vec<ValidationFileFilter> =
222            serde_json::from_value(json!([{"validation_id": {"$eq": item.id}}])).unwrap_or(vec![]);
223        let op = ListOptions {
224            limit: Some(10_000),
225            ..Default::default()
226        };
227        let val_files = ValidationFileBmc::list(ctx, mm, Some(filters), Some(op)).await?;
228        let validation_files = val_files
229            .into_iter()
230            .map(|e| MinValidationFile {
231                id: e.id,
232                name: e.name.unwrap_or_default(),
233            })
234            .collect();
235        let ret = MinViewValidation {
236            id: item.id,
237            group_id: item.group_id,
238            tissue: item.tissue,
239            notes: item.notes,
240            clone: MinClone {
241                id: clone.id,
242                name: clone.name.clone(),
243                protein: MinProtein {
244                    id: protein.id,
245                    name: protein.name.clone(),
246                },
247            },
248            antigen_retrieval_time: item.antigen_retrieval_time,
249            antigen_retrieval_type: item.antigen_retrieval_type,
250            antigen_retrieval_temperature: item.antigen_retrieval_temperature,
251            application: item.application,
252            concentration: item.concentration,
253            concentration_unit: item.concentration_unit,
254            created_at: item.created_at,
255            fixation: item.fixation,
256            fixation_notes: item.fixation_notes,
257            incubation_conditions: item.incubation_conditions,
258            methanol_treatment: item.methanol_treatment,
259            methanol_treatment_concentration: item.methanol_treatment_concentration,
260            negative_control: item.negative_control,
261            positive_control: item.positive_control,
262            saponin: item.saponin,
263            saponin_concentration: item.saponin_concentration,
264            status: item.status,
265            surface_staining: item.surface_staining,
266            surface_staining_concentration: item.surface_staining_concentration,
267            user: MinUser {
268                id: user.id,
269                name: user.name.clone().unwrap_or_default(),
270                is_admin: user.is_admin,
271            },
272            species,
273            conjugate,
274            lot,
275            validation_files,
276        };
277
278        Ok(ret)
279    }
280    pub async fn get(ctx: &Ctx, mm: &ModelManager, id: i32) -> Result<ViewValidation> {
281        let item = ValidationBmc::get(ctx, mm, id).await?;
282        let clone = ViewCloneBmc::get(ctx, mm, item.clone_id).await?;
283        let species = match clone.species_id {
284            Some(sid) => Some(SpeciesBmc::get(ctx, mm, sid).await?),
285            None => None,
286        };
287        let conjugate = match item.conjugate_id {
288            Some(cid) => Some(ConjugateBmc::get(ctx, mm, cid).await?),
289            None => None,
290        };
291        let lot = match item.lot_id {
292            Some(lid) => Some(ViewLotBmc::get(ctx, mm, lid).await?),
293            None => None,
294        };
295        let ret = ViewValidation {
296            id: item.id,
297            tissue: item.tissue,
298            notes: item.notes,
299            clone,
300            antigen_retrieval_time: item.antigen_retrieval_time,
301            antigen_retrieval_type: item.antigen_retrieval_type,
302            antigen_retrieval_temperature: item.antigen_retrieval_temperature,
303            application: item.application,
304            clone_id: item.clone_id,
305            concentration: item.concentration,
306            concentration_unit: item.concentration_unit,
307            conjugate_id: item.conjugate_id,
308            created_at: item.created_at,
309            created_by: item.created_by,
310            fixation: item.fixation,
311            fixation_notes: item.fixation_notes,
312            incubation_conditions: item.incubation_conditions,
313            is_archived: item.is_archived,
314            lot_id: item.lot_id,
315            file_id: item.file_id,
316            methanol_treatment: item.methanol_treatment,
317            methanol_treatment_concentration: item.methanol_treatment_concentration,
318            negative_control: item.negative_control,
319            positive_control: item.positive_control,
320            saponin: item.saponin,
321            saponin_concentration: item.saponin_concentration,
322            species_id: item.species_id,
323            status: item.status,
324            surface_staining: item.surface_staining,
325            surface_staining_concentration: item.surface_staining_concentration,
326            updated_at: item.updated_at,
327            user: None,
328            species,
329            conjugate,
330            lot,
331        };
332
333        Ok(ret)
334    }
335    #[allow(clippy::too_many_lines)] // FIXME
336    pub async fn list(
337        ctx: &Ctx,
338        mm: &ModelManager,
339        group_id: i32,
340        filters: Option<Vec<ValidationFilter>>,
341        list_options: Option<ListOptions>,
342    ) -> Result<Vec<ViewValidation>> {
343        let mut clone_map = HashMap::new();
344        let clone_filters: Option<Vec<CloneFilter>> = match serde_json::from_value(json!([
345            {
346                "group_id": {"$eq":group_id}
347            }
348        ])) {
349            Ok(ok) => Some(ok),
350            Err(_) => None,
351        };
352        let op = ListOptions {
353            limit: Some(10_000),
354            ..Default::default()
355        };
356        let clones: Vec<ViewClone> =
357            ViewCloneBmc::list(ctx, mm, Some(group_id), clone_filters, Some(op)).await?;
358        for clone in clones {
359            clone_map.insert(clone.id, clone);
360        }
361
362        let mut species_map = HashMap::new();
363        let species_filters: Option<Vec<SpeciesFilter>> = match serde_json::from_value(json!([
364            {
365                "group_id": {"$eq":group_id}
366            }
367        ])) {
368            Ok(ok) => Some(ok),
369            Err(_) => None,
370        };
371        let op = ListOptions {
372            limit: Some(10_000),
373            ..Default::default()
374        };
375        let speciess: Vec<Species> = SpeciesBmc::list(ctx, mm, species_filters, Some(op)).await?;
376        for species in speciess {
377            species_map.insert(species.id, species);
378        }
379
380        let mut conjugate_map = HashMap::new();
381        let conjugate_filters: Option<Vec<ConjugateFilter>> = match serde_json::from_value(json!([
382            {
383                "group_id": {"$eq":group_id}
384            }
385        ])) {
386            Ok(ok) => Some(ok),
387            Err(_) => None,
388        };
389        let op = ListOptions {
390            limit: Some(10_000),
391            ..Default::default()
392        };
393        let conjugates: Vec<Conjugate> =
394            ConjugateBmc::list(ctx, mm, conjugate_filters, Some(op)).await?;
395        for conjugate in conjugates {
396            conjugate_map.insert(conjugate.id, conjugate);
397        }
398
399        let mut lot_map = HashMap::new();
400        let mut user_map = HashMap::new();
401        let lot_filters: Option<Vec<LotFilter>> = match serde_json::from_value(json!([
402            {
403                "group_id": {"$eq":group_id}
404            }
405        ])) {
406            Ok(ok) => Some(ok),
407            Err(_) => None,
408        };
409        let op = ListOptions {
410            limit: Some(10_000),
411            ..Default::default()
412        };
413        let lots: Vec<ViewLot> =
414            ViewLotBmc::list(ctx, mm, Some(group_id), lot_filters, Some(op)).await?;
415        for lot in lots {
416            lot_map.insert(lot.id, lot);
417        }
418
419        let mem_op = ListOptions {
420            limit: Some(10_000),
421            ..Default::default()
422        };
423        let mem_filters: Option<Vec<MemberFilter>> =
424            match serde_json::from_value(json!([{"group_id": {"$eq": group_id}}])) {
425                Ok(o) => Some(o),
426                Err(_) => None,
427            };
428        let members: Vec<Member> = MemberBmc::list(ctx, mm, mem_filters, Some(mem_op)).await?;
429        let mem_map: HashMap<i32, i32> = members.iter().map(|e| (e.user_id, e.id)).collect();
430
431        let op = ListOptions {
432            limit: Some(10_000),
433            ..Default::default()
434        };
435        let users: Vec<User> = UserBmc::list(ctx, mm, None, Some(op)).await?;
436        for user in users {
437            let member_id = mem_map.get(&user.id).unwrap_or(&0);
438            user_map.insert(*member_id, user);
439        }
440
441        let validations: Vec<Validation> =
442            ValidationBmc::list(ctx, mm, filters, list_options).await?;
443        let mut returns = vec![];
444        for item in validations {
445            let clone = match clone_map.get(&{ item.clone_id }) {
446                Some(v) => v.clone(),
447                None => ViewClone::default(),
448            };
449            let mut species = None;
450            if let Some(species_id) = &item.species_id {
451                species = species_map.get(species_id).cloned();
452            };
453
454            let mut conjugate = None;
455            if let Some(conjugate_id) = &item.conjugate_id {
456                conjugate = conjugate_map.get(conjugate_id).cloned();
457            };
458
459            let mut lot = None;
460            if let Some(lot_id) = &item.lot_id {
461                lot = lot_map.get(lot_id).cloned();
462            };
463
464            let user = match user_map.get(&item.created_by) {
465                Some(v) => v.clone(),
466                None => User::default(),
467            };
468
469            returns.push(ViewValidation {
470                id: item.id,
471                tissue: item.tissue,
472                notes: item.notes,
473                clone,
474                antigen_retrieval_time: item.antigen_retrieval_time,
475                antigen_retrieval_type: item.antigen_retrieval_type,
476                antigen_retrieval_temperature: item.antigen_retrieval_temperature,
477                application: item.application,
478                clone_id: item.clone_id,
479                concentration: item.concentration,
480                concentration_unit: item.concentration_unit,
481                conjugate_id: item.conjugate_id,
482                created_at: item.created_at,
483                created_by: item.created_by,
484                fixation: item.fixation,
485                fixation_notes: item.fixation_notes,
486                incubation_conditions: item.incubation_conditions,
487                is_archived: item.is_archived,
488                lot_id: item.lot_id,
489                file_id: item.file_id,
490                methanol_treatment: item.methanol_treatment,
491                methanol_treatment_concentration: item.methanol_treatment_concentration,
492                negative_control: item.negative_control,
493                positive_control: item.positive_control,
494                saponin: item.saponin,
495                saponin_concentration: item.saponin_concentration,
496                species_id: item.species_id,
497                status: item.status,
498                surface_staining: item.surface_staining,
499                surface_staining_concentration: item.surface_staining_concentration,
500                updated_at: item.updated_at,
501                user: Some(user),
502                species,
503                conjugate,
504                lot,
505            });
506        }
507
508        Ok(returns)
509    }
510}
511
512#[cfg(test)]
513mod tests {
514    use super::*;
515    use anyhow::Result;
516
517    #[ignore]
518    #[tokio::test]
519    async fn test_view_validation_list_all_ok() -> Result<()> {
520        let mm = ModelManager::new().await?;
521        let ctx = Ctx::root_ctx();
522
523        let validations = ViewValidationBmc::list(&ctx, &mm, 1000, None, None).await?;
524
525        let _validations: Vec<ViewValidation> = validations
526            .into_iter()
527            .filter(|t| {
528                t.tissue
529                    .as_ref()
530                    .unwrap_or(&String::new())
531                    .starts_with("test_list_all_ok-validation")
532            })
533            .collect();
534
535        Ok(())
536    }
537}