1use crate::ctx::Ctx;
2use crate::model::ModelManager;
3use crate::model::Result;
4use crate::model::base::{self, DbBmc};
5use modql::field::Fields;
6use modql::filter::{FilterNodes, ListOptions, OpValsInt64};
7use serde::{Deserialize, Serialize};
8use sqlx::FromRow;
9
10impl PanelElementBmc {
11 #[must_use]
12 pub fn get_create_sql(drop_table: bool) -> String {
13 let table = Self::TABLE;
14 format!(
15 r##"{}
16create table if not exists "{table}" (
17 id serial primary key,
18 panel_id integer NOT NULL,
19 conjugate_id integer NOT NULL,
20 dilution_type smallint DEFAULT 0 NOT NULL,
21 concentration real
22);
23ALTER TABLE ONLY panel_element
24 ADD CONSTRAINT "UQ_panel_element_panel_id_and_conjugate_id" UNIQUE (panel_id, conjugate_id);
25CREATE INDEX "IDX_panel_element_conjugate_id" ON panel_element USING btree (conjugate_id);
26CREATE INDEX "IDX_panel_element_panel_id" ON panel_element USING btree (panel_id);
27 "##,
28 if drop_table {
29 format!("drop table if exists {table};")
30 } else {
31 String::new()
32 }
33 )
34 }
35}
36
37#[derive(Debug, Clone, Fields, FromRow, Serialize, Deserialize, Default)]
38pub struct PanelElement {
39 pub id: i32,
40
41 #[serde(rename = "panelId")]
42 pub panel_id: i32,
43 #[serde(rename = "conjugateId")]
44 pub conjugate_id: i32,
45 #[serde(rename = "dilutionType")]
46 pub dilution_type: i16,
47 pub concentration: Option<f32>,
48}
49
50#[derive(Fields, Deserialize, Clone, Debug)]
51pub struct PanelElementForCreate {
52 #[serde(rename = "panelId")]
53 pub panel_id: i32,
54 #[serde(rename = "conjugateId")]
55 pub conjugate_id: i32,
56 #[serde(rename = "dilutionType")]
57 pub dilution_type: i32,
58 pub concentration: Option<f32>,
59}
60
61#[derive(Fields, Default, Deserialize, Debug)]
62pub struct PanelElementForUpdate {
63 #[serde(rename = "dilutionType")]
64 pub dilution_type: i32,
65 pub concentration: Option<f32>,
66}
67
68#[derive(FilterNodes, Deserialize, Default, Debug)]
69pub struct PanelElementFilter {
70 id: Option<OpValsInt64>,
71
72 panel_id: Option<OpValsInt64>,
73 conjugate_id: Option<OpValsInt64>,
74}
75
76pub struct PanelElementBmc;
77
78impl DbBmc for PanelElementBmc {
79 const TABLE: &'static str = "panel_element";
80}
81
82impl PanelElementBmc {
83 pub async fn create(
84 ctx: &Ctx,
85 mm: &ModelManager,
86 panel_element_c: PanelElementForCreate,
87 ) -> Result<i32> {
88 base::create::<Self, _>(ctx, mm, panel_element_c).await
89 }
90 pub async fn create_full(
91 ctx: &Ctx,
92 mm: &ModelManager,
93 panel_element_c: PanelElement,
94 ) -> Result<i32> {
95 base::create::<Self, _>(ctx, mm, panel_element_c).await
96 }
97
98 pub async fn get(ctx: &Ctx, mm: &ModelManager, id: i32) -> Result<PanelElement> {
99 base::get::<Self, _>(ctx, mm, id).await
100 }
101
102 pub async fn list(
103 ctx: &Ctx,
104 mm: &ModelManager,
105 filters: Option<Vec<PanelElementFilter>>,
106 list_options: Option<ListOptions>,
107 ) -> Result<Vec<PanelElement>> {
108 base::list::<Self, _, _>(ctx, mm, filters, list_options).await
109 }
110
111 pub async fn update(
112 ctx: &Ctx,
113 mm: &ModelManager,
114 id: i32,
115 panel_element_u: PanelElementForUpdate,
116 ) -> Result<()> {
117 base::update::<Self, _>(ctx, mm, id, panel_element_u).await
118 }
119
120 pub async fn delete(ctx: &Ctx, mm: &ModelManager, id: i32) -> Result<()> {
121 base::delete::<Self>(ctx, mm, id).await
122 }
123}
124
125#[cfg(test)]
126mod tests {
127 use super::*;
128 use crate::_dev_utils;
129 use crate::model::Error;
130 use anyhow::Result;
131 use serde_json::json;
132
133 #[ignore]
134 #[tokio::test]
135 async fn test_panel_element_create_ok() -> Result<()> {
136 let mm = ModelManager::new().await?;
137 let ctx = Ctx::root_ctx();
138 let _fx_name = "test_create_ok name";
139
140 let panel_element_c = PanelElementForCreate {
141 panel_id: 1815,
142 conjugate_id: 4292,
143 dilution_type: 1,
144 concentration: None,
145 };
146 let id = PanelElementBmc::create(&ctx, &mm, panel_element_c).await?;
147
148 let panel_element = PanelElementBmc::get(&ctx, &mm, id).await?;
149 assert_eq!(panel_element.id, 1);
150
151 PanelElementBmc::delete(&ctx, &mm, id).await?;
152
153 Ok(())
154 }
155
156 #[ignore]
157 #[tokio::test]
158 async fn test_panel_element_get_err_not_found() -> Result<()> {
159 let mm = ModelManager::new().await?;
160 let ctx = Ctx::root_ctx();
161 let fx_id = 100;
162
163 let res = PanelElementBmc::get(&ctx, &mm, fx_id).await;
164
165 assert!(
166 matches!(
167 res,
168 Err(Error::EntityNotFound {
169 entity: "panel_element",
170 id: 100
171 })
172 ),
173 "EntityNotFound not matching"
174 );
175
176 Ok(())
177 }
178
179 #[ignore]
180 #[tokio::test]
181 async fn test_panel_element_list_all_ok() -> Result<()> {
182 let mm = ModelManager::new().await?;
183 let ctx = Ctx::root_ctx();
184 let tname = "test_panel_element_list_all_ok";
185 let seeds = _dev_utils::get_panel_element_seed(tname);
186 _dev_utils::seed_panel_elements(&ctx, &mm, &seeds).await?;
187
188 let panel_elements = PanelElementBmc::list(&ctx, &mm, None, None).await?;
189
190 let panel_elements: Vec<PanelElement> =
191 panel_elements.into_iter().filter(|t| t.id == 1).collect();
192 assert_eq!(panel_elements.len(), 4, "number of seeded panel_elements.");
193
194 if false {
195 for panel_element in panel_elements.iter() {
196 PanelElementBmc::delete(&ctx, &mm, panel_element.id).await?;
197 }
198 }
199
200 Ok(())
201 }
202
203 #[ignore]
204 #[tokio::test]
205 async fn test_panel_element_list_by_filter_ok() -> Result<()> {
206 let mm = ModelManager::new().await?;
207 let ctx = Ctx::root_ctx();
208 let tname = "test_panel_element_list_all_ok";
209 let seeds = _dev_utils::get_panel_element_seed(tname);
210 _dev_utils::seed_panel_elements(&ctx, &mm, &seeds).await?;
211
212 let filters: Vec<PanelElementFilter> = serde_json::from_value(json!([
213 {
214 "name": {
215 "$endsWith": ".a",
216 "$containsAny": ["01", "02"]
217 }
218 },
219 {
220 "name": {"$contains": "03"}
221 }
222 ]))?;
223 let list_options = serde_json::from_value(json!({
224 "order_bys": "!id"
225 }))?;
226 let panel_elements =
227 PanelElementBmc::list(&ctx, &mm, Some(filters), Some(list_options)).await?;
228
229 assert_eq!(panel_elements.len(), 3);
230 assert!(panel_elements[0].id == 3);
231
232 if false {
233 let panel_elements = PanelElementBmc::list(
234 &ctx,
235 &mm,
236 Some(serde_json::from_value(json!([{
237 "name": {"$startsWith": "test_list_by_filter_ok"}
238 }]))?),
239 None,
240 )
241 .await?;
242 assert_eq!(panel_elements.len(), 5);
243 for panel_element in panel_elements.iter() {
244 PanelElementBmc::delete(&ctx, &mm, panel_element.id).await?;
245 }
246 }
247
248 Ok(())
249 }
250
251 #[ignore]
252 #[tokio::test]
253 async fn test_panel_element_update_ok() -> Result<()> {
254 let mm = ModelManager::new().await?;
255 let ctx = Ctx::root_ctx();
256 let tname = "test_panel_element_list_all_ok";
257 let seeds = _dev_utils::get_panel_element_seed(tname);
258 let fx_panel_element = _dev_utils::seed_panel_elements(&ctx, &mm, &seeds)
259 .await?
260 .remove(0);
261
262 PanelElementBmc::update(
263 &ctx,
264 &mm,
265 fx_panel_element.id,
266 PanelElementForUpdate {
267 ..Default::default()
268 },
269 )
270 .await?;
271
272 let panel_element = PanelElementBmc::get(&ctx, &mm, fx_panel_element.id).await?;
273 assert_eq!(panel_element.id, 1);
274
275 Ok(())
276 }
277
278 #[ignore]
279 #[tokio::test]
280 async fn test_panel_element_delete_err_not_found() -> Result<()> {
281 let mm = ModelManager::new().await?;
282 let ctx = Ctx::root_ctx();
283 let fx_id = 100;
284
285 let res = PanelElementBmc::delete(&ctx, &mm, fx_id).await;
286
287 assert!(
288 matches!(
289 res,
290 Err(Error::EntityNotFound {
291 entity: "panel_element",
292 id: 100
293 })
294 ),
295 "EntityNotFound not matching"
296 );
297
298 Ok(())
299 }
300}