diff --git a/src/lib.rs b/src/lib.rs index bbc6a1a..a64a3db 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -10,6 +10,7 @@ use std::{ use crate::errors::Error; use crate::native::{ + common::{Category, Comment, Entry, Field, LockState, Reason, State, Value}, site_native::SiteNative, subject_native::{Form, Patient, SubjectNative}, user_native::UserNative, @@ -135,6 +136,7 @@ pub fn parse_site_native_file(xml_path: &Path) -> Result { /// form_index: 1, /// form_group: Some("Demographic".to_string()), /// form_state: "In-Work".to_string(), +/// lock_state: None, /// states: Some(vec![State { /// value: "form.state.in.work".to_string(), /// signer: "Paul Sanders - Project Manager".to_string(), @@ -355,6 +357,7 @@ pub fn parse_site_native_file(xml_path: &Path) -> Result { /// form_index: 1, /// form_group: Some("Demographic".to_string()), /// form_state: "In-Work".to_string(), +/// lock_state: None, /// states: Some(vec![State { /// value: "form.state.in.work".to_string(), /// signer: "Paul Sanders - Project Manager".to_string(), @@ -446,6 +449,7 @@ pub fn parse_subject_native_file(xml_path: &Path) -> Result @@ -506,6 +510,7 @@ pub fn parse_subject_native_file(xml_path: &Path) -> Result Result Result(reader: R) -> Result { let mut xml_reader = Reader::from_reader(reader); xml_reader.config_mut().trim_text(true); @@ -837,6 +841,13 @@ fn parse_subject_native_streaming(reader: R) -> Result { + let attrs = extract_attributes(e)?; + let lock_state = LockState::from_attributes(attrs)?; + if let Some(ref mut form) = current_form { + form.lock_state = Some(lock_state); + } + } "value" if in_entry => { let attrs = extract_attributes(e)?; let value = Value::from_attributes(attrs)?; @@ -964,6 +975,7 @@ pub fn parse_user_native_file(xml_path: &Path) -> Result { /// form_index: 1, /// form_group: None, /// form_state: "In-Work".to_string(), +/// lock_state: None, /// states: Some(vec![State { /// value: "form.state.in.work".to_string(), /// signer: "Paul Sanders - Project Manager".to_string(), diff --git a/src/native/common.rs b/src/native/common.rs index 540d710..f6040a5 100644 --- a/src/native/common.rs +++ b/src/native/common.rs @@ -674,6 +674,7 @@ impl Form { form_group, form_state, states: None, + lock_state: None, categories: None, }) } @@ -780,6 +781,116 @@ impl State { } } +#[cfg(not(feature = "python"))] +#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)] +pub struct LockState { + #[serde(rename = "locked")] + #[serde(alias = "@locked")] + #[serde(alias = "locked")] + pub locked: bool, + + #[serde(rename = "user")] + #[serde(alias = "@user")] + #[serde(alias = "user")] + #[serde( + default = "default_string_none", + deserialize_with = "deserialize_empty_string_as_none" + )] + pub user: Option, + + #[serde(rename = "userUniqueId")] + #[serde(alias = "@userUniqueId")] + #[serde(alias = "userUniqueId")] + #[serde( + default = "default_string_none", + deserialize_with = "deserialize_empty_string_as_none" + )] + pub user_unique_id: Option, + + #[serde(rename = "dateTimeChanged")] + #[serde(alias = "@dateTimeChanged")] + #[serde(alias = "dateTimeChanged")] + #[serde( + default = "default_datetime_none", + deserialize_with = "deserialize_empty_string_as_none_datetime" + )] + pub date_time_changed: Option>, +} + +#[cfg(feature = "python")] +#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)] +#[pyclass] +pub struct LockState { + #[serde(rename = "locked")] + #[serde(alias = "@locked")] + #[serde(alias = "locked")] + pub locked: bool, + + #[serde(rename = "user")] + #[serde(alias = "@user")] + #[serde(alias = "user")] + #[serde( + default = "default_string_none", + deserialize_with = "deserialize_empty_string_as_none" + )] + pub user: Option, + + #[serde(rename = "userUniqueId")] + #[serde(alias = "@userUniqueId")] + #[serde(alias = "userUniqueId")] + #[serde( + default = "default_string_none", + deserialize_with = "deserialize_empty_string_as_none" + )] + pub user_unique_id: Option, + + #[serde(rename = "dateTimeChanged")] + #[serde(alias = "@dateTimeChanged")] + #[serde(alias = "dateTimeChanged")] + #[serde( + default = "default_datetime_none", + deserialize_with = "deserialize_empty_string_as_none_datetime" + )] + pub date_time_changed: Option>, +} + +#[cfg(feature = "python")] +#[pymethods] +impl LockState { + #[getter] + fn locked(&self) -> PyResult { + Ok(self.locked) + } + + #[getter] + fn user(&self) -> PyResult> { + Ok(self.user.clone()) + } + + #[getter] + fn user_unique_id(&self) -> PyResult> { + Ok(self.user_unique_id.clone()) + } + + #[getter] + fn date_time_changed<'py>(&self, py: Python<'py>) -> PyResult>> { + to_py_datetime_option(py, &self.date_time_changed) + } + + pub fn to_dict<'py>(&self, py: Python<'py>) -> PyResult> { + let dict = PyDict::new(py); + dict.set_item("locked", self.locked)?; + dict.set_item("user", &self.user)?; + dict.set_item("user_unique_id", &self.user_unique_id)?; + dict.set_item( + "date_time_changed", + to_py_datetime_option(py, &self.date_time_changed)?, + )?; + + Ok(dict) + } +} + #[cfg(not(feature = "python"))] #[derive(Clone, Debug, Deserialize, Serialize, PartialEq)] pub struct Form { @@ -876,6 +987,9 @@ pub struct Form { #[serde(alias = "state")] pub states: Option>, + #[serde(alias = "lockState")] + pub lock_state: Option, + #[serde(alias = "category")] pub categories: Option>, } @@ -977,6 +1091,9 @@ pub struct Form { #[serde(alias = "state")] pub states: Option>, + #[serde(alias = "lockState")] + pub lock_state: Option, + #[serde(alias = "category")] pub categories: Option>, } @@ -1059,6 +1176,11 @@ impl Form { Ok(self.states.clone()) } + #[getter] + fn lock_state(&self) -> PyResult> { + Ok(self.lock_state.clone()) + } + #[getter] fn categories(&self) -> PyResult>> { Ok(self.categories.clone()) @@ -1098,6 +1220,12 @@ impl Form { dict.set_item("states", py.None())?; } + if let Some(lock_state) = &self.lock_state { + dict.set_item("lock_state", lock_state.to_dict(py)?)?; + } else { + dict.set_item("lock_state", py.None())?; + } + if let Some(categories) = &self.categories { let mut category_dicts = Vec::new(); for category in categories { @@ -1140,6 +1268,33 @@ impl State { } } +impl LockState { + pub fn from_attributes( + attrs: std::collections::HashMap, + ) -> Result { + let locked = attrs.get("locked").map(|s| s == "true").unwrap_or(false); + let user = attrs.get("user").filter(|s| !s.is_empty()).cloned(); + let user_unique_id = attrs.get("userUniqueId").filter(|s| !s.is_empty()).cloned(); + + let date_time_changed = if let Some(dtc) = attrs.get("dateTimeChanged") { + if dtc.is_empty() { + None + } else { + parse_datetime_internal(dtc).ok() + } + } else { + None + }; + + Ok(LockState { + locked, + user, + user_unique_id, + date_time_changed, + }) + } +} + impl Category { pub fn from_attributes( attrs: std::collections::HashMap, diff --git a/src/native/snapshots/prelude_xml_parser__native__subject_native__tests__deserialize_subject_native_json.snap b/src/native/snapshots/prelude_xml_parser__native__subject_native__tests__deserialize_subject_native_json.snap index 22db3b7..9ecd936 100644 --- a/src/native/snapshots/prelude_xml_parser__native__subject_native__tests__deserialize_subject_native_json.snap +++ b/src/native/snapshots/prelude_xml_parser__native__subject_native__tests__deserialize_subject_native_json.snap @@ -31,6 +31,7 @@ patients: signer: Paul Sanders - Project Manager signerUniqueId: "1681162687395" dateSigned: "2023-04-15T16:09:02Z" + lock_state: ~ categories: - name: Demographics categoryType: normal @@ -80,6 +81,7 @@ patients: signer: Paul Sanders - Project Manager signerUniqueId: "1681162687395" dateSigned: "2023-04-16T16:10:02Z" + lock_state: ~ categories: - name: Demographics categoryType: normal diff --git a/src/native/snapshots/prelude_xml_parser__native__user_native__tests__deserialize_user_native_json.snap b/src/native/snapshots/prelude_xml_parser__native__user_native__tests__deserialize_user_native_json.snap index 018d7ea..9d99ad8 100644 --- a/src/native/snapshots/prelude_xml_parser__native__user_native__tests__deserialize_user_native_json.snap +++ b/src/native/snapshots/prelude_xml_parser__native__user_native__tests__deserialize_user_native_json.snap @@ -27,6 +27,7 @@ users: signer: Paul Sanders - Project Manager signerUniqueId: "1681162687395" dateSigned: "2023-08-07T15:15:41Z" + lock_state: ~ categories: - name: demographics categoryType: normal diff --git a/tests/assets/subject_native.xml b/tests/assets/subject_native.xml index b233a0b..a87afd6 100644 --- a/tests/assets/subject_native.xml +++ b/tests/assets/subject_native.xml @@ -4,6 +4,7 @@
+