prelude_xml_parser/native/
user_native.rs1use std::sync::Arc;
2
3use serde::{Deserialize, Serialize};
4
5#[cfg(feature = "python")]
6use pyo3::{exceptions::PyValueError, prelude::*, types::PyDict};
7
8pub use crate::native::common::{
9 Category, Comment, Entry, Export, Field, Form, Reason, State, Value,
10};
11use quick_xml::events::BytesStart;
12
13use crate::native::deserializers::{
14 deserialize_empty_string_as_none, optional_string, required_attribute, visit_attributes,
15};
16
17#[cfg(not(feature = "python"))]
18#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
19#[serde(rename_all = "camelCase")]
20pub struct User {
21 #[serde(rename = "uniqueId")]
22 #[serde(alias = "@uniqueId")]
23 #[serde(alias = "uniqueId")]
24 pub unique_id: String,
25
26 #[serde(rename = "lastLanguage")]
27 #[serde(alias = "@lastLanguage")]
28 #[serde(alias = "lastLanguage")]
29 #[serde(default, deserialize_with = "deserialize_empty_string_as_none")]
30 pub last_language: Option<String>,
31 #[serde(rename = "creator")]
32 #[serde(alias = "@creator")]
33 #[serde(alias = "creator")]
34 pub creator: String,
35 #[serde(rename = "numberOfForms")]
36 #[serde(alias = "@numberOfForms")]
37 #[serde(alias = "numberOfForms")]
38 pub number_of_forms: usize,
39
40 #[serde(alias = "form")]
41 pub forms: Option<Arc<Vec<Form>>>,
42}
43
44#[cfg(not(feature = "python"))]
45impl User {
46 pub(crate) fn from_attributes(e: &BytesStart<'_>) -> Result<Self, crate::errors::Error> {
47 let mut unique_id: Option<&str> = None;
48 let mut last_language = "";
49 let mut creator: Option<&str> = None;
50 let mut number_of_forms = "";
51
52 visit_attributes(e, |key, attr| match key {
53 b"uniqueId" => unique_id = Some(attr),
54 b"lastLanguage" => last_language = attr,
55 b"creator" => creator = Some(attr),
56 b"numberOfForms" => number_of_forms = attr,
57 _ => {}
58 })?;
59
60 Ok(User {
61 unique_id: required_attribute(unique_id, "uniqueId")?,
62 last_language: optional_string(last_language),
63 creator: required_attribute(creator, "creator")?,
64 number_of_forms: number_of_forms.parse().unwrap_or(0),
65 forms: None,
66 })
67 }
68
69 pub(crate) fn set_forms(&mut self, forms: Vec<Form>) {
70 self.forms = if forms.is_empty() {
71 None
72 } else {
73 Some(Arc::new(forms))
74 };
75 }
76}
77
78#[cfg(feature = "python")]
79#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
80#[serde(rename_all = "camelCase")]
81#[pyclass(skip_from_py_object)]
82pub struct User {
83 #[serde(rename = "uniqueId")]
84 #[serde(alias = "@uniqueId")]
85 #[serde(alias = "uniqueId")]
86 pub unique_id: String,
87
88 #[serde(rename = "lastLanguage")]
89 #[serde(alias = "@lastLanguage")]
90 #[serde(alias = "lastLanguage")]
91 #[serde(default, deserialize_with = "deserialize_empty_string_as_none")]
92 pub last_language: Option<String>,
93 #[serde(rename = "creator")]
94 #[serde(alias = "@creator")]
95 #[serde(alias = "creator")]
96 pub creator: String,
97 #[serde(rename = "numberOfForms")]
98 #[serde(alias = "@numberOfForms")]
99 #[serde(alias = "numberOfForms")]
100 pub number_of_forms: usize,
101
102 #[serde(alias = "form")]
103 pub forms: Option<Arc<Vec<Form>>>,
104}
105
106#[cfg(feature = "python")]
107impl User {
108 pub(crate) fn from_attributes(e: &BytesStart<'_>) -> Result<Self, crate::errors::Error> {
109 let mut unique_id: Option<&str> = None;
110 let mut last_language = "";
111 let mut creator: Option<&str> = None;
112 let mut number_of_forms = "";
113
114 visit_attributes(e, |key, attr| match key {
115 b"uniqueId" => unique_id = Some(attr),
116 b"lastLanguage" => last_language = attr,
117 b"creator" => creator = Some(attr),
118 b"numberOfForms" => number_of_forms = attr,
119 _ => {}
120 })?;
121
122 Ok(User {
123 unique_id: required_attribute(unique_id, "uniqueId")?,
124 last_language: optional_string(last_language),
125 creator: required_attribute(creator, "creator")?,
126 number_of_forms: number_of_forms.parse().unwrap_or(0),
127 forms: None,
128 })
129 }
130
131 pub(crate) fn set_forms(&mut self, forms: Vec<Form>) {
132 self.forms = if forms.is_empty() {
133 None
134 } else {
135 Some(Arc::new(forms))
136 };
137 }
138}
139
140#[cfg(feature = "python")]
141#[pymethods]
142impl User {
143 #[getter]
144 fn unique_id(&self) -> PyResult<String> {
145 Ok(self.unique_id.clone())
146 }
147
148 #[getter]
149 fn last_language(&self) -> PyResult<Option<String>> {
150 Ok(self.last_language.clone())
151 }
152
153 #[getter]
154 fn creator(&self) -> PyResult<String> {
155 Ok(self.creator.clone())
156 }
157
158 #[getter]
159 fn number_of_forms(&self) -> PyResult<usize> {
160 Ok(self.number_of_forms)
161 }
162
163 #[getter]
164 fn forms(&self) -> PyResult<Option<Vec<Form>>> {
165 Ok(self.forms.as_deref().cloned())
166 }
167
168 pub fn to_dict<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyDict>> {
169 let dict = PyDict::new(py);
170 dict.set_item("unique_id", &self.unique_id)?;
171 dict.set_item("last_language", &self.last_language)?;
172 dict.set_item("creator", &self.creator)?;
173 dict.set_item("number_of_forms", self.number_of_forms)?;
174
175 let mut form_dicts = Vec::new();
176 if let Some(forms) = &self.forms {
177 for form in forms.iter() {
178 let form_dict = form.to_dict(py)?;
179 form_dicts.push(form_dict);
180 }
181 dict.set_item("forms", form_dicts)?;
182 } else {
183 dict.set_item("forms", py.None())?;
184 }
185
186 Ok(dict)
187 }
188}
189
190#[cfg(not(feature = "python"))]
191#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
193#[serde(rename_all = "camelCase")]
194pub struct UserNative {
195 #[serde(default)]
196 pub export: Option<Export>,
197
198 #[serde(alias = "user")]
199 pub users: Vec<User>,
200}
201
202#[cfg(not(feature = "python"))]
203impl UserNative {
204 pub fn to_json(&self) -> serde_json::Result<String> {
221 let json = serde_json::to_string(&self)?;
222
223 Ok(json)
224 }
225}
226
227#[cfg(feature = "python")]
228#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
230#[serde(rename_all = "camelCase")]
231#[pyclass(get_all, skip_from_py_object)]
232pub struct UserNative {
233 #[serde(default)]
234 pub export: Option<Export>,
235
236 #[serde(alias = "user")]
237 pub users: Vec<User>,
238}
239
240#[cfg(feature = "python")]
241#[pymethods]
242impl UserNative {
243 #[getter]
244 fn export(&self) -> PyResult<Option<Export>> {
245 Ok(self.export.clone())
246 }
247
248 #[getter]
249 fn users(&self) -> PyResult<Vec<User>> {
250 Ok(self.users.clone())
251 }
252
253 fn to_dict<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyDict>> {
255 let dict = PyDict::new(py);
256 let mut user_dicts = Vec::new();
257 for user in &self.users {
258 let user_dict = user.to_dict(py)?;
259 user_dicts.push(user_dict);
260 }
261 match &self.export {
262 Some(export) => dict.set_item("export", export.to_dict(py)?)?,
263 None => dict.set_item("export", py.None())?,
264 }
265 dict.set_item("users", user_dicts)?;
266 Ok(dict)
267 }
268
269 fn to_json(&self) -> PyResult<String> {
271 serde_json::to_string(&self)
272 .map_err(|_| PyErr::new::<PyValueError, _>("Error converting to JSON"))
273 }
274}
275
276#[cfg(test)]
277mod tests {
278 use super::*;
279 use insta::assert_yaml_snapshot;
280
281 #[test]
282 fn deserialize_user_native_json() {
283 let json_str = r#"{
284 "users": [
285 {
286 "uniqueId": "1691421275437",
287 "lastLanguage": null,
288 "creator": "Paul Sanders(1681162687395)",
289 "numberOfForms": 1,
290 "forms": [
291 {
292 "name": "form.name.demographics",
293 "lastModified": "2023-08-07T15:15:41Z",
294 "whoLastModifiedName": "Paul Sanders",
295 "whoLastModifiedRole": "Project Manager",
296 "whenCreated": 1691421341578,
297 "hasErrors": false,
298 "hasWarnings": false,
299 "locked": false,
300 "user": null,
301 "dateTimeChanged": null,
302 "formTitle": "User Demographics",
303 "formIndex": 1,
304 "formGroup": null,
305 "formState": "In-Work",
306 "states": [
307 {
308 "value": "form.state.in.work",
309 "signer": "Paul Sanders - Project Manager",
310 "signerUniqueId": "1681162687395",
311 "dateSigned": "2023-08-07T15:15:41Z"
312 }
313 ],
314 "categories": [
315 {
316 "name": "demographics",
317 "categoryType": "normal",
318 "highestIndex": 0,
319 "fields": [
320 {
321 "name": "address",
322 "fieldType": "text",
323 "dataType": "string",
324 "errorCode": "undefined",
325 "whenCreated": "2024-01-12T20:14:09Z",
326 "keepHistory": true,
327 "entries": null
328 },
329 {
330 "name": "email",
331 "fieldType": "text",
332 "dataType": "string",
333 "errorCode": "undefined",
334 "whenCreated": "2023-08-07T15:15:41Z",
335 "keepHistory": true,
336 "entries": [
337 {
338 "entryId": "1",
339 "value": {
340 "by": "Paul Sanders",
341 "byUniqueId": "1681162687395",
342 "role": "Project Manager",
343 "when": "2023-08-07T15:15:41Z",
344 "value": "jazz@artemis.com"
345 },
346 "reason": null
347 }
348 ]
349 }
350 ]
351 },
352 {
353 "name": "Administrative",
354 "categoryType": "normal",
355 "highestIndex": 0,
356 "fields": [
357 {
358 "name": "study_assignment",
359 "fieldType": "text",
360 "dataType": null,
361 "errorCode": "undefined",
362 "whenCreated": "2023-08-07T15:15:41Z",
363 "keepHistory": true,
364 "entries": [
365 {
366 "entryId": "1",
367 "value": {
368 "by": "set from calculation",
369 "byUniqueId": null,
370 "role": "System",
371 "when": "2023-08-07T15:15:41Z",
372 "value": "On 07-Aug-2023 10:15 -0500, Paul Sanders assigned user from another study"
373 },
374 "reason": {
375 "by": "set from calculation",
376 "byUniqueId": null,
377 "role": "System",
378 "when": "2023-08-07T15:15:41Z",
379 "value": "calculated value"
380 }
381 }
382 ]
383 }
384 ]
385 }
386 ]
387 }
388 ]
389 }
390 ]
391}
392
393 "#;
394
395 let result: UserNative = serde_json::from_str(json_str).unwrap();
396
397 assert_yaml_snapshot!(result);
398 }
399}