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