Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 14 additions & 2 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -135,6 +136,7 @@ pub fn parse_site_native_file(xml_path: &Path) -> Result<SiteNative, Error> {
/// 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(),
Expand Down Expand Up @@ -355,6 +357,7 @@ pub fn parse_site_native_file(xml_path: &Path) -> Result<SiteNative, Error> {
/// 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(),
Expand Down Expand Up @@ -446,6 +449,7 @@ pub fn parse_subject_native_file(xml_path: &Path) -> Result<SubjectNative, Error
/// ```
/// use chrono::{DateTime, Utc};
/// use prelude_xml_parser::parse_subject_native_string;
/// use prelude_xml_parser::native::common::LockState;
/// use prelude_xml_parser::native::subject_native::*;
///
/// let xml = r#"<export_from_vision_EDC date="30-May-2024 10:35 -0500" createdBy="Paul Sanders" role="Project Manager" numberSubjectsProcessed="4">
Expand Down Expand Up @@ -506,6 +510,7 @@ pub fn parse_subject_native_file(xml_path: &Path) -> Result<SubjectNative, Error
/// form_index: 1,
/// form_group: Some("Day 0".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(),
Expand Down Expand Up @@ -575,6 +580,7 @@ pub fn parse_subject_native_file(xml_path: &Path) -> Result<SubjectNative, Error
/// form_index: 1,
/// form_group: Some("Day 0".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(),
Expand Down Expand Up @@ -626,8 +632,6 @@ pub fn parse_subject_native_string(xml_str: &str) -> Result<SubjectNative, Error
parse_subject_native_streaming(Cursor::new(xml_str.as_bytes()))
}

use crate::native::common::{Category, Comment, Entry, Field, Reason, State, Value};

fn parse_subject_native_streaming<R: std::io::BufRead>(reader: R) -> Result<SubjectNative, Error> {
let mut xml_reader = Reader::from_reader(reader);
xml_reader.config_mut().trim_text(true);
Expand Down Expand Up @@ -837,6 +841,13 @@ fn parse_subject_native_streaming<R: std::io::BufRead>(reader: R) -> Result<Subj
let state = State::from_attributes(attrs)?;
current_states.push(state);
}
"lockState" if in_form => {
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)?;
Expand Down Expand Up @@ -964,6 +975,7 @@ pub fn parse_user_native_file(xml_path: &Path) -> Result<UserNative, Error> {
/// 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(),
Expand Down
155 changes: 155 additions & 0 deletions src/native/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -674,6 +674,7 @@ impl Form {
form_group,
form_state,
states: None,
lock_state: None,
categories: None,
})
}
Expand Down Expand Up @@ -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<String>,

#[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<String>,

#[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<DateTime<Utc>>,
}

#[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<String>,

#[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<String>,

#[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<DateTime<Utc>>,
}

#[cfg(feature = "python")]
#[pymethods]
impl LockState {
#[getter]
fn locked(&self) -> PyResult<bool> {
Ok(self.locked)
}

#[getter]
fn user(&self) -> PyResult<Option<String>> {
Ok(self.user.clone())
}

#[getter]
fn user_unique_id(&self) -> PyResult<Option<String>> {
Ok(self.user_unique_id.clone())
}

#[getter]
fn date_time_changed<'py>(&self, py: Python<'py>) -> PyResult<Option<Bound<'py, PyDateTime>>> {
to_py_datetime_option(py, &self.date_time_changed)
}

pub fn to_dict<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyDict>> {
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 {
Expand Down Expand Up @@ -876,6 +987,9 @@ pub struct Form {
#[serde(alias = "state")]
pub states: Option<Vec<State>>,

#[serde(alias = "lockState")]
pub lock_state: Option<LockState>,

#[serde(alias = "category")]
pub categories: Option<Vec<Category>>,
}
Expand Down Expand Up @@ -977,6 +1091,9 @@ pub struct Form {
#[serde(alias = "state")]
pub states: Option<Vec<State>>,

#[serde(alias = "lockState")]
pub lock_state: Option<LockState>,

#[serde(alias = "category")]
pub categories: Option<Vec<Category>>,
}
Expand Down Expand Up @@ -1059,6 +1176,11 @@ impl Form {
Ok(self.states.clone())
}

#[getter]
fn lock_state(&self) -> PyResult<Option<LockState>> {
Ok(self.lock_state.clone())
}

#[getter]
fn categories(&self) -> PyResult<Option<Vec<Category>>> {
Ok(self.categories.clone())
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -1140,6 +1268,33 @@ impl State {
}
}

impl LockState {
pub fn from_attributes(
attrs: std::collections::HashMap<String, String>,
) -> Result<Self, crate::errors::Error> {
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<String, String>,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions tests/assets/subject_native.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
<patient patientId="ABC-001" uniqueId="1681574905819" whenCreated="2023-04-15 12:09:02 -0400" creator="Paul Sanders" siteName="Some Site" siteUniqueId="1681574834910" lastLanguage="" numberOfForms="6">
<form name="day.0.form.name.demographics" lastModified="2023-04-15 12:09:15 -0400" whoLastModifiedName="Paul Sanders" whoLastModifiedRole="Project Manager" whenCreated="1681574905839" hasErrors="false" hasWarnings="false" locked="false" user="" dateTimeChanged="" formTitle="Demographics" formIndex="1" formGroup="Day 0" formState="In-Work">
<state value="form.state.in.work" signer="Paul Sanders - Project Manager" signerUniqueId="1681162687395" dateSigned="2023-04-15 12:09:02 -0400" />
<lockState locked="true" user="Some User" userUniqueId="1630429016609" dateTimeChanged="2024-10-31 09:49:15 -0500" />
<category name="Demographics" type="normal" highestIndex="0">
<field name="breed" type="combo-box" dataType="string" errorCode="valid" whenCreated="2023-04-15 12:08:26 -0400" keepHistory="true">
<entry id="1">
Expand Down
Loading