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