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)] 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}