From 1416ee1bcbdcb4055ec34e8ef6b938ffe6c570ad Mon Sep 17 00:00:00 2001 From: Geoff Low Date: Wed, 30 Mar 2022 14:58:41 +0100 Subject: [PATCH 01/26] Added get test data function --- tests/__init__.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/tests/__init__.py b/tests/__init__.py index e69de29..3cc519c 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -0,0 +1,12 @@ + +import os + +def get_data_file_path(filename: str): + # strip off ./data prefix if it is there + if filename.startswith("./data"): + filename = os.path.split(filename)[-1] + target = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'data', filename) + if not os.path.exists(target): + raise FileNotFoundError(f"File {target} not found") + return target + \ No newline at end of file From fbf3440e311c14c299bd2a9b831908fc9cdfab9d Mon Sep 17 00:00:00 2001 From: Geoff Low Date: Wed, 30 Mar 2022 14:59:09 +0100 Subject: [PATCH 02/26] re-order classess so resolution works --- odmlib/odm_2_0/model.py | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/odmlib/odm_2_0/model.py b/odmlib/odm_2_0/model.py index 7df6319..400bd79 100644 --- a/odmlib/odm_2_0/model.py +++ b/odmlib/odm_2_0/model.py @@ -53,6 +53,17 @@ class Protocol(OE.ODMElement): Alias = T.ODMListObject(element_class=Alias) +class ItemGroupRef(OE.ODMElement): + ItemGroupOID = T.OID(required=True) + OrderNumber = T.Integer(required=False) + Mandatory = T.ValueSetString(required=True) + CollectionExceptionConditionOID = T.OIDRef() + + +class WorkflowRef(OE.ODMElement): + WorkflowOID = T.OID(required=True) + + class StudyEventDef(OE.ODMElement): """ represents ODM v2.0 StudyEventDef and can serialize as JSON or XML """ OID = T.OID(required=True) @@ -79,12 +90,6 @@ def __iter__(self): return iter(self.FormRef) -class ItemGroupRef(OE.ODMElement): - ItemGroupOID = T.OID(required=True) - OrderNumber = T.Integer(required=False) - Mandatory = T.ValueSetString(required=True) - CollectionExceptionConditionOID = T.OIDRef() - class ArchiveLayout(OE.ODMElement): OID = T.OID(required=True) @@ -330,8 +335,6 @@ class ExceptionEvent(OE.ODMElement): StudyEventRef = T.ODMListObject(element_class=StudyEventRef) -class WorkflowRef(OE.ODMElement): - WorkflowOID = T.OID(required=True) class Arm(OE.ODMElement): From 29b093aa4d7be69aa010e13ab7eba5e3116de1d1 Mon Sep 17 00:00:00 2001 From: Geoff Low Date: Tue, 5 Apr 2022 12:33:20 +0100 Subject: [PATCH 03/26] Renamed classes so TestFinder doesn't see them --- tests/test_descriptor.py | 8 +++--- tests/test_typed.py | 62 ++++++++++++++++++++-------------------- 2 files changed, 35 insertions(+), 35 deletions(-) diff --git a/tests/test_descriptor.py b/tests/test_descriptor.py index 57631f9..9670675 100644 --- a/tests/test_descriptor.py +++ b/tests/test_descriptor.py @@ -4,7 +4,7 @@ import odmlib.odm_element as OE -class TestText(OE.ODMElement): +class TextTest(OE.ODMElement): Name = T.String(required=True) OrderNumber = T.PositiveInteger(required=False) @@ -12,12 +12,12 @@ class TestText(OE.ODMElement): class TestDescriptor(unittest.TestCase): def test_assignment(self): - test = TestText(Name="test name", OrderNumber="1") + test = TextTest(Name="test name", OrderNumber="1") self.assertEqual(test.Name, "test name") with self.assertRaises(TypeError): test.OID = None - TestText.Name = "VariableOne" - self.assertEqual(TestText.Name, "VariableOne") + TextTest.Name = "VariableOne" + self.assertEqual(TextTest.Name, "VariableOne") def test_get_missing_attribute(self): igd = ODM.ItemGroupDef(OID="IG.VS", Name="Vital Signs", Repeating="Yes") diff --git a/tests/test_typed.py b/tests/test_typed.py index 1fdd8ac..5802d2c 100644 --- a/tests/test_typed.py +++ b/tests/test_typed.py @@ -6,7 +6,7 @@ import odmlib.odm_element as OE -class TestText(OE.ODMElement): +class TextTest(OE.ODMElement): Name = T.String(required=True) Label = T.Sized(required=True, max_length=4) Range = T.Regex(required=False, pat="[0-4]-[0-9]") @@ -16,7 +16,7 @@ class TestText(OE.ODMElement): TestTime = T.IncompleteTimeString(required=False) -class TestPartialDate(OE.ODMElement): +class PartialDateTest(OE.ODMElement): Name = T.String(required=True) TestDateTime = T.PartialDateTimeString(required=False) TestDate = T.PartialDateString(required=False) @@ -242,25 +242,25 @@ def test_string_type(self): self.assertIsNone(item.Origin) def test_sized_type(self): - sized = TestText(Name="AETERM", Label="help") + sized = TextTest(Name="AETERM", Label="help") self.assertEqual(sized.Label, "help") # too long with self.assertRaises(ValueError): - sized = TestText(Name="AETERM", Label="Verbatim Term") + sized = TextTest(Name="AETERM", Label="Verbatim Term") def test_regex_type(self): - regex = TestText(Name="AETERM", Label="help", Range="4-9") + regex = TextTest(Name="AETERM", Label="help", Range="4-9") self.assertEqual(regex.Range, "4-9") # not a match with self.assertRaises(ValueError): - regex = TestText(Name="AETERM", Label="help", Range="6-7") + regex = TextTest(Name="AETERM", Label="help", Range="6-7") def test_url_type(self): - url = TestText(Name="AETERM", Label="help", Range="4-9", Link="https://cdisc.org") + url = TextTest(Name="AETERM", Label="help", Range="4-9", Link="https://cdisc.org") self.assertEqual(url.Link, "https://cdisc.org") # not a match with self.assertRaises(ValueError): - url = TestText(Name="AETERM", Label="help", Range="4-9", Link="cdisc.org") + url = TextTest(Name="AETERM", Label="help", Range="4-9", Link="cdisc.org") def test_filename_type(self): archive = ODM.ArchiveLayout(OID="AL.AECRF", PdfFileName="ae_annotated_crf.pdf") @@ -277,70 +277,70 @@ def test_filename_type(self): def test_incomplete_datetime_type(self): # valid incomplete datetime 2004-12-01T01:01:01Z and 2004---15T-:05:- - idatetime = TestText(Name="AETERM", Label="help", Range="4-9", TestDateTime="2004-12-01T01:01:01Z") + idatetime = TextTest(Name="AETERM", Label="help", Range="4-9", TestDateTime="2004-12-01T01:01:01Z") self.assertEqual(idatetime.TestDateTime, "2004-12-01T01:01:01Z") - idatetime = TestText(Name="AETERM", Label="help", Range="4-9", TestDateTime="2004---15T-:05:-") + idatetime = TextTest(Name="AETERM", Label="help", Range="4-9", TestDateTime="2004---15T-:05:-") self.assertEqual(idatetime.TestDateTime, "2004---15T-:05:-") # invalid incomplete datetime with self.assertRaises(ValueError): - idatetime = TestText(Name="AETERM", Label="help", Range="4-7", TestDateTime="2004---15T-:05") + idatetime = TextTest(Name="AETERM", Label="help", Range="4-7", TestDateTime="2004---15T-:05") def test_incomplete_date_type(self): # valid incomplete date 2004-12-20 and 2004---15 and ------ - idate = TestText(Name="AETERM", Label="help", Range="4-9", TestDate="2004-12-20") + idate = TextTest(Name="AETERM", Label="help", Range="4-9", TestDate="2004-12-20") self.assertEqual(idate.TestDate, "2004-12-20") - idate = TestText(Name="AETERM", Label="help", Range="4-9", TestDate="2004---15") + idate = TextTest(Name="AETERM", Label="help", Range="4-9", TestDate="2004---15") self.assertEqual(idate.TestDate, "2004---15") - idate = TestText(Name="AETERM", Label="help", Range="4-7", TestDate="-----") + idate = TextTest(Name="AETERM", Label="help", Range="4-7", TestDate="-----") self.assertEqual(idate.TestDate, "-----") # invalid incomplete date with self.assertRaises(ValueError): - idate = TestText(Name="AETERM", Label="help", Range="4-7", TestDate="----32") + idate = TextTest(Name="AETERM", Label="help", Range="4-7", TestDate="----32") def test_incomplete_time_type(self): # valid incomplete time 2004-12-01T01:01:01Z and 2004---15T-:05:- - itime = TestText(Name="AETERM", Label="help", Range="4-9", TestTime="01:01:01Z") + itime = TextTest(Name="AETERM", Label="help", Range="4-9", TestTime="01:01:01Z") self.assertEqual(itime.TestTime, "01:01:01Z") - itime = TestText(Name="AETERM", Label="help", Range="4-9", TestTime="-:05:-") + itime = TextTest(Name="AETERM", Label="help", Range="4-9", TestTime="-:05:-") self.assertEqual(itime.TestTime, "-:05:-") # invalid incomplete datetime with self.assertRaises(ValueError): - itime = TestText(Name="AETERM", Label="help", Range="4-7", TestTime="-:05") + itime = TextTest(Name="AETERM", Label="help", Range="4-7", TestTime="-:05") def test_partial_datetime_type(self): # valid partial datetime - pdatetime = TestPartialDate(Name="AETERM", TestDateTime="2004-12-01T01:01:01Z") + pdatetime = PartialDateTest(Name="AETERM", TestDateTime="2004-12-01T01:01:01Z") self.assertEqual(pdatetime.TestDateTime, "2004-12-01T01:01:01Z") - pdatetime = TestPartialDate(Name="AETERM", TestDateTime="2004-12") + pdatetime = PartialDateTest(Name="AETERM", TestDateTime="2004-12") self.assertEqual(pdatetime.TestDateTime, "2004-12") - pdatetime = TestPartialDate(Name="AETERM", TestDateTime="2004-12-05T12") + pdatetime = PartialDateTest(Name="AETERM", TestDateTime="2004-12-05T12") self.assertEqual(pdatetime.TestDateTime, "2004-12-05T12") # invalid partial datetime with self.assertRaises(ValueError): - pdatetime = TestText(Name="AETERM", TestDateTime="2004---15T-:05") + pdatetime = TextTest(Name="AETERM", TestDateTime="2004---15T-:05") def test_partial_date_type(self): # valid partial date - pdate = TestPartialDate(Name="AETERM", TestDate="2004-12-20") + pdate = PartialDateTest(Name="AETERM", TestDate="2004-12-20") self.assertEqual(pdate.TestDate, "2004-12-20") - pdate = TestPartialDate(Name="AETERM", TestDate="2004-12") + pdate = PartialDateTest(Name="AETERM", TestDate="2004-12") self.assertEqual(pdate.TestDate, "2004-12") - pdate = TestPartialDate(Name="AETERM", TestDate="2004") + pdate = PartialDateTest(Name="AETERM", TestDate="2004") self.assertEqual(pdate.TestDate, "2004") # invalid partial date with self.assertRaises(ValueError): - pdate = TestPartialDate(Name="AETERM", TestDate="----20") + pdate = PartialDateTest(Name="AETERM", TestDate="----20") with self.assertRaises(ValueError): - pdate = TestPartialDate(Name="AETERM", TestDate="2004-13-20") + pdate = PartialDateTest(Name="AETERM", TestDate="2004-13-20") def test_partial_time_type(self): # valid partial time - ptime = TestPartialDate(Name="AETERM", TestTime="01:01:01Z") + ptime = PartialDateTest(Name="AETERM", TestTime="01:01:01Z") self.assertEqual(ptime.TestTime, "01:01:01Z") - ptime = TestPartialDate(Name="AETERM", TestTime="12:05") + ptime = PartialDateTest(Name="AETERM", TestTime="12:05") self.assertEqual(ptime.TestTime, "12:05") - ptime = TestPartialDate(Name="AETERM", TestTime="12") + ptime = PartialDateTest(Name="AETERM", TestTime="12") self.assertEqual(ptime.TestTime, "12") # invalid partial datetime with self.assertRaises(ValueError): - ptime = TestPartialDate(Name="AETERM", TestTime="-:05") + ptime = PartialDateTest(Name="AETERM", TestTime="-:05") From 63f683782f356d5563e08af6e2f60a2ce145a387 Mon Sep 17 00:00:00 2001 From: Geoff Low Date: Tue, 5 Apr 2022 12:33:51 +0100 Subject: [PATCH 04/26] Added routines to help find data/fixture files --- tests/__init__.py | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/tests/__init__.py b/tests/__init__.py index 3cc519c..9a758dd 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -1,12 +1,26 @@ import os -def get_data_file_path(filename: str): +def remove_prefix(filepath, parent_dir): + """ + Remove the parent_dir prefix from the filepath. + """ + if filepath.startswith(parent_dir): + filepath = filepath[len(parent_dir):] + return filepath + + +def get_data_file_path(filename: str, check_exists: bool = True): # strip off ./data prefix if it is there - if filename.startswith("./data"): - filename = os.path.split(filename)[-1] target = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'data', filename) - if not os.path.exists(target): + if check_exists and not os.path.exists(target): raise FileNotFoundError(f"File {target} not found") return target - \ No newline at end of file + + +def get_fixture_file_path(filename: str): + # strip off ./fixtures prefix if it is there + target = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'fixtures', filename) + if not os.path.exists(target): + raise FileNotFoundError(f"File {target} not found") + return target \ No newline at end of file From a4a4b67b68f3af749a5861d2f9cda1eade95498a Mon Sep 17 00:00:00 2001 From: Geoff Low Date: Tue, 5 Apr 2022 12:47:00 +0100 Subject: [PATCH 05/26] Added missing XSD document for fixtures --- .../fixtures/odm1-3-2/ODM1-3-2-foundation.xsd | 2651 +++++++++++++++++ tests/fixtures/odm1-3-2/ODM1-3-2.xsd | 15 + tests/fixtures/odm1-3-2/xlink.xsd | 45 + tests/fixtures/odm1-3-2/xml.xsd | 121 + .../fixtures/odm1-3-2/xmldsig-core-schema.xsd | 291 ++ 5 files changed, 3123 insertions(+) create mode 100644 tests/fixtures/odm1-3-2/ODM1-3-2-foundation.xsd create mode 100644 tests/fixtures/odm1-3-2/ODM1-3-2.xsd create mode 100644 tests/fixtures/odm1-3-2/xlink.xsd create mode 100644 tests/fixtures/odm1-3-2/xml.xsd create mode 100644 tests/fixtures/odm1-3-2/xmldsig-core-schema.xsd diff --git a/tests/fixtures/odm1-3-2/ODM1-3-2-foundation.xsd b/tests/fixtures/odm1-3-2/ODM1-3-2-foundation.xsd new file mode 100644 index 0000000..b6fde69 --- /dev/null +++ b/tests/fixtures/odm1-3-2/ODM1-3-2-foundation.xsd @@ -0,0 +1,2651 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/tests/fixtures/odm1-3-2/ODM1-3-2.xsd b/tests/fixtures/odm1-3-2/ODM1-3-2.xsd new file mode 100644 index 0000000..6f404ce --- /dev/null +++ b/tests/fixtures/odm1-3-2/ODM1-3-2.xsd @@ -0,0 +1,15 @@ + + + + + + + + \ No newline at end of file diff --git a/tests/fixtures/odm1-3-2/xlink.xsd b/tests/fixtures/odm1-3-2/xlink.xsd new file mode 100644 index 0000000..8a6f81d --- /dev/null +++ b/tests/fixtures/odm1-3-2/xlink.xsd @@ -0,0 +1,45 @@ + + + + + + Comment describing your root element + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/tests/fixtures/odm1-3-2/xml.xsd b/tests/fixtures/odm1-3-2/xml.xsd new file mode 100644 index 0000000..67f9454 --- /dev/null +++ b/tests/fixtures/odm1-3-2/xml.xsd @@ -0,0 +1,121 @@ + + + + + + See http://www.w3.org/XML/1998/namespace.html and + http://www.w3.org/TR/REC-xml for information about this namespace. + + This schema document describes the XML namespace, in a form + suitable for import by other schema documents. + + Note that local names in this namespace are intended to be defined + only by the World Wide Web Consortium or its subgroups. The + following names are currently defined in this namespace and should + not be used with conflicting semantics by any Working Group, + specification, or document instance: + + base (as an attribute name): denotes an attribute whose value + provides a URI to be used as the base for interpreting any + relative URIs in the scope of the element on which it + appears; its value is inherited. This name is reserved + by virtue of its definition in the XML Base specification. + + lang (as an attribute name): denotes an attribute whose value + is a language code for the natural language of the content of + any element; its value is inherited. This name is reserved + by virtue of its definition in the XML specification. + + space (as an attribute name): denotes an attribute whose + value is a keyword indicating what whitespace processing + discipline is intended for the content of the element; its + value is inherited. This name is reserved by virtue of its + definition in the XML specification. + + Father (in any context at all): denotes Jon Bosak, the chair of + the original XML Working Group. This name is reserved by + the following decision of the W3C XML Plenary and + XML Coordination groups: + + In appreciation for his vision, leadership and dedication + the W3C XML Plenary on this 10th day of February, 2000 + reserves for Jon Bosak in perpetuity the XML name + xml:Father + + + + + This schema defines attributes and an attribute group + suitable for use by + schemas wishing to allow xml:base, xml:lang or xml:space attributes + on elements they define. + + To enable this, such a schema must import this schema + for the XML namespace, e.g. as follows: + <schema . . .> + . . . + <import namespace="http://www.w3.org/XML/1998/namespace" + schemaLocation="http://www.w3.org/2001/03/xml.xsd"/> + + Subsequently, qualified reference to any of the attributes + or the group defined below will have the desired effect, e.g. + + <type . . .> + . . . + <attributeGroup ref="xml:specialAttrs"/> + + will define a type which will schema-validate an instance + element with any of those attributes + + + + In keeping with the XML Schema WG's standard versioning + policy, this schema document will persist at + http://www.w3.org/2001/03/xml.xsd. + At the date of issue it can also be found at + http://www.w3.org/2001/xml.xsd. + The schema document at that URI may however change in the future, + in order to remain compatible with the latest version of XML Schema + itself. In other words, if the XML Schema namespace changes, the version + of this document at + http://www.w3.org/2001/xml.xsd will change + accordingly; the version at + http://www.w3.org/2001/03/xml.xsd will not change. + + + + + + In due course, we should install the relevant ISO 2- and 3-letter + codes as the enumerated possible values . . . + + + + + + + + + + + + + + + + See http://www.w3.org/TR/xmlbase/ for + information about this attribute. + + + + + + diff --git a/tests/fixtures/odm1-3-2/xmldsig-core-schema.xsd b/tests/fixtures/odm1-3-2/xmldsig-core-schema.xsd new file mode 100644 index 0000000..9b5ffef --- /dev/null +++ b/tests/fixtures/odm1-3-2/xmldsig-core-schema.xsd @@ -0,0 +1,291 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From c44e4ce68130081cb6752fb1dcc5713a61c27bc8 Mon Sep 17 00:00:00 2001 From: Geoff Low Date: Tue, 5 Apr 2022 12:49:23 +0100 Subject: [PATCH 06/26] Added Dev Requirements Files --- .gitignore | 2 ++ requirements-dev.txt | 5 +++++ tox.ini | 9 +++++++++ 3 files changed, 16 insertions(+) create mode 100644 requirements-dev.txt create mode 100644 tox.ini diff --git a/.gitignore b/.gitignore index 23801aa..f803171 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,5 @@ venv/ docs/build/ odmlib/build/ dist/ +.tox/ +.idea/ diff --git a/requirements-dev.txt b/requirements-dev.txt new file mode 100644 index 0000000..c022575 --- /dev/null +++ b/requirements-dev.txt @@ -0,0 +1,5 @@ +-r requirements.txt +sphinx +flake8 +tox +pytest diff --git a/tox.ini b/tox.ini new file mode 100644 index 0000000..60c4777 --- /dev/null +++ b/tox.ini @@ -0,0 +1,9 @@ +# content of: tox.ini , put in same dir as setup.py +[tox] +envlist = py37, py38, py39, py310 + +# Note `python setup.py test` is deprecated +[testenv] +deps = -rrequirements-dev.txt +commands = pytest + From baaa3018b4f7a4cdfecfb70ad021eaa54739ce2b Mon Sep 17 00:00:00 2001 From: Geoff Low Date: Tue, 5 Apr 2022 12:52:41 +0100 Subject: [PATCH 07/26] Added some typings --- odmlib/odm_parser.py | 41 ++++++++++++++++++++++------------------- 1 file changed, 22 insertions(+), 19 deletions(-) diff --git a/odmlib/odm_parser.py b/odmlib/odm_parser.py index 41b6faa..ca8a646 100644 --- a/odmlib/odm_parser.py +++ b/odmlib/odm_parser.py @@ -1,4 +1,7 @@ +from xml.etree import ElementTree import xml.etree.ElementTree as ET +from xml.etree.ElementTree import Element +from typing import List, Optional import xmlschema as XSD import odmlib.ns_registry as NS from abc import ABC, abstractmethod @@ -28,11 +31,11 @@ class ODMSchemaValidator(SchemaValidator): def __init__(self, xsd_file): self.xsd = XSD.XMLSchema(xsd_file) - def validate_tree(self, tree): + def validate_tree(self, tree: ElementTree): result = self.xsd.is_valid(tree) return result - def validate_file(self, odm_file): + def validate_file(self, odm_file: str): try: result = self.xsd.validate(odm_file) except XSD.validators.exceptions.XMLSchemaChildrenValidationError as ex: @@ -68,27 +71,27 @@ def __init__(self): self.admin_data = [] self.clinical_data = [] - def ODM(self): + def ODM(self) -> Element: return self.root - def Study(self): + def Study(self) -> List[Element]: study = self.root.findall(ODM_PREFIX + "Study", ODM_NS) return study - def MetaDataVersion(self, idx=0): + def MetaDataVersion(self, idx: int = 0) -> List[Element]: study = self.root.findall(ODM_PREFIX + "Study", ODM_NS) self.mdv = study[idx].findall(ODM_PREFIX + "MetaDataVersion", ODM_NS) return self.mdv - def AdminData(self): + def AdminData(self) -> List[Element]: self.admin_data = self.root.findall(ODM_PREFIX + "AdminData", ODM_NS) return self.admin_data - def ClinicalData(self): + def ClinicalData(self) -> List[Element]: self.clinical_data = self.root.findall(ODM_PREFIX + "ClinicalData", ODM_NS) return self.clinical_data - def ReferenceData(self): + def ReferenceData(self) -> List[Element]: self.reference_data = self.root.findall(ODM_PREFIX + "ReferenceData", ODM_NS) return self.reference_data @@ -98,13 +101,13 @@ def __init__(self, odm_file, namespace_registry=None): self.odm_file = odm_file super().__init__(ns_registry=namespace_registry) - def parse(self): + def parse(self) -> Element: self.register_namespaces() odm_tree = ET.parse(self.odm_file) self.root = odm_tree.getroot() return self.root - def parse_tree(self): + def parse_tree(self) -> ElementTree: self.register_namespaces() return ET.parse(self.odm_file) @@ -114,12 +117,12 @@ def __init__(self, odm_string, namespace_registry=None): self.odm_string = odm_string super().__init__(ns_registry=namespace_registry) - def parse(self): + def parse(self) -> Element: self.register_namespaces() self.root = ET.fromstring(self.odm_string) return self.root - def parse_tree(self): + def parse_tree(self) -> ElementTree: self.register_namespaces() #return ET.ElementTree(ET.fromstring(self.odm_string)) return ET.fromstring(self.odm_string) @@ -133,29 +136,29 @@ def __init__(self, odm_string): self.clinical_data = [] self.reference_data = [] - def parse(self): + def parse(self) -> dict: return self.root - def ODM(self): + def ODM(self) -> dict: return self.root - def Study(self): + def Study(self) -> List[dict]: study = self.root["Study"] return study - def MetaDataVersion(self): + def MetaDataVersion(self) -> List[dict]: study = self.root["Study"] self.mdv = study[0]["MetaDataVersion"] return self.mdv - def AdminData(self): + def AdminData(self) -> List[dict]: self.admin_data = self.root["AdminData"] return self.admin_data - def ClinicalData(self): + def ClinicalData(self) -> List[dict]: self.clinical_data = self.root["ClinicalData"] return self.clinical_data - def ReferenceData(self): + def ReferenceData(self) -> List[dict]: self.reference_data = self.root["ReferenceData"] return self.reference_data From 674042b24fc8aefbc358b089fef1544be531f4c0 Mon Sep 17 00:00:00 2001 From: Geoff Low Date: Tue, 5 Apr 2022 12:53:02 +0100 Subject: [PATCH 08/26] Refactor test models --- tests/test_create_dataset.py | 13 ++- tests/test_create_odm.py | 154 ++++++++++++++++--------- tests/test_define21_parser_metadata.py | 5 +- tests/test_define_parser_metadata.py | 5 +- tests/test_extended_alias.py | 2 +- tests/test_insert_item.py | 8 +- tests/test_itemGroupDef.py | 2 +- tests/test_itemGroupDef_define.py | 2 +- tests/test_itemGroupDef_define21.py | 2 +- tests/test_metadataversion.py | 4 +- tests/test_odm_parser_metadata.py | 6 +- tests/test_odm_root.py | 4 +- tests/test_odm_string_parser.py | 6 +- tests/test_odm_validator.py | 11 +- tests/test_oid_index.py | 5 +- tests/test_read_dataset.py | 4 +- tests/test_referenceData.py | 2 +- 17 files changed, 146 insertions(+), 89 deletions(-) diff --git a/tests/test_create_dataset.py b/tests/test_create_dataset.py index 721fa53..dbc6e34 100644 --- a/tests/test_create_dataset.py +++ b/tests/test_create_dataset.py @@ -3,10 +3,11 @@ import datetime import odmlib.ns_registry as NS import odmlib.odm_loader as OL +from tests import get_data_file_path -ODM_XML_FILE = "./data/ae_test.xml" -ODM_JSON_FILE = "./data/ae_test.json" +ODM_XML_FILE = "ae_test.xml" +ODM_JSON_FILE = "ae_test.json" class TestCreateDataset(unittest.TestCase): def setUp(self) -> None: @@ -24,11 +25,11 @@ def setUp(self) -> None: def test_write_dataset_xml(self): - self.root.write_xml(ODM_XML_FILE) + self.root.write_xml(get_data_file_path(ODM_XML_FILE)) loader = OL.XMLODMLoader(model_package="dataset_1_0_1", ns_uri="http://www.cdisc.org/ns/Dataset-XML/v1.0") NS.NamespaceRegistry(prefix="odm", uri="http://www.cdisc.org/ns/odm/v1.3", is_default=True) ns = NS.NamespaceRegistry(prefix="data", uri="http://www.cdisc.org/ns/Dataset-XML/v1.0") - loader.create_document(ODM_XML_FILE, ns) + loader.create_document(get_data_file_path(ODM_XML_FILE), ns) odm = loader.load_odm() self.assertEqual(odm.FileOID, "ODM.DATASET.001") self.assertEqual(odm.ClinicalData.ItemGroupData[0].ItemGroupOID, "IG.AE") @@ -36,9 +37,9 @@ def test_write_dataset_xml(self): self.assertEqual(odm.ClinicalData.ItemGroupData[1].ItemData[4].Value, "ANXIETY") def test_write_dataset_json(self): - self.root.write_json(ODM_JSON_FILE) + self.root.write_json(get_data_file_path(ODM_JSON_FILE)) loader = OL.JSONODMLoader(model_package="dataset_1_0_1") - loader.create_document(ODM_JSON_FILE) + loader.create_document(get_data_file_path(ODM_JSON_FILE)) odm = loader.load_odm() self.assertEqual(odm.FileOID, "ODM.DATASET.001") self.assertEqual(odm.ClinicalData.ItemGroupData[0].ItemGroupOID, "IG.AE") diff --git a/tests/test_create_odm.py b/tests/test_create_odm.py index 960aed3..7b80c2b 100644 --- a/tests/test_create_odm.py +++ b/tests/test_create_odm.py @@ -1,3 +1,4 @@ +import os import unittest import odmlib.odm_1_3_2.model as ODM import datetime @@ -6,14 +7,16 @@ import odmlib.ns_registry as NS import odmlib.odm_loader as OL import odmlib.loader as LD +from tests import get_data_file_path -ODM_XML_FILE = "./data/simple_create.xml" -ODM_JSON_FILE = "./data/simple_create.json" - +ODM_XML_FILE = get_data_file_path("simple_create.xml", check_exists=False) +ODM_JSON_FILE = get_data_file_path("simple_create.json", check_exists=False) +ODM_SIMPLE_STR_FILE = get_data_file_path("simple_create_from_string.xml", check_exists=False) class MyTestCase(unittest.TestCase): def setUp(self) -> None: - current_datetime = datetime.datetime.utcnow().replace(tzinfo=datetime.timezone.utc).isoformat() + current_datetime = datetime.datetime.utcnow().replace( + tzinfo=datetime.timezone.utc).isoformat() root = ODM.ODM(FileOID="ODM.DEMO.001", Granularity="Metadata", AsOfDateTime=current_datetime, CreationDateTime=current_datetime, ODMVersion="1.3.2", FileType="Snapshot", Originator="swhume", SourceSystem="odmlib", SourceSystemVersion="0.1") @@ -23,10 +26,12 @@ def setUp(self) -> None: # create the global variables root.Study[0].GlobalVariables = ODM.GlobalVariables() - root.Study[0].GlobalVariables.StudyName = ODM.StudyName(_content="Get Started with ODM XML") + root.Study[0].GlobalVariables.StudyName = ODM.StudyName( + _content="Get Started with ODM XML") root.Study[0].GlobalVariables.StudyDescription = ODM.StudyDescription( _content="Demo to get started with odmlib") - root.Study[0].GlobalVariables.ProtocolName = ODM.ProtocolName(_content="ODM XML Get Started") + root.Study[0].GlobalVariables.ProtocolName = ODM.ProtocolName( + _content="ODM XML Get Started") # create the MetaDataVersion root.Study[0].MetaDataVersion.append(ODM.MetaDataVersion(OID="MDV.DEMO-ODM-01", Name="Get Started MDV", @@ -34,31 +39,41 @@ def setUp(self) -> None: # create Protocol p = ODM.Protocol() p.Description = ODM.Description() - p.Description.TranslatedText.append(ODM.TranslatedText(_content="Get Started Protocol", lang="en")) - p.StudyEventRef.append(ODM.StudyEventRef(StudyEventOID="BASELINE", OrderNumber=1, Mandatory="Yes")) + p.Description.TranslatedText.append(ODM.TranslatedText( + _content="Get Started Protocol", lang="en")) + p.StudyEventRef.append(ODM.StudyEventRef( + StudyEventOID="BASELINE", OrderNumber=1, Mandatory="Yes")) root.Study[0].MetaDataVersion[0].Protocol = p # create a StudyEventDef - sed = ODM.StudyEventDef(OID="BASELINE", Name="Baseline Visit", Repeating="No", Type="Scheduled") - sed.FormRef.append(ODM.FormRef(FormOID="ODM.F.DM", Mandatory="Yes", OrderNumber=1)) + sed = ODM.StudyEventDef( + OID="BASELINE", Name="Baseline Visit", Repeating="No", Type="Scheduled") + sed.FormRef.append(ODM.FormRef(FormOID="ODM.F.DM", + Mandatory="Yes", OrderNumber=1)) root.Study[0].MetaDataVersion[0].StudyEventDef.append(sed) # create a FormDef fd = ODM.FormDef(OID="ODM.F.DM", Name="Demographics", Repeating="No") - fd.ItemGroupRef.append(ODM.ItemGroupRef(ItemGroupOID="ODM.IG.DM", Mandatory="Yes", OrderNumber=2)) + fd.ItemGroupRef.append(ODM.ItemGroupRef( + ItemGroupOID="ODM.IG.DM", Mandatory="Yes", OrderNumber=2)) root.Study[0].MetaDataVersion[0].FormDef.append(fd) # create an ItemGroupDef - igd = ODM.ItemGroupDef(OID="ODM.IG.DM", Name="Demographics", Repeating="No") - igd.ItemRef.append(ODM.ItemRef(ItemOID="ODM.IT.DM.BRTHYR", Mandatory="Yes")) + igd = ODM.ItemGroupDef( + OID="ODM.IG.DM", Name="Demographics", Repeating="No") + igd.ItemRef.append(ODM.ItemRef( + ItemOID="ODM.IT.DM.BRTHYR", Mandatory="Yes")) root.Study[0].MetaDataVersion[0].ItemGroupDef.append(igd) # create an ItemDef - itd = ODM.ItemDef(OID="ODM.IT.DM.BRTHYR", Name="Birth Year", DataType="integer") + itd = ODM.ItemDef(OID="ODM.IT.DM.BRTHYR", + Name="Birth Year", DataType="integer") itd.Description = ODM.Description() - itd.Description.TranslatedText.append(ODM.TranslatedText(_content="Year of the subject's birth", lang="en")) + itd.Description.TranslatedText.append(ODM.TranslatedText( + _content="Year of the subject's birth", lang="en")) itd.Question = ODM.Question() - itd.Question.TranslatedText.append(ODM.TranslatedText(_content="Birth Year", lang="en")) + itd.Question.TranslatedText.append( + ODM.TranslatedText(_content="Birth Year", lang="en")) itd.Alias.append(ODM.Alias(Context="CDASH", Name="BRTHYR")) itd.Alias.append(ODM.Alias(Context="SDTM", Name="BRTHDTC")) root.Study[0].MetaDataVersion[0].ItemDef.append(itd) @@ -69,29 +84,15 @@ def setUp(self) -> None: # save the same ODM document to a JSON file root.write_json(ODM_JSON_FILE) + # Simple create from string + self.generate_simple_create_from_string() - def test_read_odm_xml(self): - loader = LD.ODMLoader(OL.XMLODMLoader(model_package="odm_1_3_2", ns_uri="http://www.cdisc.org/ns/odm/v1.3")) - loader.open_odm_document(ODM_XML_FILE) - mdv = loader.MetaDataVersion() - item_list = mdv.ItemDef - item = item_list[0] - self.assertEqual(item.OID, "ODM.IT.DM.BRTHYR") - # tests the __len__ in ItemGroupDef - self.assertEqual(len(item_list), 1) - - def test_read_odm_json(self): - loader = LD.ODMLoader(OL.JSONODMLoader(model_package="odm_1_3_2")) - loader.open_odm_document(ODM_JSON_FILE) - mdv = loader.MetaDataVersion() - igd_list = mdv.ItemGroupDef - igd = igd_list[0] - self.assertEqual(igd.OID, "ODM.IG.DM") - # tests the __len__ in ItemGroupDef - self.assertEqual(len(igd_list), 1) - - def test_xml_to_string(self): - current_datetime = datetime.datetime.utcnow().replace(tzinfo=datetime.timezone.utc).isoformat() + def generate_simple_create_from_string(self): + """ + Generates a fixture for the test + """ + current_datetime = datetime.datetime.utcnow().replace( + tzinfo=datetime.timezone.utc).isoformat() root = ODM.ODM(FileOID="ODM.DEMO.001", Granularity="Metadata", AsOfDateTime=current_datetime, CreationDateTime=current_datetime, ODMVersion="1.3.2", FileType="Snapshot", Originator="swhume", SourceSystem="odmlib", SourceSystemVersion="0.1") @@ -101,10 +102,12 @@ def test_xml_to_string(self): # create the global variables root.Study[0].GlobalVariables = ODM.GlobalVariables() - root.Study[0].GlobalVariables.StudyName = ODM.StudyName(_content="Get Started with ODM XML") + root.Study[0].GlobalVariables.StudyName = ODM.StudyName( + _content="Get Started with ODM XML") root.Study[0].GlobalVariables.StudyDescription = ODM.StudyDescription( _content="Demo to get started with odmlib") - root.Study[0].GlobalVariables.ProtocolName = ODM.ProtocolName(_content="ODM XML Get Started") + root.Study[0].GlobalVariables.ProtocolName = ODM.ProtocolName( + _content="ODM XML Get Started") # create the MetaDataVersion root.Study[0].MetaDataVersion.append(ODM.MetaDataVersion(OID="MDV.DEMO-ODM-01", Name="Get Started MDV", @@ -112,31 +115,41 @@ def test_xml_to_string(self): # create Protocol p = ODM.Protocol() p.Description = ODM.Description() - p.Description.TranslatedText.append(ODM.TranslatedText(_content="Get Started Protocol", lang="en")) - p.StudyEventRef.append(ODM.StudyEventRef(StudyEventOID="BASELINE", OrderNumber=1, Mandatory="Yes")) + p.Description.TranslatedText.append(ODM.TranslatedText( + _content="Get Started Protocol", lang="en")) + p.StudyEventRef.append(ODM.StudyEventRef( + StudyEventOID="BASELINE", OrderNumber=1, Mandatory="Yes")) root.Study[0].MetaDataVersion[0].Protocol = p # create a StudyEventDef - sed = ODM.StudyEventDef(OID="BASELINE", Name="Baseline Visit", Repeating="No", Type="Scheduled") - sed.FormRef.append(ODM.FormRef(FormOID="ODM.F.DM", Mandatory="Yes", OrderNumber=1)) + sed = ODM.StudyEventDef( + OID="BASELINE", Name="Baseline Visit", Repeating="No", Type="Scheduled") + sed.FormRef.append(ODM.FormRef(FormOID="ODM.F.DM", + Mandatory="Yes", OrderNumber=1)) root.Study[0].MetaDataVersion[0].StudyEventDef.append(sed) # create a FormDef fd = ODM.FormDef(OID="ODM.F.DM", Name="Demographics", Repeating="No") - fd.ItemGroupRef.append(ODM.ItemGroupRef(ItemGroupOID="ODM.IG.DM", Mandatory="Yes", OrderNumber=2)) + fd.ItemGroupRef.append(ODM.ItemGroupRef( + ItemGroupOID="ODM.IG.DM", Mandatory="Yes", OrderNumber=2)) root.Study[0].MetaDataVersion[0].FormDef.append(fd) # create an ItemGroupDef - igd = ODM.ItemGroupDef(OID="ODM.IG.DM", Name="Demographics", Repeating="No") - igd.ItemRef.append(ODM.ItemRef(ItemOID="ODM.IT.DM.BRTHYR", Mandatory="Yes")) + igd = ODM.ItemGroupDef( + OID="ODM.IG.DM", Name="Demographics", Repeating="No") + igd.ItemRef.append(ODM.ItemRef( + ItemOID="ODM.IT.DM.BRTHYR", Mandatory="Yes")) root.Study[0].MetaDataVersion[0].ItemGroupDef.append(igd) # create an ItemDef - itd = ODM.ItemDef(OID="ODM.IT.DM.BRTHYR", Name="Birth Year", DataType="integer") + itd = ODM.ItemDef(OID="ODM.IT.DM.BRTHYR", + Name="Birth Year", DataType="integer") itd.Description = ODM.Description() - itd.Description.TranslatedText.append(ODM.TranslatedText(_content="Year of the subject's birth", lang="en")) + itd.Description.TranslatedText.append(ODM.TranslatedText( + _content="Year of the subject's birth", lang="en")) itd.Question = ODM.Question() - itd.Question.TranslatedText.append(ODM.TranslatedText(_content="Birth Year", lang="en")) + itd.Question.TranslatedText.append( + ODM.TranslatedText(_content="Birth Year", lang="en")) itd.Alias.append(ODM.Alias(Context="CDASH", Name="BRTHYR")) itd.Alias.append(ODM.Alias(Context="SDTM", Name="BRTHDTC")) root.Study[0].MetaDataVersion[0].ItemDef.append(itd) @@ -145,11 +158,46 @@ def test_xml_to_string(self): # add namespaces nsr = NS.NamespaceRegistry() odm_str = nsr.set_odm_namespace_attributes_string(odm_xml_string) - with open("./data/simple_create_from_string.xml", "w") as xml_file: + with open(ODM_SIMPLE_STR_FILE, "w") as xml_file: xml_file.write(odm_str) - loader = LD.ODMLoader(OL.XMLODMLoader(model_package="odm_1_3_2", ns_uri="http://www.cdisc.org/ns/odm/v1.3")) - loader.open_odm_document("./data/simple_create_from_string.xml") + def tearDown(self) -> None: + """ + Clean up the test files. + """ + for temp_file in [ODM_XML_FILE, + ODM_JSON_FILE, + ODM_SIMPLE_STR_FILE]: + if os.path.exists(temp_file): + os.remove(temp_file) + + def test_read_odm_xml(self): + loader = LD.ODMLoader(OL.XMLODMLoader( + model_package="odm_1_3_2", ns_uri="http://www.cdisc.org/ns/odm/v1.3")) + loader.open_odm_document(ODM_XML_FILE) + mdv = loader.MetaDataVersion() + item_list = mdv.ItemDef + item = item_list[0] + self.assertEqual(item.OID, "ODM.IT.DM.BRTHYR") + # tests the __len__ in ItemGroupDef + self.assertEqual(len(item_list), 1) + + def test_read_odm_json(self): + loader = LD.ODMLoader(OL.JSONODMLoader(model_package="odm_1_3_2")) + loader.open_odm_document(ODM_JSON_FILE) + mdv = loader.MetaDataVersion() + igd_list = mdv.ItemGroupDef + igd = igd_list[0] + self.assertEqual(igd.OID, "ODM.IG.DM") + # tests the __len__ in ItemGroupDef + self.assertEqual(len(igd_list), 1) + + + def test_xml_to_string(self): + + loader = LD.ODMLoader(OL.XMLODMLoader( + model_package="odm_1_3_2", ns_uri="http://www.cdisc.org/ns/odm/v1.3")) + loader.open_odm_document(get_data_file_path("simple_create_from_string.xml")) mdv = loader.MetaDataVersion() item_list = mdv.ItemDef item = item_list[0] diff --git a/tests/test_define21_parser_metadata.py b/tests/test_define21_parser_metadata.py index 85547cf..933c18b 100644 --- a/tests/test_define21_parser_metadata.py +++ b/tests/test_define21_parser_metadata.py @@ -4,13 +4,14 @@ import odmlib.ns_registry as NS import odmlib.define_loader as OL import odmlib.loader as LD +from tests import get_data_file_path ODM_NS = "{http://www.cdisc.org/ns/odm/v1.3}" class TestDefine21LoaderMetaData(unittest.TestCase): def setUp(self) -> None: - self.odm_file_1 = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'data', 'defineV21-SDTM-metadata.xml') + self.odm_file_1 = get_data_file_path('defineV21-SDTM-metadata.xml') NS.NamespaceRegistry(prefix="odm", uri="http://www.cdisc.org/ns/odm/v1.3", is_default=True) NS.NamespaceRegistry(prefix="def", uri="http://www.cdisc.org/ns/def/v2.1") self.nsr = NS.NamespaceRegistry(prefix="nciodm", uri="http://ncicb.nci.nih.gov/xml/odm/EVS/CDISC") @@ -31,7 +32,7 @@ def test_MetaDataVersion(self): self.assertDictEqual(self.mdv[0].attrib, mdv_dict) self.assertEqual(mdv_dict["{http://www.cdisc.org/ns/def/v2.1}DefineVersion"], self.mdv[0].attrib["{http://www.cdisc.org/ns/def/v2.1}DefineVersion"]) - # self.assertEqual(769, len([e.tag for e in self.mdv[0].getchildren()])) + self.assertEqual(337, len([e.tag for e in self.mdv[0]])) def test_Standards(self): # self.assertTrue(isinstance(self.mdv[0].Standards.Standard, list)) diff --git a/tests/test_define_parser_metadata.py b/tests/test_define_parser_metadata.py index 333745f..6ad6969 100644 --- a/tests/test_define_parser_metadata.py +++ b/tests/test_define_parser_metadata.py @@ -2,13 +2,14 @@ import os import odmlib.odm_parser as P import odmlib.ns_registry as NS +from tests import get_data_file_path ODM_NS = "{http://www.cdisc.org/ns/odm/v1.3}" class TestOdmParserMetaData(unittest.TestCase): def setUp(self) -> None: - self.odm_file_1 = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'data', 'define2-0-0-sdtm-test.xml') + self.odm_file_1 = get_data_file_path('define2-0-0-sdtm-test.xml') NS.NamespaceRegistry(prefix="odm", uri="http://www.cdisc.org/ns/odm/v1.3", is_default=True) NS.NamespaceRegistry(prefix="def", uri="http://www.cdisc.org/ns/def/v2.0") self.nsr = NS.NamespaceRegistry(prefix="nciodm", uri="http://ncicb.nci.nih.gov/xml/odm/EVS/CDISC") @@ -27,7 +28,7 @@ def test_MetaDataVersion(self): self.assertDictEqual(self.mdv[0].attrib, mdv_dict) self.assertEqual(mdv_dict["{http://www.cdisc.org/ns/def/v2.0}DefineVersion"], self.mdv[0].attrib["{http://www.cdisc.org/ns/def/v2.0}DefineVersion"]) - self.assertEqual(769, len([e.tag for e in self.mdv[0].getchildren()])) + self.assertEqual(769, len([e.tag for e in self.mdv[0]])) def test_ItemGroupDef(self): ig = self.parser.ItemGroupDef(parent=self.mdv[0]) diff --git a/tests/test_extended_alias.py b/tests/test_extended_alias.py index 7176150..c682925 100644 --- a/tests/test_extended_alias.py +++ b/tests/test_extended_alias.py @@ -1,5 +1,5 @@ import unittest -import model_extended as ODM +from . import model_extended as ODM class TestExtendedAlias(unittest.TestCase): diff --git a/tests/test_insert_item.py b/tests/test_insert_item.py index 8a4ed38..14485c1 100644 --- a/tests/test_insert_item.py +++ b/tests/test_insert_item.py @@ -4,13 +4,15 @@ import odmlib.loader as LD import os +from tests import get_data_file_path + class TestInsertItem(unittest.TestCase): def setUp(self): - self.odm_file = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'data', 'cdash-odm-test.xml') - self.odm_file_out = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'data', 'cdash-odm-test-insert.xml') + self.odm_file = get_data_file_path('cdash-odm-test.xml') + self.odm_file_out = get_data_file_path('cdash-odm-test-insert.xml') self.loader = LD.ODMLoader(OL.XMLODMLoader()) - + def test_insert_item_with_none_element(self): attrs = self.set_item_attributes() item = ODM.ItemDef(**attrs) diff --git a/tests/test_itemGroupDef.py b/tests/test_itemGroupDef.py index a186814..258891f 100644 --- a/tests/test_itemGroupDef.py +++ b/tests/test_itemGroupDef.py @@ -88,7 +88,7 @@ def test_to_xml(self): igd.ItemRef = [ir1, ir2] igd_xml = igd.to_xml() self.assertEqual(igd_xml.attrib["OID"], "IG.VS") - self.assertListEqual(["Description", "ItemRef", "ItemRef"], [e.tag for e in igd_xml.getchildren()]) + self.assertListEqual(["Description", "ItemRef", "ItemRef"], [e.tag for e in igd_xml]) def test_itemgroupdef_round_trip(self): """ system test to create and serialize an ItemGroupDef object """ diff --git a/tests/test_itemGroupDef_define.py b/tests/test_itemGroupDef_define.py index 9d47e2c..d2099a6 100644 --- a/tests/test_itemGroupDef_define.py +++ b/tests/test_itemGroupDef_define.py @@ -92,7 +92,7 @@ def test_to_xml(self): igd.ItemRef = [ir1, ir2] igd_xml = igd.to_xml() self.assertEqual(igd_xml.attrib["OID"], "IG.VS") - self.assertListEqual(["Description", "ItemRef", "ItemRef"], [e.tag for e in igd_xml.getchildren()]) + self.assertListEqual(["Description", "ItemRef", "ItemRef"], [e.tag for e in igd_xml]) def test_itemgroupdef_round_trip(self): """ system test to create and serialize an ItemGroupDef object """ diff --git a/tests/test_itemGroupDef_define21.py b/tests/test_itemGroupDef_define21.py index 70be1c0..d9408e6 100644 --- a/tests/test_itemGroupDef_define21.py +++ b/tests/test_itemGroupDef_define21.py @@ -100,7 +100,7 @@ def test_to_xml(self): igd.ItemRef = [ir1, ir2] igd_xml = igd.to_xml() self.assertEqual(igd_xml.attrib["OID"], "IG.VS") - self.assertListEqual(["Description", "ItemRef", "ItemRef"], [e.tag for e in igd_xml.getchildren()]) + self.assertListEqual(["Description", "ItemRef", "ItemRef"], [e.tag for e in igd_xml]) def test_itemgroupdef_parse_xml(self): parser = ODM_PARSER.ODMParser(self.input_file, self.nsr) diff --git a/tests/test_metadataversion.py b/tests/test_metadataversion.py index 698b8fe..cdbf928 100644 --- a/tests/test_metadataversion.py +++ b/tests/test_metadataversion.py @@ -115,9 +115,9 @@ def test_mdv_to_xml(self): self.assertEqual("MDV.TRACE-XML-ODM-01", mdv_xml.attrib["OID"]) children = ['Protocol', 'StudyEventDef', 'StudyEventDef', 'FormDef', 'FormDef', 'ItemGroupDef', 'ItemGroupDef', 'ItemDef', 'ItemDef', 'CodeList', 'MethodDef', 'ConditionDef'] - found_list = [e.tag for e in mdv_xml.getchildren()] + found_list = [e.tag for e in mdv_xml] print(found_list) - self.assertListEqual(children, [e.tag for e in mdv_xml.getchildren()]) + self.assertListEqual(children, [e.tag for e in mdv_xml]) def add_CD(self): tt1 = ODM.TranslatedText(_content="Skip the BRTHMO field when BRTHYR length NE 4", lang="en") diff --git a/tests/test_odm_parser_metadata.py b/tests/test_odm_parser_metadata.py index fe004f9..52551f5 100644 --- a/tests/test_odm_parser_metadata.py +++ b/tests/test_odm_parser_metadata.py @@ -15,12 +15,12 @@ def setUp(self) -> None: def test_MetaDataVersion(self): self.assertTrue(isinstance(self.mdv, list)) self.assertDictEqual(self.mdv[0].attrib, {"Name": "TRACE-XML MDV", "OID": "MDV.TRACE-XML-ODM-01"}) - self.assertEqual(81, len([e.tag for e in self.mdv[0].getchildren()])) + self.assertEqual(81, len([e.tag for e in self.mdv[0]])) def test_Protocol(self): protocol = self.parser.Protocol(parent=self.mdv[0]) self.assertIsInstance(protocol[0]["elem"], Element) - self.assertListEqual([ODM_NS + "StudyEventRef"], [e.tag for e in protocol[0]["elem"].getchildren()]) + self.assertListEqual([ODM_NS + "StudyEventRef"], [e.tag for e in protocol[0]["elem"]]) def test_StudyEventRef(self): protocol = self.parser.Protocol(parent=self.mdv[0]) @@ -37,7 +37,7 @@ def test_FormRef(self): fr = self.parser.FormRef(parent=sed[0]["elem"]) self.assertEqual(fr[0]["FormOID"], "ODM.F.DM") fr_list = [ODM_NS + "FormRef", ODM_NS + "FormRef", ODM_NS + "FormRef"] - self.assertListEqual(fr_list, [e.tag for e in sed[0]["elem"].getchildren()]) + self.assertListEqual(fr_list, [e.tag for e in sed[0]["elem"]]) def test_FormDef(self): fd = self.parser.FormDef(parent=self.mdv[0]) diff --git a/tests/test_odm_root.py b/tests/test_odm_root.py index 298ceea..50efb61 100644 --- a/tests/test_odm_root.py +++ b/tests/test_odm_root.py @@ -23,9 +23,9 @@ def test_odm_xml_file(self): self.odm.Study = [study] odm_xml = self.odm.to_xml() self.write_odm_file(odm_xml) - found_list = [e.tag for e in odm_xml.getchildren()] + found_list = [e.tag for e in odm_xml] print(found_list) - self.assertListEqual(["Study"], [e.tag for e in odm_xml.getchildren()]) + self.assertListEqual(["Study"], [e.tag for e in odm_xml]) def test_odm_xml_writer(self): study = self.add_study() diff --git a/tests/test_odm_string_parser.py b/tests/test_odm_string_parser.py index 759e90e..efe998a 100644 --- a/tests/test_odm_string_parser.py +++ b/tests/test_odm_string_parser.py @@ -17,12 +17,12 @@ def setUp(self) -> None: def test_MetaDataVersion(self): self.assertTrue(isinstance(self.mdv, list)) self.assertDictEqual(self.mdv[0].attrib, {"Name": "TRACE-XML MDV", "OID": "MDV.TRACE-XML-ODM-01"}) - self.assertEqual(81, len([e.tag for e in self.mdv[0].getchildren()])) + self.assertEqual(81, len([e.tag for e in self.mdv[0]])) def test_Protocol(self): protocol = self.parser.Protocol(parent=self.mdv[0]) self.assertIsInstance(protocol[0]["elem"], Element) - self.assertListEqual([ODM_NS + "StudyEventRef"], [e.tag for e in protocol[0]["elem"].getchildren()]) + self.assertListEqual([ODM_NS + "StudyEventRef"], [e.tag for e in protocol[0]["elem"]]) def test_StudyEventRef(self): protocol = self.parser.Protocol(parent=self.mdv[0]) @@ -39,7 +39,7 @@ def test_FormRef(self): fr = self.parser.FormRef(parent=sed[0]["elem"]) self.assertEqual(fr[0]["FormOID"], "ODM.F.DM") fr_list = [ODM_NS + "FormRef", ODM_NS + "FormRef", ODM_NS + "FormRef"] - self.assertListEqual(fr_list, [e.tag for e in sed[0]["elem"].getchildren()]) + self.assertListEqual(fr_list, [e.tag for e in sed[0]["elem"]]) def test_FormDef(self): fd = self.parser.FormDef(parent=self.mdv[0]) diff --git a/tests/test_odm_validator.py b/tests/test_odm_validator.py index dffb32e..fe27942 100644 --- a/tests/test_odm_validator.py +++ b/tests/test_odm_validator.py @@ -1,13 +1,14 @@ from unittest import TestCase import os import odmlib.odm_parser as P +from tests import get_data_file_path, get_fixture_file_path class TestODMValidator(TestCase): def setUp(self) -> None: - self.odm_file = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'data', 'cdash-odm-test.xml') + self.odm_file = get_data_file_path('cdash-odm-test.xml') # set the file and path to point to the odm 1.3.2 schema on your system - odm_schema_file = os.path.join(os.sep, 'home', 'sam', 'standards', 'odm1-3-2', 'ODM1-3-2.xsd') + odm_schema_file = get_fixture_file_path('odm1-3-2/ODM1-3-2.xsd') self.validator = P.ODMSchemaValidator(odm_schema_file) def test_validate_tree_valid(self): @@ -21,13 +22,13 @@ def test_validate_file(self): self.assertIsNone(self.validator.validate_file(self.odm_file)) def test_validate_file_invalid(self): - odm_file = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'data', 'cdash-odm-test-invalid.xml') + odm_file = get_data_file_path('cdash-odm-test-invalid.xml') # with self.assertRaises(XSD.validators.exceptions.XMLSchemaChildrenValidationError): with self.assertRaises(P.OdmlibSchemaValidationError): self.validator.validate_file(odm_file) def test_validate_file_invalid_msg(self): - odm_file = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'data', 'cdash-odm-test-invalid.xml') + odm_file = get_data_file_path('cdash-odm-test-invalid.xml') try: self.validator.validate_file(odm_file) except P.OdmlibSchemaValidationError as ex: @@ -35,7 +36,7 @@ def test_validate_file_invalid_msg(self): self.assertIn("failed validating", ex.args[0].msg) def test_validate_tree_invalid(self): - odm_file = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'data', 'cdash-odm-test-invalid.xml') + odm_file = get_data_file_path('cdash-odm-test-invalid.xml') self.parser = P.ODMParser(odm_file) tree = self.parser.parse_tree() is_valid = self.validator.validate_tree(tree) diff --git a/tests/test_oid_index.py b/tests/test_oid_index.py index 17a6c74..7c4af8a 100644 --- a/tests/test_oid_index.py +++ b/tests/test_oid_index.py @@ -4,11 +4,12 @@ import odmlib.loader as LD import os import odmlib.oid_index as IDX +from tests import get_data_file_path class TestOIDIndex(unittest.TestCase): def setUp(self) -> None: - self.odm_file = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'data', 'cdash-odm-test.xml') + self.odm_file = get_data_file_path('cdash-odm-test.xml') self.loader = LD.ODMLoader(OL.XMLODMLoader()) self.loader.open_odm_document(self.odm_file) self.odm = self.loader.root() @@ -28,7 +29,7 @@ def test_oid_index_find_codelist(self): self.assertEqual(found[1].CodeListOID, "ODM.CL.NY_SUB_Y_N") def test_oid_index_define_igd(self): - self.define_file = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'data', 'defineV21-SDTM.xml') + self.define_file = get_data_file_path('defineV21-SDTM.xml') self.loader = LD.ODMLoader(DL.XMLDefineLoader(model_package="define_2_1", ns_uri="http://www.cdisc.org/ns/def/v2.1")) self.loader.open_odm_document(self.define_file) self.odm = self.loader.root() diff --git a/tests/test_read_dataset.py b/tests/test_read_dataset.py index b1e3b9a..7c2920e 100644 --- a/tests/test_read_dataset.py +++ b/tests/test_read_dataset.py @@ -3,10 +3,12 @@ import odmlib.ns_registry as NS import os +from tests import get_data_file_path + class TestReadDataset(unittest.TestCase): def setUp(self) -> None: - self.odm_file = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'data', 'ae_test.xml') + self.odm_file = get_data_file_path('ae_test.xml') self.loader = OL.XMLODMLoader(model_package="dataset_1_0_1", ns_uri="http://www.cdisc.org/ns/Dataset-XML/v1.0") NS.NamespaceRegistry(prefix="odm", uri="http://www.cdisc.org/ns/odm/v1.3", is_default=True) self.ns = NS.NamespaceRegistry(prefix="data", uri="http://www.cdisc.org/ns/Dataset-XML/v1.0") diff --git a/tests/test_referenceData.py b/tests/test_referenceData.py index 2a273b1..cd48e82 100644 --- a/tests/test_referenceData.py +++ b/tests/test_referenceData.py @@ -22,7 +22,7 @@ def test_reference_data_to_xml(self): root = self.create_odm_document(rd) odm_xml = root.to_xml() self.assertEqual(odm_xml.attrib["FileOID"], "ODM.TEST.RD.001") - self.assertListEqual(["ReferenceData"], [e.tag for e in odm_xml.getchildren()]) + self.assertListEqual(["ReferenceData"], [e.tag for e in odm_xml]) def test_clinical_data_from_xml(self): parser = ODM_PARSER.ODMParser(self.odm_test_file) From f8883b874e4175f021b8bf23c9383a2344313f30 Mon Sep 17 00:00:00 2001 From: Geoff Low Date: Tue, 5 Apr 2022 13:15:31 +0100 Subject: [PATCH 09/26] Use helpers, still missing a file --- tests/test_clinicalData.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/tests/test_clinicalData.py b/tests/test_clinicalData.py index 5b978f6..1301297 100644 --- a/tests/test_clinicalData.py +++ b/tests/test_clinicalData.py @@ -7,13 +7,14 @@ import datetime from odmlib.odm_1_3_2.rules import metadata_schema as METADATA from odmlib.odm_1_3_2.rules import oid_ref as OID +from tests import get_data_file_path class TestClinicalData(TestCase): def setUp(self) -> None: - self.odm_test_file = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'data','test_clinical_data_01.xml') - self.odm_test_file2 = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'data','odm-data-snapshot.xml') + self.odm_test_file = get_data_file_path('test_clinical_data_01.xml') + self.odm_test_file2 = get_data_file_path('odm-data-snapshot.xml') def test_clinical_data_to_xml(self): cd = [] @@ -46,7 +47,7 @@ def test_clinical_data_to_xml(self): root = self.create_odm_document(cd) odm_xml = root.to_xml() self.assertEqual(odm_xml.attrib["FileOID"], "ODM.TEST.CD.001") - self.assertListEqual(["ClinicalData", "ClinicalData"], [e.tag for e in odm_xml.getchildren()]) + self.assertListEqual(["ClinicalData", "ClinicalData"], [e.tag for e in odm_xml]) def test_clinical_data_from_xml(self): From 4af5b3bce7147c8ac59ea266f77ace7877b15023 Mon Sep 17 00:00:00 2001 From: Geoff Low Date: Tue, 5 Apr 2022 13:16:56 +0100 Subject: [PATCH 10/26] Initial pass of the ci pipeline --- .github/workflows/ci.yml | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 .github/workflows/ci.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..c40a3e5 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,32 @@ +name: Python Package + +on: [push] + +jobs: + build: + runs-on: ubuntu-latest + strategy: + fail-fast: true + matrix: + python-version: ["3.7", "3.8", "3.9", "3.10"] + + steps: + - uses: actions/checkout@v2 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v2 + with: + python-version: ${{ matrix.python-version }} + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install flake8 + if [ -f requirements-dev.txt ]; then pip install -r requirements-dev.txt; fi + - name: Lint with flake8 + run: | + # stop the build if there are Python syntax errors or undefined names + flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics + # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide + flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics + - name: Test with pytest + run: | + pytest \ No newline at end of file From 54b16013a231c15f6b0f15a3b9bb7eae03ab418b Mon Sep 17 00:00:00 2001 From: Geoff Low Date: Tue, 5 Apr 2022 13:17:53 +0100 Subject: [PATCH 11/26] Prob need to generate these --- tests/data/ae_test.json | 2 +- tests/data/ae_test.xml | 2 +- tests/data/defineV21-SDTM-test.json | 2 +- tests/data/defineV21-SDTM-test.xml | 2 +- tests/data/test_clinical_data_01.xml | 2 +- tests/data/test_referece_data_01.xml | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/data/ae_test.json b/tests/data/ae_test.json index c266722..aa2ddb7 100644 --- a/tests/data/ae_test.json +++ b/tests/data/ae_test.json @@ -1 +1 @@ -{"FileOID": "ODM.DATASET.001", "AsOfDateTime": "2022-03-20T14:58:25.616094+00:00", "DatasetXMLVersion": "1.0.0", "CreationDateTime": "2022-03-20T14:58:25.616094+00:00", "ODMVersion": "1.3.2", "FileType": "Snapshot", "Originator": "swhume", "SourceSystem": "odmlib", "SourceSystemVersion": "0.1", "ClinicalData": {"StudyOID": "cdisc.odmlib.001", "MetaDataVersionOID": "MDV.001", "ItemGroupData": [{"ItemGroupOID": "IG.AE", "ItemGroupDataSeq": 1, "ItemData": [{"ItemOID": "IT.STUDYID", "Value": "CDISC01"}, {"ItemOID": "IT.AE.DOMAIN", "Value": "AE"}, {"ItemOID": "IT.USUBJID", "Value": "CDISC01.100008"}, {"ItemOID": "IT.AE.AESEQ", "Value": "1"}, {"ItemOID": "IT.AE.AETERM", "Value": "AGITATED"}, {"ItemOID": "IT.AE.AEMODIFY", "Value": "AGITATION"}, {"ItemOID": "IT.AE.AEDECOD", "Value": "Agitation"}, {"ItemOID": "IT.AE.AESEV", "Value": "MILD"}, {"ItemOID": "IT.AE.AESER", "Value": "N"}, {"ItemOID": "IT.AE.AEACN", "Value": "DOSE NOT CHANGED"}, {"ItemOID": "IT.AE.AEREL", "Value": "POSSIBLY RELATED"}]}, {"ItemGroupOID": "IG.AE", "ItemGroupDataSeq": 2, "ItemData": [{"ItemOID": "IT.STUDYID", "Value": "CDISC01"}, {"ItemOID": "IT.AE.DOMAIN", "Value": "AE"}, {"ItemOID": "IT.USUBJID", "Value": "CDISC01.100008"}, {"ItemOID": "IT.AE.AESEQ", "Value": "2"}, {"ItemOID": "IT.AE.AETERM", "Value": "ANXIETY"}, {"ItemOID": "IT.AE.AEMODIFY", "Value": "AGITATION"}, {"ItemOID": "IT.AE.AEDECOD", "Value": "Anxiety"}, {"ItemOID": "IT.AE.AESEV", "Value": "MODERATE"}, {"ItemOID": "IT.AE.AESER", "Value": "N"}, {"ItemOID": "IT.AE.AEACN", "Value": "DOSE NOT CHANGED"}, {"ItemOID": "IT.AE.AEREL", "Value": "POSSIBLY RELATED"}]}]}} \ No newline at end of file +{"FileOID": "ODM.DATASET.001", "AsOfDateTime": "2022-04-05T11:51:36.757671+00:00", "DatasetXMLVersion": "1.0.0", "CreationDateTime": "2022-04-05T11:51:36.757671+00:00", "ODMVersion": "1.3.2", "FileType": "Snapshot", "Originator": "swhume", "SourceSystem": "odmlib", "SourceSystemVersion": "0.1", "ClinicalData": {"StudyOID": "cdisc.odmlib.001", "MetaDataVersionOID": "MDV.001", "ItemGroupData": [{"ItemGroupOID": "IG.AE", "ItemGroupDataSeq": 1, "ItemData": [{"ItemOID": "IT.STUDYID", "Value": "CDISC01"}, {"ItemOID": "IT.AE.DOMAIN", "Value": "AE"}, {"ItemOID": "IT.USUBJID", "Value": "CDISC01.100008"}, {"ItemOID": "IT.AE.AESEQ", "Value": "1"}, {"ItemOID": "IT.AE.AETERM", "Value": "AGITATED"}, {"ItemOID": "IT.AE.AEMODIFY", "Value": "AGITATION"}, {"ItemOID": "IT.AE.AEDECOD", "Value": "Agitation"}, {"ItemOID": "IT.AE.AESEV", "Value": "MILD"}, {"ItemOID": "IT.AE.AESER", "Value": "N"}, {"ItemOID": "IT.AE.AEACN", "Value": "DOSE NOT CHANGED"}, {"ItemOID": "IT.AE.AEREL", "Value": "POSSIBLY RELATED"}]}, {"ItemGroupOID": "IG.AE", "ItemGroupDataSeq": 2, "ItemData": [{"ItemOID": "IT.STUDYID", "Value": "CDISC01"}, {"ItemOID": "IT.AE.DOMAIN", "Value": "AE"}, {"ItemOID": "IT.USUBJID", "Value": "CDISC01.100008"}, {"ItemOID": "IT.AE.AESEQ", "Value": "2"}, {"ItemOID": "IT.AE.AETERM", "Value": "ANXIETY"}, {"ItemOID": "IT.AE.AEMODIFY", "Value": "AGITATION"}, {"ItemOID": "IT.AE.AEDECOD", "Value": "Anxiety"}, {"ItemOID": "IT.AE.AESEV", "Value": "MODERATE"}, {"ItemOID": "IT.AE.AESER", "Value": "N"}, {"ItemOID": "IT.AE.AEACN", "Value": "DOSE NOT CHANGED"}, {"ItemOID": "IT.AE.AEREL", "Value": "POSSIBLY RELATED"}]}]}} \ No newline at end of file diff --git a/tests/data/ae_test.xml b/tests/data/ae_test.xml index 4a69251..14a4583 100644 --- a/tests/data/ae_test.xml +++ b/tests/data/ae_test.xml @@ -1,2 +1,2 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/tests/data/defineV21-SDTM-test.json b/tests/data/defineV21-SDTM-test.json index 7754262..8722bc8 100644 --- a/tests/data/defineV21-SDTM-test.json +++ b/tests/data/defineV21-SDTM-test.json @@ -1 +1 @@ -{"FileOID": "DEFINE.TEST.IGD.001", "FileType": "Snapshot", "AsOfDateTime": "2022-03-20T14:58:26.149574+00:00", "CreationDateTime": "2022-03-20T14:58:26.149601+00:00", "ODMVersion": "1.3.2", "Originator": "CDISC 360", "SourceSystem": "WS2", "SourceSystemVersion": "0.1", "Context": "Other", "Study": {"OID": "ST.TEST.IGD.001", "GlobalVariables": {"StudyName": {"_content": "TEST ODM ItemGroupDef"}, "StudyDescription": {"_content": "ItemGroupDef 001"}, "ProtocolName": {"_content": "ODM ItemGroupDef"}}, "MetaDataVersion": {"OID": "MDV.TEST.IGD.001", "Name": "ItemGroupDefTest001", "Description": "ItemGroupDef Test 001", "DefineVersion": "2.1.0", "ItemGroupDef": [{"OID": "IG.VS", "Name": "VS", "Repeating": "Yes", "Domain": "VS", "SASDatasetName": "VS", "IsReferenceData": "No", "Purpose": "Tabulation", "ArchiveLocationID": "LF.VS", "Structure": "One record per vital sign measurement per visit per subject", "StandardOID": "STD.1", "IsNonStandard": "Yes", "HasNoData": "Yes", "Description": {"TranslatedText": [{"_content": "this is the first test description", "lang": "en"}]}, "ItemRef": [{"ItemOID": "IT.STUDYID", "Mandatory": "Yes", "OrderNumber": 1, "KeySequence": 1}, {"ItemOID": "IT.TA.DOMAIN", "Mandatory": "Yes", "OrderNumber": 2}, {"ItemOID": "IT.TA.ARMCD", "Mandatory": "Yes", "OrderNumber": 3, "KeySequence": 2}, {"ItemOID": "IT.TA.ARM", "Mandatory": "Yes", "OrderNumber": 4}, {"ItemOID": "IT.TA.TAETORD", "Mandatory": "Yes", "OrderNumber": 5, "KeySequence": 3}, {"ItemOID": "IT.TA.ETCD", "Mandatory": "Yes", "OrderNumber": 6}, {"ItemOID": "IT.TA.ELEMENT", "Mandatory": "No", "OrderNumber": 7}, {"ItemOID": "IT.TA.TABRANCH", "Mandatory": "No", "OrderNumber": 8}, {"ItemOID": "IT.TA.TATRANS", "Mandatory": "No", "OrderNumber": 9}, {"ItemOID": "IT.TA.EPOCH", "Mandatory": "No", "OrderNumber": 10}]}]}}} \ No newline at end of file +{"FileOID": "DEFINE.TEST.IGD.001", "FileType": "Snapshot", "AsOfDateTime": "2022-04-05T11:51:37.167651+00:00", "CreationDateTime": "2022-04-05T11:51:37.167661+00:00", "ODMVersion": "1.3.2", "Originator": "CDISC 360", "SourceSystem": "WS2", "SourceSystemVersion": "0.1", "Context": "Other", "Study": {"OID": "ST.TEST.IGD.001", "GlobalVariables": {"StudyName": {"_content": "TEST ODM ItemGroupDef"}, "StudyDescription": {"_content": "ItemGroupDef 001"}, "ProtocolName": {"_content": "ODM ItemGroupDef"}}, "MetaDataVersion": {"OID": "MDV.TEST.IGD.001", "Name": "ItemGroupDefTest001", "Description": "ItemGroupDef Test 001", "DefineVersion": "2.1.0", "ItemGroupDef": [{"OID": "IG.VS", "Name": "VS", "Repeating": "Yes", "Domain": "VS", "SASDatasetName": "VS", "IsReferenceData": "No", "Purpose": "Tabulation", "ArchiveLocationID": "LF.VS", "Structure": "One record per vital sign measurement per visit per subject", "StandardOID": "STD.1", "IsNonStandard": "Yes", "HasNoData": "Yes", "Description": {"TranslatedText": [{"_content": "this is the first test description", "lang": "en"}]}, "ItemRef": [{"ItemOID": "IT.STUDYID", "Mandatory": "Yes", "OrderNumber": 1, "KeySequence": 1}, {"ItemOID": "IT.TA.DOMAIN", "Mandatory": "Yes", "OrderNumber": 2}, {"ItemOID": "IT.TA.ARMCD", "Mandatory": "Yes", "OrderNumber": 3, "KeySequence": 2}, {"ItemOID": "IT.TA.ARM", "Mandatory": "Yes", "OrderNumber": 4}, {"ItemOID": "IT.TA.TAETORD", "Mandatory": "Yes", "OrderNumber": 5, "KeySequence": 3}, {"ItemOID": "IT.TA.ETCD", "Mandatory": "Yes", "OrderNumber": 6}, {"ItemOID": "IT.TA.ELEMENT", "Mandatory": "No", "OrderNumber": 7}, {"ItemOID": "IT.TA.TABRANCH", "Mandatory": "No", "OrderNumber": 8}, {"ItemOID": "IT.TA.TATRANS", "Mandatory": "No", "OrderNumber": 9}, {"ItemOID": "IT.TA.EPOCH", "Mandatory": "No", "OrderNumber": 10}]}]}}} \ No newline at end of file diff --git a/tests/data/defineV21-SDTM-test.xml b/tests/data/defineV21-SDTM-test.xml index 039688c..b1afdef 100644 --- a/tests/data/defineV21-SDTM-test.xml +++ b/tests/data/defineV21-SDTM-test.xml @@ -1,2 +1,2 @@ -TEST ODM ItemGroupDefItemGroupDef 001ODM ItemGroupDefthis is the first test description \ No newline at end of file +TEST ODM ItemGroupDefItemGroupDef 001ODM ItemGroupDefthis is the first test description \ No newline at end of file diff --git a/tests/data/test_clinical_data_01.xml b/tests/data/test_clinical_data_01.xml index 1b900c9..44c276a 100644 --- a/tests/data/test_clinical_data_01.xml +++ b/tests/data/test_clinical_data_01.xml @@ -1,2 +1,2 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/tests/data/test_referece_data_01.xml b/tests/data/test_referece_data_01.xml index f128d40..c01d929 100644 --- a/tests/data/test_referece_data_01.xml +++ b/tests/data/test_referece_data_01.xml @@ -1,2 +1,2 @@ - \ No newline at end of file + \ No newline at end of file From 883665c918af69680ebd3fa01eb477cba7ad9e97 Mon Sep 17 00:00:00 2001 From: Geoff Low Date: Thu, 2 Jun 2022 16:50:52 +0100 Subject: [PATCH 12/26] Added tox --- tox.ini | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/tox.ini b/tox.ini index 60c4777..b2f844b 100644 --- a/tox.ini +++ b/tox.ini @@ -1,6 +1,24 @@ # content of: tox.ini , put in same dir as setup.py [tox] -envlist = py37, py38, py39, py310 +envlist = py37, py38, py39, py310,flake8 + +[flake8] +extend-exclude = N812 +exclude = .git,__pycache__,build,dist +max-complexity = 10 +max-line-length = 120 + +# Linters +[testenv:flake8] +skip_install = true +deps = + flake8 + flake8-bugbear + flake8-docstrings>=1.3.1 + flake8-typing-imports>=1.1 + pep8-naming +commands = + flake8 odmlib/ setup.py # Note `python setup.py test` is deprecated [testenv] From 519720e444ee1b5d112a579e81027c70227dde46 Mon Sep 17 00:00:00 2001 From: Geoff Low Date: Thu, 2 Jun 2022 16:51:18 +0100 Subject: [PATCH 13/26] Updates prior to moving to personal machine --- odmlib/ct_1_1_1/model.py | 24 +- odmlib/dataset_1_0_1/model.py | 8 +- odmlib/define_2_0/model.py | 8 +- odmlib/define_2_0/rules/metadata_schema.py | 780 ++++++++++++----- odmlib/define_2_0/rules/oid_ref.py | 31 +- odmlib/define_2_1/model.py | 25 +- odmlib/define_2_1/rules/metadata_schema.py | 892 ++++++++++++++------ odmlib/define_2_1/rules/oid_ref.py | 32 +- odmlib/define_loader.py | 31 +- odmlib/descriptor.py | 19 +- odmlib/document_loader.py | 24 +- odmlib/loader.py | 11 +- odmlib/ns_registry.py | 15 +- odmlib/odm_1_3_2/model.py | 12 +- odmlib/odm_1_3_2/rules/metadata_schema.py | 931 +++++++++++++++------ odmlib/odm_1_3_2/rules/oid_ref.py | 23 +- odmlib/odm_2_0/model.py | 57 +- odmlib/odm_2_0/valueset.py | 363 +++++++- odmlib/odm_element.py | 115 ++- odmlib/odm_loader.py | 40 +- odmlib/odm_parser.py | 24 +- odmlib/oid_index.py | 7 +- odmlib/typed.py | 123 ++- odmlib/valueset.py | 9 +- requirements-dev.txt | 1 + setup.py | 6 +- tests/data/ae_test.json | 2 +- tests/data/ae_test.xml | 2 +- tests/data/defineV21-SDTM-test.json | 2 +- tests/data/defineV21-SDTM-test.xml | 2 +- tests/data/test_clinical_data_01.xml | 2 +- tests/data/test_odm_002.xml | 2 - tests/data/test_referece_data_01.xml | 2 +- tests/test_attribute_order.py | 176 ++-- tests/test_bottom_up.py | 71 +- tests/test_cerberus_schemas_odm.py | 216 +++-- tests/test_clinicalData.py | 148 ++-- tests/test_codeList.py | 17 +- tests/test_codeListItem.py | 6 +- tests/test_codeList_define.py | 22 +- tests/test_conditiondef.py | 35 +- tests/test_conformance_checks_odm.py | 198 +++-- tests/test_create_dataset.py | 111 ++- tests/test_create_odm.py | 12 +- tests/test_define21_parser_metadata.py | 15 +- tests/test_define_parser_metadata.py | 18 +- tests/test_descriptor.py | 10 +- tests/test_enumeratedItem.py | 3 +- tests/test_extended_alias.py | 3 +- tests/test_formDef.py | 87 +- tests/test_insert_item.py | 7 +- tests/test_itemDef.py | 106 ++- tests/test_itemDef_define.py | 70 +- tests/test_itemGroupDef.py | 97 ++- tests/test_itemGroupDef_define.py | 105 ++- tests/test_itemGroupDef_define21.py | 124 ++- tests/test_json_from_xml.py | 9 +- tests/test_metadataversion.py | 526 ++++++------ tests/test_methodDef.py | 36 +- tests/test_methodDef_check.py | 36 +- tests/test_methodDef_define.py | 36 +- tests/test_ns_registry.py | 81 +- tests/test_odm_json_string_parser.py | 4 +- tests/test_odm_loader.py | 16 +- tests/test_odm_loader_define.py | 10 +- tests/test_odm_loader_define_string.py | 8 +- tests/test_odm_loader_string.py | 10 +- tests/test_odm_parser_metadata.py | 19 +- tests/test_odm_root.py | 538 ++++++------ tests/test_odm_string_parser.py | 4 +- tests/test_oid_index.py | 3 +- tests/test_rangeCheck.py | 66 +- tests/test_read_dataset.py | 19 +- tests/test_referenceData.py | 71 +- tests/test_study.py | 24 +- tests/test_studyEventDef.py | 29 +- tests/test_typed.py | 74 +- tests/test_value_list_define.py | 34 +- 78 files changed, 4681 insertions(+), 2254 deletions(-) delete mode 100644 tests/data/test_odm_002.xml diff --git a/odmlib/ct_1_1_1/model.py b/odmlib/ct_1_1_1/model.py index ed94c9c..84af89a 100644 --- a/odmlib/ct_1_1_1/model.py +++ b/odmlib/ct_1_1_1/model.py @@ -3,7 +3,9 @@ import odmlib.ns_registry as NS -NS.NamespaceRegistry(prefix="odm", uri="http://www.cdisc.org/ns/odm/v1.3", is_default=True) +NS.NamespaceRegistry( + prefix="odm", uri="http://www.cdisc.org/ns/odm/v1.3", is_default=True +) NS.NamespaceRegistry(prefix="nciodm", uri="http://ncicb.nci.nih.gov/xml/odm/EVS/CDISC") NS.NamespaceRegistry(prefix="xs", uri="http://www.w3.org/2001/XMLSchema-instance") NS.NamespaceRegistry(prefix="xml", uri="http://www.w3.org/XML/1998/namespace") @@ -62,8 +64,12 @@ class EnumeratedItem(OE.ODMElement): CodedValue = T.String(required=True) ExtCodeID = T.String(required=True, namespace="nciodm") CDISCSynonym = T.ODMListObject(element_class=CDISCSynonym, namespace="nciodm") - CDISCDefinition = T.ODMObject(required=True, element_class=CDISCDefinition, namespace="nciodm") - PreferredTerm = T.ODMObject(required=True, element_class=PreferredTerm, namespace="nciodm") + CDISCDefinition = T.ODMObject( + required=True, element_class=CDISCDefinition, namespace="nciodm" + ) + PreferredTerm = T.ODMObject( + required=True, element_class=PreferredTerm, namespace="nciodm" + ) class CodeList(OE.ODMElement): @@ -74,9 +80,15 @@ class CodeList(OE.ODMElement): CodeListExtensible = T.ValueSetString(required=True, namespace="nciodm") Description = T.ODMObject(element_class=Description) EnumeratedItem = T.ODMListObject(element_class=EnumeratedItem) - CDISCSubmissionValue = T.ODMObject(required=True, element_class=CDISCSubmissionValue, namespace="nciodm") - CDISCSynonym = T.ODMObject(required=True, element_class=CDISCSynonym, namespace="nciodm") - PreferredTerm = T.ODMObject(required=True, element_class=PreferredTerm, namespace="nciodm") + CDISCSubmissionValue = T.ODMObject( + required=True, element_class=CDISCSubmissionValue, namespace="nciodm" + ) + CDISCSynonym = T.ODMObject( + required=True, element_class=CDISCSynonym, namespace="nciodm" + ) + PreferredTerm = T.ODMObject( + required=True, element_class=PreferredTerm, namespace="nciodm" + ) class MetaDataVersion(OE.ODMElement): diff --git a/odmlib/dataset_1_0_1/model.py b/odmlib/dataset_1_0_1/model.py index 6c57315..dfb35a0 100644 --- a/odmlib/dataset_1_0_1/model.py +++ b/odmlib/dataset_1_0_1/model.py @@ -4,7 +4,9 @@ import odmlib.ns_registry as NS -NS.NamespaceRegistry(prefix="odm", uri="http://www.cdisc.org/ns/odm/v1.3", is_default=True) +NS.NamespaceRegistry( + prefix="odm", uri="http://www.cdisc.org/ns/odm/v1.3", is_default=True +) NS.NamespaceRegistry(prefix="xs", uri="http://www.w3.org/2001/XMLSchema-instance") NS.NamespaceRegistry(prefix="xml", uri="http://www.w3.org/XML/1998/namespace") NS.NamespaceRegistry(prefix="data", uri="http://www.cdisc.org/ns/Dataset-XML/v1.0") @@ -50,7 +52,9 @@ class ODM(OE.ODMElement): PriorFileOID = T.OIDRef(required=False) AsOfDateTime = T.DateTimeString(required=False) ODMVersion = T.ValueSetString(required=False) - DatasetXMLVersion = T.ExtendedValidValues(required=True, valid_values=["1.0.0", "1.0.1"]) + DatasetXMLVersion = T.ExtendedValidValues( + required=True, valid_values=["1.0.0", "1.0.1"] + ) Originator = T.String(required=False) SourceSystem = T.String(required=False) SourceSystemVersion = T.String(required=False) diff --git a/odmlib/define_2_0/model.py b/odmlib/define_2_0/model.py index c9a5515..6ead735 100644 --- a/odmlib/define_2_0/model.py +++ b/odmlib/define_2_0/model.py @@ -52,7 +52,9 @@ class ItemRef(ODM.ItemRef): MethodOID = ODM.ItemRef.MethodOID Role = ODM.ItemRef.Role RoleCodeListOID = ODM.ItemRef.RoleCodeListOID - WhereClauseRef = T.ODMListObject(required=True, element_class=WhereClauseRef, namespace="def") + WhereClauseRef = T.ODMListObject( + required=True, element_class=WhereClauseRef, namespace="def" + ) class title(OE.ODMElement): @@ -213,7 +215,9 @@ class AnnotatedCRF(OE.ODMElement): class SupplementalDoc(OE.ODMElement): namespace = "def" - DocumentRef = T.ODMListObject(required=True, element_class=DocumentRef, namespace="def") + DocumentRef = T.ODMListObject( + required=True, element_class=DocumentRef, namespace="def" + ) class WhereClauseDef(OE.ODMElement): diff --git a/odmlib/define_2_0/rules/metadata_schema.py b/odmlib/define_2_0/rules/metadata_schema.py index 763ae1a..b6adce3 100644 --- a/odmlib/define_2_0/rules/metadata_schema.py +++ b/odmlib/define_2_0/rules/metadata_schema.py @@ -6,11 +6,13 @@ class ConformanceChecker(ABC): @abstractmethod def check_conformance(self, doc, schema_name): raise NotImplementedError( - "Attempted to execute an abstract method validate_tree in the Validator class") + "Attempted to execute an abstract method validate_tree in the Validator class" + ) class MetadataSchema(ConformanceChecker): - """ The metadata schema for Define-XML v2.0 to aid in conformance checking """ + """The metadata schema for Define-XML v2.0 to aid in conformance checking""" + def __init__(self): self._set_metadata_registry() @@ -24,222 +26,564 @@ def check_conformance(self, doc, schema_name): @staticmethod def _set_metadata_registry(): - schema_registry.add("TranslatedText", {"lang": {"type": "string"}, - "_content": {"type": "string", "required": True}}) - - schema_registry.add("Alias", { - "Context": {"type": "string", "required": True}, - "Name": {"type": "string", "required": True} - }) - - schema_registry.add("Description", {"TranslatedText": {"type": "list", - "schema": {"type": "dict", "schema": schema_registry.get("TranslatedText")}}}) + schema_registry.add( + "TranslatedText", + { + "lang": {"type": "string"}, + "_content": {"type": "string", "required": True}, + }, + ) + + schema_registry.add( + "Alias", + { + "Context": {"type": "string", "required": True}, + "Name": {"type": "string", "required": True}, + }, + ) + + schema_registry.add( + "Description", + { + "TranslatedText": { + "type": "list", + "schema": { + "type": "dict", + "schema": schema_registry.get("TranslatedText"), + }, + } + }, + ) schema_registry.add("title", {"_content": {"type": "string", "required": True}}) - schema_registry.add("leaf", { - "ID": {"type": "string", "required": True}, - "href": {"type": "string", "required": True}, - "title": {"type": "dict", "schema": schema_registry.get("title")}}) - - schema_registry.add("WhereClauseRef", { - "WhereClauseOID": {"type": "string", "required": True} - }) - - schema_registry.add("ValueListRef", { - "ValueListOID": {"type": "string", "required": True} - }) - - schema_registry.add("PDFPageRef", { - "Type": {"type": "string", "required": True, "allowed": ["PhysicalRef", "NamedDestination"]}, - "PageRefs": {"type": "string"}, - "FirstPage": {"type": "integer"}, - "LastPage": {"type": "integer"} - }) - - schema_registry.add("DocumentRef", { - "leafID": {"type": "string", "required": True}, - "PDFPageRef": {"type": "list", "schema": {"type": "dict", "schema": schema_registry.get("PDFPageRef")}} - }) - - schema_registry.add("ItemRef", { - "ItemOID": {"type": "string", "required": True}, - "OrderNumber": { "type": "integer", "required": False}, - "Mandatory": { "type": "string", "required": False, "allowed": ["Yes", "No"]}, - "KeySequence": { "type": "integer", "required": False}, - "MethodOID": {"type": "string", "required": False}, - "Role": {"type": "string", "required": False}, - "RoleCodeListOID": {"type": "string", "required": False}, - "WhereClauseRef": {"type": "list", "schema": {"type": "dict", "schema": schema_registry.get("WhereClauseRef")}} - }) - - schema_registry.add("ItemGroupDef", { - "OID": {"type": "string", "required": True}, - "Name": {"type": "string", "required": True}, - "Repeating": {"type": "string", "allowed": ["Yes", "No"]}, - "IsReferenceData": {"type": "string", "allowed": ["Yes", "No"]}, - "SASDatasetName": {"type": "string"}, - "Domain": {"type": "string"}, - "Origin": {"type": "string"}, - "Purpose": {"type": "string"}, - "Structure": {"type": "string", "required": True}, - "Class": {"type": "string", "required": True}, - "ArchiveLocationID": {"type": "string", "required": True}, - "CommentOID": {"type": "string"}, - "Description": {"type": "dict", "schema": schema_registry.get("Description")}, - "ItemRef": {"type": "list", "schema": {"type": "dict", "schema": schema_registry.get("ItemRef")}}, - "Alias": {"type": "list", "schema": {"type": "dict", "schema": schema_registry.get("Alias")}}, - "leaf": {"type": "dict", "schema": schema_registry.get("leaf")} - }) - - schema_registry.add("FormalExpression", { - "Context": {"type": "string", "required": True}, - "_content": {"type": "string", "required": True} - }) - - schema_registry.add("RangeCheck", { - "Comparator": {"type": "string", "allowed": ["LT", "LE", "GT", "GE", "EQ", "NE", "IN", "NOTIN"]}, - "SoftHard": {"type": "string", "allowed": ["Soft", "Hard"]}, - "ItemOID": {"type": "string", "required": True}, - "CheckValue": {"type": "list", "schema": {"type": "dict", "schema": {"_content": {"type": "string"}}}} - }) - - schema_registry.add("Origin", { - "Type": {"type": "string", "required": True, - "allowed": ["CRF", "Derived", "Assigned", "Assigned", "Protocol", "eDT", "Predecessor"]}, - "DocumentRef": {"type": "list", "schema": {"type": "dict", "schema": schema_registry.get("DocumentRef")}}, - "Description": {"type": "dict", "schema": schema_registry.get("Description")} - }) - - schema_registry.add("CodeListRef", { - "CodeListOID": {"type": "string"} - }) - - schema_registry.add("ItemDef", { - "OID": {"type": "string", "required": True}, - "Name": {"type": "string", "required": True}, - "DataType": {"type": "string", "allowed": ["text", "integer", "float", "date", "time", "datetime", "string", - "boolean", "double", "hexBinary", "base64Binary", "hexFloat", - "base64Float", "partialDate", "partialTime", "partialDatetime", - "durationDatetime", "intervalDatetime", "incompleteDatetime", - "incompleteDate", "incompleteTime", "URI"]}, - "Length": {"type": "integer"}, - "SignificantDigits": {"type": "integer"}, - "SASFieldName": {"type": "string"}, - "DisplayFormat": {"type": "string"}, - "CommentOID": {"type": "string"}, - "Description": {"type": "dict", "schema": schema_registry.get("Description")}, - "CodeListRef": {"type": "dict", "schema": schema_registry.get("CodeListRef")}, - "Origin": {"type": "dict", "schema": schema_registry.get("Origin")}, - "ValueListRef": {"type": "dict", "schema": schema_registry.get("ValueListRef")}, - "Alias": {"type": "list", "schema": {"type": "dict", "schema": schema_registry.get("Alias")}} - }) - - schema_registry.add("CodeListItem", { - "CodedValue": {"type": "string", "required": True}, - "Rank": {"type": "float"}, - "OrderNumber": {"type": "integer"}, - "ExtendedValue": {"type": "string", "allowed": ["Yes"]}, - "Decode": {"type": "dict", "schema": {"TranslatedText": {"type": "list", - "schema": {"type": "dict", "schema": schema_registry.get("TranslatedText")}}}}, - "Alias": {"type": "list", "schema": {"type": "dict", "schema": schema_registry.get("Alias")}} - }) - - schema_registry.add("EnumeratedItem", { - "CodedValue": {"type": "string", "required": True}, - "Rank": {"type": "float"}, - "OrderNumber": {"type": "integer"}, - "ExtendedValue": {"type": "string", "allowed": ["Yes"]}, - "Alias": {"type": "list", "schema": {"type": "dict", "schema": schema_registry.get("Alias")}} - }) - - schema_registry.add("ExternalCodeList", { - "Dictionary": {"type": "string"}, - "Version": {"type": "string"}, - "ref": {"type": "string"}, - "href": {"type": "string"} - }) - - schema_registry.add("CodeList", { - "OID": {"type": "string", "required": True}, - "Name": {"type": "string", "required": True}, - "DataType": {"type": "string", "allowed": ["text", "integer", "float", "string"]}, - "SASFormatName": {"type": "string"}, - "Description": {"type": "dict", "schema": schema_registry.get("Description")}, - "CodeListItem": {"type": "list", "schema": {"type": "dict", "schema": schema_registry.get("CodeListItem")}}, - "EnumeratedItem": {"type": "list", "schema": {"type": "dict", "schema": schema_registry.get("EnumeratedItem")}}, - "ExternalCodeList": {"type": "dict", "schema": schema_registry.get("ExternalCodeList")}, - "Alias": {"type": "list", "schema": {"type": "dict", "schema": schema_registry.get("Alias")}} - }) - - schema_registry.add("AnnotatedCRF", { - "DocumentRef": {"type": "dict", "schema": schema_registry.get("DocumentRef")} - }) - - schema_registry.add("SupplementalDoc", { - "DocumentRef": {"type": "list", "schema": {"type": "dict", "schema": schema_registry.get("DocumentRef")}} - }) - - schema_registry.add("WhereClauseDef", { - "OID": {"type": "string", "required": True}, - "CommentOID": {"type": "string"}, - "RangeCheck": {"type": "list", "schema": {"type": "dict", "schema": schema_registry.get("RangeCheck")}} - }) - - schema_registry.add("ValueListDef", { - "OID": {"type": "string", "required": True}, - "ItemRef": {"type": "list", "schema": {"type": "dict", "schema": schema_registry.get("ItemRef")}} - }) - - schema_registry.add("CommentDef", { - "OID": {"type": "string", "required": True}, - "Description": {"type": "dict", "schema": schema_registry.get("Description")}, - "DocumentRef": {"type": "list", "schema": {"type": "dict", "schema": schema_registry.get("DocumentRef")}} - }) - - schema_registry.add("MethodDef", { - "OID": {"type": "string", "required": True}, - "Name": {"type": "string", "required": True}, - "Type": {"type": "string", "required": True, "allowed": ["Computation", "Imputation", "Transpose", "Other"]}, - "Description": {"type": "dict", "schema": schema_registry.get("Description")}, - "FormalExpression": {"type": "list", "schema": {"type": "dict", "schema": schema_registry.get("FormalExpression")}}, - "Alias": {"type": "list", "schema": {"type": "dict", "schema": schema_registry.get("Alias")}}, - "DocumentRef": {"type": "list", "schema": {"type": "dict", "schema": schema_registry.get("DocumentRef")}} - }) - - schema_registry.add("MetaDataVersion", { - "OID": {"type": "string", "required": True}, - "Name": {"type": "string", "required": True}, - "Description": {"type": "string"}, - "DefineVersion": {"type": "string", "required": True}, - "StandardName": {"type": "string", "required": True}, - "StandardVersion": {"type": "string", "required": True}, - "AnnotatedCRF": {"type": "dict", "schema": schema_registry.get("AnnotatedCRF")}, - "SupplementalDoc": {"type": "dict", "schema": schema_registry.get("SupplementalDoc")}, - "ValueListDef": {"type": "list", "schema": {"type": "dict", "schema": schema_registry.get("ValueListDef")}}, - "WhereClauseDef": {"type": "list", "schema": {"type": "dict", "schema": schema_registry.get("WhereClauseDef")}}, - "ItemGroupDef": {"type": "list", "schema": {"type": "dict", "schema": schema_registry.get("ItemGroupDef")}}, - "ItemDef": {"type": "list", "schema": {"type": "dict", "schema": schema_registry.get("ItemDef")}}, - "CodeList": {"type": "list", "schema": {"type": "dict", "schema": schema_registry.get("CodeList")}}, - "MethodDef": {"type": "list", "schema": {"type": "dict", "schema": schema_registry.get("MethodDef")}}, - "CommentDef": {"type": "list", "schema": {"type": "dict", "schema": schema_registry.get("CommentDef")}}, - "leaf": {"type": "list", "schema": {"type": "dict", "schema": schema_registry.get("leaf")}} - }) - - schema_registry.add("StudyName", {"_content": {"type": "string", "required": True}}) - - schema_registry.add("StudyDescription", {"_content": {"type": "string", "required": True}}) - - schema_registry.add("ProtocolName", {"_content": {"type": "string", "required": True}}) - - schema_registry.add("GlobalVariables", { - "StudyName": {"type": "dict", "schema": schema_registry.get("StudyName")}, - "StudyDescription": {"type": "dict", "schema": schema_registry.get("StudyDescription")}, - "ProtocolName": {"type": "dict", "schema": schema_registry.get("ProtocolName")} - }) - - schema_registry.add("Study", { - "OID": {"type": "string", "required": True}, - "GlobalVariables": {"type": "dict", "schema": schema_registry.get("GlobalVariables")}, - "MetaDataVersion": {"type": "dict", "schema": schema_registry.get("MetaDataVersion")} - }) + schema_registry.add( + "leaf", + { + "ID": {"type": "string", "required": True}, + "href": {"type": "string", "required": True}, + "title": {"type": "dict", "schema": schema_registry.get("title")}, + }, + ) + + schema_registry.add( + "WhereClauseRef", {"WhereClauseOID": {"type": "string", "required": True}} + ) + + schema_registry.add( + "ValueListRef", {"ValueListOID": {"type": "string", "required": True}} + ) + + schema_registry.add( + "PDFPageRef", + { + "Type": { + "type": "string", + "required": True, + "allowed": ["PhysicalRef", "NamedDestination"], + }, + "PageRefs": {"type": "string"}, + "FirstPage": {"type": "integer"}, + "LastPage": {"type": "integer"}, + }, + ) + + schema_registry.add( + "DocumentRef", + { + "leafID": {"type": "string", "required": True}, + "PDFPageRef": { + "type": "list", + "schema": { + "type": "dict", + "schema": schema_registry.get("PDFPageRef"), + }, + }, + }, + ) + + schema_registry.add( + "ItemRef", + { + "ItemOID": {"type": "string", "required": True}, + "OrderNumber": {"type": "integer", "required": False}, + "Mandatory": { + "type": "string", + "required": False, + "allowed": ["Yes", "No"], + }, + "KeySequence": {"type": "integer", "required": False}, + "MethodOID": {"type": "string", "required": False}, + "Role": {"type": "string", "required": False}, + "RoleCodeListOID": {"type": "string", "required": False}, + "WhereClauseRef": { + "type": "list", + "schema": { + "type": "dict", + "schema": schema_registry.get("WhereClauseRef"), + }, + }, + }, + ) + + schema_registry.add( + "ItemGroupDef", + { + "OID": {"type": "string", "required": True}, + "Name": {"type": "string", "required": True}, + "Repeating": {"type": "string", "allowed": ["Yes", "No"]}, + "IsReferenceData": {"type": "string", "allowed": ["Yes", "No"]}, + "SASDatasetName": {"type": "string"}, + "Domain": {"type": "string"}, + "Origin": {"type": "string"}, + "Purpose": {"type": "string"}, + "Structure": {"type": "string", "required": True}, + "Class": {"type": "string", "required": True}, + "ArchiveLocationID": {"type": "string", "required": True}, + "CommentOID": {"type": "string"}, + "Description": { + "type": "dict", + "schema": schema_registry.get("Description"), + }, + "ItemRef": { + "type": "list", + "schema": { + "type": "dict", + "schema": schema_registry.get("ItemRef"), + }, + }, + "Alias": { + "type": "list", + "schema": {"type": "dict", "schema": schema_registry.get("Alias")}, + }, + "leaf": {"type": "dict", "schema": schema_registry.get("leaf")}, + }, + ) + + schema_registry.add( + "FormalExpression", + { + "Context": {"type": "string", "required": True}, + "_content": {"type": "string", "required": True}, + }, + ) + + schema_registry.add( + "RangeCheck", + { + "Comparator": { + "type": "string", + "allowed": ["LT", "LE", "GT", "GE", "EQ", "NE", "IN", "NOTIN"], + }, + "SoftHard": {"type": "string", "allowed": ["Soft", "Hard"]}, + "ItemOID": {"type": "string", "required": True}, + "CheckValue": { + "type": "list", + "schema": { + "type": "dict", + "schema": {"_content": {"type": "string"}}, + }, + }, + }, + ) + + schema_registry.add( + "Origin", + { + "Type": { + "type": "string", + "required": True, + "allowed": [ + "CRF", + "Derived", + "Assigned", + "Assigned", + "Protocol", + "eDT", + "Predecessor", + ], + }, + "DocumentRef": { + "type": "list", + "schema": { + "type": "dict", + "schema": schema_registry.get("DocumentRef"), + }, + }, + "Description": { + "type": "dict", + "schema": schema_registry.get("Description"), + }, + }, + ) + + schema_registry.add("CodeListRef", {"CodeListOID": {"type": "string"}}) + + schema_registry.add( + "ItemDef", + { + "OID": {"type": "string", "required": True}, + "Name": {"type": "string", "required": True}, + "DataType": { + "type": "string", + "allowed": [ + "text", + "integer", + "float", + "date", + "time", + "datetime", + "string", + "boolean", + "double", + "hexBinary", + "base64Binary", + "hexFloat", + "base64Float", + "partialDate", + "partialTime", + "partialDatetime", + "durationDatetime", + "intervalDatetime", + "incompleteDatetime", + "incompleteDate", + "incompleteTime", + "URI", + ], + }, + "Length": {"type": "integer"}, + "SignificantDigits": {"type": "integer"}, + "SASFieldName": {"type": "string"}, + "DisplayFormat": {"type": "string"}, + "CommentOID": {"type": "string"}, + "Description": { + "type": "dict", + "schema": schema_registry.get("Description"), + }, + "CodeListRef": { + "type": "dict", + "schema": schema_registry.get("CodeListRef"), + }, + "Origin": {"type": "dict", "schema": schema_registry.get("Origin")}, + "ValueListRef": { + "type": "dict", + "schema": schema_registry.get("ValueListRef"), + }, + "Alias": { + "type": "list", + "schema": {"type": "dict", "schema": schema_registry.get("Alias")}, + }, + }, + ) + + schema_registry.add( + "CodeListItem", + { + "CodedValue": {"type": "string", "required": True}, + "Rank": {"type": "float"}, + "OrderNumber": {"type": "integer"}, + "ExtendedValue": {"type": "string", "allowed": ["Yes"]}, + "Decode": { + "type": "dict", + "schema": { + "TranslatedText": { + "type": "list", + "schema": { + "type": "dict", + "schema": schema_registry.get("TranslatedText"), + }, + } + }, + }, + "Alias": { + "type": "list", + "schema": {"type": "dict", "schema": schema_registry.get("Alias")}, + }, + }, + ) + + schema_registry.add( + "EnumeratedItem", + { + "CodedValue": {"type": "string", "required": True}, + "Rank": {"type": "float"}, + "OrderNumber": {"type": "integer"}, + "ExtendedValue": {"type": "string", "allowed": ["Yes"]}, + "Alias": { + "type": "list", + "schema": {"type": "dict", "schema": schema_registry.get("Alias")}, + }, + }, + ) + + schema_registry.add( + "ExternalCodeList", + { + "Dictionary": {"type": "string"}, + "Version": {"type": "string"}, + "ref": {"type": "string"}, + "href": {"type": "string"}, + }, + ) + + schema_registry.add( + "CodeList", + { + "OID": {"type": "string", "required": True}, + "Name": {"type": "string", "required": True}, + "DataType": { + "type": "string", + "allowed": ["text", "integer", "float", "string"], + }, + "SASFormatName": {"type": "string"}, + "Description": { + "type": "dict", + "schema": schema_registry.get("Description"), + }, + "CodeListItem": { + "type": "list", + "schema": { + "type": "dict", + "schema": schema_registry.get("CodeListItem"), + }, + }, + "EnumeratedItem": { + "type": "list", + "schema": { + "type": "dict", + "schema": schema_registry.get("EnumeratedItem"), + }, + }, + "ExternalCodeList": { + "type": "dict", + "schema": schema_registry.get("ExternalCodeList"), + }, + "Alias": { + "type": "list", + "schema": {"type": "dict", "schema": schema_registry.get("Alias")}, + }, + }, + ) + + schema_registry.add( + "AnnotatedCRF", + { + "DocumentRef": { + "type": "dict", + "schema": schema_registry.get("DocumentRef"), + } + }, + ) + + schema_registry.add( + "SupplementalDoc", + { + "DocumentRef": { + "type": "list", + "schema": { + "type": "dict", + "schema": schema_registry.get("DocumentRef"), + }, + } + }, + ) + + schema_registry.add( + "WhereClauseDef", + { + "OID": {"type": "string", "required": True}, + "CommentOID": {"type": "string"}, + "RangeCheck": { + "type": "list", + "schema": { + "type": "dict", + "schema": schema_registry.get("RangeCheck"), + }, + }, + }, + ) + + schema_registry.add( + "ValueListDef", + { + "OID": {"type": "string", "required": True}, + "ItemRef": { + "type": "list", + "schema": { + "type": "dict", + "schema": schema_registry.get("ItemRef"), + }, + }, + }, + ) + + schema_registry.add( + "CommentDef", + { + "OID": {"type": "string", "required": True}, + "Description": { + "type": "dict", + "schema": schema_registry.get("Description"), + }, + "DocumentRef": { + "type": "list", + "schema": { + "type": "dict", + "schema": schema_registry.get("DocumentRef"), + }, + }, + }, + ) + + schema_registry.add( + "MethodDef", + { + "OID": {"type": "string", "required": True}, + "Name": {"type": "string", "required": True}, + "Type": { + "type": "string", + "required": True, + "allowed": ["Computation", "Imputation", "Transpose", "Other"], + }, + "Description": { + "type": "dict", + "schema": schema_registry.get("Description"), + }, + "FormalExpression": { + "type": "list", + "schema": { + "type": "dict", + "schema": schema_registry.get("FormalExpression"), + }, + }, + "Alias": { + "type": "list", + "schema": {"type": "dict", "schema": schema_registry.get("Alias")}, + }, + "DocumentRef": { + "type": "list", + "schema": { + "type": "dict", + "schema": schema_registry.get("DocumentRef"), + }, + }, + }, + ) + + schema_registry.add( + "MetaDataVersion", + { + "OID": {"type": "string", "required": True}, + "Name": {"type": "string", "required": True}, + "Description": {"type": "string"}, + "DefineVersion": {"type": "string", "required": True}, + "StandardName": {"type": "string", "required": True}, + "StandardVersion": {"type": "string", "required": True}, + "AnnotatedCRF": { + "type": "dict", + "schema": schema_registry.get("AnnotatedCRF"), + }, + "SupplementalDoc": { + "type": "dict", + "schema": schema_registry.get("SupplementalDoc"), + }, + "ValueListDef": { + "type": "list", + "schema": { + "type": "dict", + "schema": schema_registry.get("ValueListDef"), + }, + }, + "WhereClauseDef": { + "type": "list", + "schema": { + "type": "dict", + "schema": schema_registry.get("WhereClauseDef"), + }, + }, + "ItemGroupDef": { + "type": "list", + "schema": { + "type": "dict", + "schema": schema_registry.get("ItemGroupDef"), + }, + }, + "ItemDef": { + "type": "list", + "schema": { + "type": "dict", + "schema": schema_registry.get("ItemDef"), + }, + }, + "CodeList": { + "type": "list", + "schema": { + "type": "dict", + "schema": schema_registry.get("CodeList"), + }, + }, + "MethodDef": { + "type": "list", + "schema": { + "type": "dict", + "schema": schema_registry.get("MethodDef"), + }, + }, + "CommentDef": { + "type": "list", + "schema": { + "type": "dict", + "schema": schema_registry.get("CommentDef"), + }, + }, + "leaf": { + "type": "list", + "schema": {"type": "dict", "schema": schema_registry.get("leaf")}, + }, + }, + ) + + schema_registry.add( + "StudyName", {"_content": {"type": "string", "required": True}} + ) + + schema_registry.add( + "StudyDescription", {"_content": {"type": "string", "required": True}} + ) + + schema_registry.add( + "ProtocolName", {"_content": {"type": "string", "required": True}} + ) + + schema_registry.add( + "GlobalVariables", + { + "StudyName": { + "type": "dict", + "schema": schema_registry.get("StudyName"), + }, + "StudyDescription": { + "type": "dict", + "schema": schema_registry.get("StudyDescription"), + }, + "ProtocolName": { + "type": "dict", + "schema": schema_registry.get("ProtocolName"), + }, + }, + ) + + schema_registry.add( + "Study", + { + "OID": {"type": "string", "required": True}, + "GlobalVariables": { + "type": "dict", + "schema": schema_registry.get("GlobalVariables"), + }, + "MetaDataVersion": { + "type": "dict", + "schema": schema_registry.get("MetaDataVersion"), + }, + }, + ) diff --git a/odmlib/define_2_0/rules/oid_ref.py b/odmlib/define_2_0/rules/oid_ref.py index 1fc52c6..e7ff069 100644 --- a/odmlib/define_2_0/rules/oid_ref.py +++ b/odmlib/define_2_0/rules/oid_ref.py @@ -1,4 +1,3 @@ - class OIDRef: def __init__(self, skip_attrs=[], skip_elems=[]): self.oid = {} @@ -8,12 +7,23 @@ def __init__(self, skip_attrs=[], skip_elems=[]): self._init_ref_def() self.def_ref = {} self._init_def_ref() - self.skip_attr = ["FileOID", "PriorFileOID", "StudyOID", "MetaDataVersionOID", "ItemGroupOID"] + skip_attrs - self.skip_elem = ["ODM", "Study", "MetaDataVersion", "ItemGroupDef"] + skip_elems + self.skip_attr = [ + "FileOID", + "PriorFileOID", + "StudyOID", + "MetaDataVersionOID", + "ItemGroupOID", + ] + skip_attrs + self.skip_elem = [ + "ODM", + "Study", + "MetaDataVersion", + "ItemGroupDef", + ] + skip_elems self.is_verified = False def add_oid(self, oid, element): - """ ODMLIB expects all OIDs to be unique within the scope of an ODM document """ + """ODMLIB expects all OIDs to be unique within the scope of an ODM document""" if oid in self.oid: raise ValueError(f"OID {oid} is not unique - element {element}") if element not in self.skip_elem: @@ -34,14 +44,18 @@ def check_oid_refs(self): for oid in oid_set: if attr not in self.skip_attr: if oid not in self.oid: - raise ValueError(f"OID {oid} referenced in the attribute {attr} is not found.") + raise ValueError( + f"OID {oid} referenced in the attribute {attr} is not found." + ) elif self.ref_def.get(attr) != self.oid.get(oid): - raise ValueError(f"OID reference for attribute {attr} element types do not match: " - f"{self.ref_def.get(attr)} and {self.oid.get(oid)}") + raise ValueError( + f"OID reference for attribute {attr} element types do not match: " + f"{self.ref_def.get(attr)} and {self.oid.get(oid)}" + ) return True def check_unreferenced_oids(self): - """ identify ELEMENTS that are defined but not used """ + """identify ELEMENTS that are defined but not used""" orphans = {} for oid, elem in self.oid.items(): for ref in self.def_ref[elem]: @@ -71,7 +85,6 @@ def _init_oid_ref(self): self.oid_ref["ArchiveLayoutOID"] = set() self.oid_ref["leafID"] = set() - def _init_ref_def(self): # self.ref_def["MetaDataVersionOID"] = "MetaDataVersion" # self.ref_def["StudyOID"] = "Study" diff --git a/odmlib/define_2_1/model.py b/odmlib/define_2_1/model.py index 8fe2d44..c9d2edb 100644 --- a/odmlib/define_2_1/model.py +++ b/odmlib/define_2_1/model.py @@ -54,7 +54,9 @@ class ItemRef(ODM.ItemRef): RoleCodeListOID = ODM.ItemRef.RoleCodeListOID IsNonStandard = T.ValueSetString(namespace="def") HasNoData = T.ValueSetString(namespace="def") - WhereClauseRef = T.ODMListObject(required=True, element_class=WhereClauseRef, namespace="def") + WhereClauseRef = T.ODMListObject( + required=True, element_class=WhereClauseRef, namespace="def" + ) class title(OE.ODMElement): @@ -153,9 +155,20 @@ class DocumentRef(OE.ODMElement): class Origin(OE.ODMElement): namespace = "def" - Type = T.ExtendedValidValues(required=True, valid_values=["Collected", "Derived", "Assigned", "Protocol", - "Predecessor", "Not Available"]) - Source = T.ExtendedValidValues(valid_values=["Subject", "Investigator", "Vendor", "Sponsor"]) + Type = T.ExtendedValidValues( + required=True, + valid_values=[ + "Collected", + "Derived", + "Assigned", + "Protocol", + "Predecessor", + "Not Available", + ], + ) + Source = T.ExtendedValidValues( + valid_values=["Subject", "Investigator", "Vendor", "Sponsor"] + ) Description = T.ODMObject(element_class=Description) DocumentRef = T.ODMListObject(element_class=DocumentRef, namespace="def") @@ -237,7 +250,9 @@ class AnnotatedCRF(OE.ODMElement): class SupplementalDoc(OE.ODMElement): namespace = "def" - DocumentRef = T.ODMListObject(required=True, element_class=DocumentRef, namespace="def") + DocumentRef = T.ODMListObject( + required=True, element_class=DocumentRef, namespace="def" + ) class WhereClauseDef(OE.ODMElement): diff --git a/odmlib/define_2_1/rules/metadata_schema.py b/odmlib/define_2_1/rules/metadata_schema.py index fef9cc7..941ef90 100644 --- a/odmlib/define_2_1/rules/metadata_schema.py +++ b/odmlib/define_2_1/rules/metadata_schema.py @@ -6,11 +6,13 @@ class ConformanceChecker(ABC): @abstractmethod def check_conformance(self, doc, schema_name): raise NotImplementedError( - "Attempted to execute an abstract method validate_tree in the Validator class") + "Attempted to execute an abstract method validate_tree in the Validator class" + ) class MetadataSchema(ConformanceChecker): - """ The metadata schema for Define-XML v2.1 to aid in conformance checking """ + """The metadata schema for Define-XML v2.1 to aid in conformance checking""" + def __init__(self): self._set_metadata_registry() @@ -24,260 +26,638 @@ def check_conformance(self, doc, schema_name): @staticmethod def _set_metadata_registry(): - schema_registry.add("TranslatedText", {"lang": {"type": "string"}, - "_content": {"type": "string", "required": True}}) - - schema_registry.add("Alias", { - "Context": {"type": "string", "required": True}, - "Name": {"type": "string", "required": True} - }) - - schema_registry.add("Description", {"TranslatedText": {"type": "list", - "schema": {"type": "dict", "schema": schema_registry.get("TranslatedText")}}}) + schema_registry.add( + "TranslatedText", + { + "lang": {"type": "string"}, + "_content": {"type": "string", "required": True}, + }, + ) + + schema_registry.add( + "Alias", + { + "Context": {"type": "string", "required": True}, + "Name": {"type": "string", "required": True}, + }, + ) + + schema_registry.add( + "Description", + { + "TranslatedText": { + "type": "list", + "schema": { + "type": "dict", + "schema": schema_registry.get("TranslatedText"), + }, + } + }, + ) schema_registry.add("title", {"_content": {"type": "string", "required": True}}) - schema_registry.add("leaf", { - "ID": {"type": "string", "required": True}, - "href": {"type": "string", "required": True}, - "title": {"type": "dict", "schema": schema_registry.get("title")}}) - - schema_registry.add("WhereClauseRef", { - "WhereClauseOID": {"type": "string", "required": True} - }) - - schema_registry.add("ValueListRef", { - "ValueListOID": {"type": "string", "required": True} - }) - - schema_registry.add("PDFPageRef", { - "Type": {"type": "string", "required": True, "allowed": ["PhysicalRef", "NamedDestination"]}, - "PageRefs": {"type": "string"}, - "FirstPage": {"type": "integer"}, - "LastPage": {"type": "integer"}, - "Title": {"type": "string"} - }) - - schema_registry.add("DocumentRef", { - "leafID": {"type": "string", "required": True}, - "PDFPageRef": {"type": "list", "schema": {"type": "dict", "schema": schema_registry.get("PDFPageRef")}} - }) - - schema_registry.add("ItemRef", { - "ItemOID": {"type": "string", "required": True}, - "OrderNumber": { "type": "integer", "required": False}, - "Mandatory": { "type": "string", "required": False, "allowed": ["Yes", "No"]}, - "KeySequence": { "type": "integer", "required": False}, - "MethodOID": {"type": "string", "required": False}, - "Role": {"type": "string", "required": False}, - "RoleCodeListOID": {"type": "string", "required": False}, - "IsNonStandard": {"type": "string", "allowed": ["Yes"]}, - "HasNoData": {"type": "string", "allowed": ["Yes"]}, - "WhereClauseRef": {"type": "list", "schema": {"type": "dict", "schema": schema_registry.get("WhereClauseRef")}} - }) - - schema_registry.add("SubClass", { - "Name": {"type": "string"}, - "ParentClass": {"type": "string"} - }) - - schema_registry.add("Class", { - "Name": {"type": "string"}, - "SubClass": {"type": "list", "schema": {"type": "dict", "schema": schema_registry.get("SubClass")}} - }) - - schema_registry.add("ItemGroupDef", { - "OID": {"type": "string", "required": True}, - "Name": {"type": "string", "required": True}, - "Repeating": {"type": "string", "allowed": ["Yes", "No"]}, - "IsReferenceData": {"type": "string", "allowed": ["Yes", "No"]}, - "SASDatasetName": {"type": "string"}, - "Domain": {"type": "string"}, - "Origin": {"type": "string"}, - "Purpose": {"type": "string"}, - "Structure": {"type": "string", "required": True}, - "ArchiveLocationID": {"type": "string"}, - "CommentOID": {"type": "string"}, - "IsNonStandard": {"type": "string", "allowed": ["Yes"]}, - "StandardOID": {"type": "string"}, - "HasNoData": {"type": "string", "allowed": ["Yes"]}, - "Description": {"type": "dict", "schema": schema_registry.get("Description")}, - "ItemRef": {"type": "list", "schema": {"type": "dict", "schema": schema_registry.get("ItemRef")}}, - "Alias": {"type": "list", "schema": {"type": "dict", "schema": schema_registry.get("Alias")}}, - "Class": {"type": "dict", "schema": schema_registry.get("Class")}, - "leaf": {"type": "dict", "schema": schema_registry.get("leaf")} - }) - - schema_registry.add("FormalExpression", { - "Context": {"type": "string", "required": True}, - "_content": {"type": "string", "required": True} - }) - - schema_registry.add("RangeCheck", { - "Comparator": {"type": "string", "allowed": ["LT", "LE", "GT", "GE", "EQ", "NE", "IN", "NOTIN"]}, - "SoftHard": {"type": "string", "allowed": ["Soft", "Hard"]}, - "ItemOID": {"type": "string", "required": True}, - "CheckValue": {"type": "list", "schema": {"type": "dict", "schema": {"_content": {"type": "string"}}}} - }) - - schema_registry.add("Origin", { - "Type": {"type": "string", "required": True, - "allowed": ["Collected", "Derived", "Assigned", "Protocol", "Predecessor", "Not Available"]}, - "Source": {"type": "string", "allowed": ["Subject", "Investigator", "Vendor", "Sponsor"]}, - "DocumentRef": {"type": "list", "schema": {"type": "dict", "schema": schema_registry.get("DocumentRef")}}, - "Description": {"type": "dict", "schema": schema_registry.get("Description")} - }) - - schema_registry.add("CodeListRef", { - "CodeListOID": {"type": "string"} - }) - - schema_registry.add("ItemDef", { - "OID": {"type": "string", "required": True}, - "Name": {"type": "string", "required": True}, - "DataType": {"type": "string", "allowed": ["text", "integer", "float", "date", "time", "datetime", "string", - "boolean", "double", "hexBinary", "base64Binary", "hexFloat", - "base64Float", "partialDate", "partialTime", "partialDatetime", - "durationDatetime", "intervalDatetime", "incompleteDatetime", - "incompleteDate", "incompleteTime", "URI"]}, - "Length": {"type": "integer"}, - "SignificantDigits": {"type": "integer"}, - "SASFieldName": {"type": "string"}, - "DisplayFormat": {"type": "string"}, - "CommentOID": {"type": "string"}, - "Description": {"type": "dict", "schema": schema_registry.get("Description")}, - "CodeListRef": {"type": "dict", "schema": schema_registry.get("CodeListRef")}, - "Origin": {"type": "list", "schema": {"type": "dict", "schema": schema_registry.get("Origin")}}, - "ValueListRef": {"type": "dict", "schema": schema_registry.get("ValueListRef")}, - "Alias": {"type": "list", "schema": {"type": "dict", "schema": schema_registry.get("Alias")}} - }) - - schema_registry.add("CodeListItem", { - "CodedValue": {"type": "string", "required": True}, - "Rank": {"type": "float"}, - "OrderNumber": {"type": "integer"}, - "ExtendedValue": {"type": "string", "allowed": ["Yes"]}, - "Description": {"type": "dict", "schema": schema_registry.get("Description")}, - "Decode": {"type": "dict", "schema": {"TranslatedText": {"type": "list", - "schema": {"type": "dict", "schema": schema_registry.get("TranslatedText")}}}}, - "Alias": {"type": "list", "schema": {"type": "dict", "schema": schema_registry.get("Alias")}} - }) - - schema_registry.add("EnumeratedItem", { - "CodedValue": {"type": "string", "required": True}, - "Rank": {"type": "float"}, - "OrderNumber": {"type": "integer"}, - "ExtendedValue": {"type": "string", "allowed": ["Yes"]}, - "Description": {"type": "dict", "schema": schema_registry.get("Description")}, - "Alias": {"type": "list", "schema": {"type": "dict", "schema": schema_registry.get("Alias")}} - }) - - schema_registry.add("ExternalCodeList", { - "Dictionary": {"type": "string"}, - "Version": {"type": "string"}, - "ref": {"type": "string"}, - "href": {"type": "string"} - }) - - schema_registry.add("CodeList", { - "OID": {"type": "string", "required": True}, - "Name": {"type": "string", "required": True}, - "DataType": {"type": "string", "allowed": ["text", "integer", "float", "string"]}, - "IsNonStandard": {"type": "string", "allowed": ["Yes"]}, - "StandardOID": {"type": "string"}, - "CommentOID": {"type": "string"}, - "SASFormatName": {"type": "string"}, - "Description": {"type": "dict", "schema": schema_registry.get("Description")}, - "CodeListItem": {"type": "list", "schema": {"type": "dict", "schema": schema_registry.get("CodeListItem")}}, - "EnumeratedItem": {"type": "list", "schema": {"type": "dict", "schema": schema_registry.get("EnumeratedItem")}}, - "ExternalCodeList": {"type": "dict", "schema": schema_registry.get("ExternalCodeList")}, - "Alias": {"type": "list", "schema": {"type": "dict", "schema": schema_registry.get("Alias")}} - }) - - schema_registry.add("AnnotatedCRF", { - "DocumentRef": {"type": "dict", "schema": schema_registry.get("DocumentRef")} - }) - - schema_registry.add("SupplementalDoc", { - "DocumentRef": {"type": "list", "schema": {"type": "dict", "schema": schema_registry.get("DocumentRef")}} - }) - - schema_registry.add("WhereClauseDef", { - "OID": {"type": "string", "required": True}, - "CommentOID": {"type": "string"}, - "RangeCheck": {"type": "list", "schema": {"type": "dict", "schema": schema_registry.get("RangeCheck")}} - }) - - schema_registry.add("ValueListDef", { - "OID": {"type": "string", "required": True}, - "Description": {"type": "dict", "schema": schema_registry.get("Description")}, - "ItemRef": {"type": "list", "schema": {"type": "dict", "schema": schema_registry.get("ItemRef")}} - }) - - schema_registry.add("CommentDef", { - "OID": {"type": "string", "required": True}, - "Description": {"type": "dict", "schema": schema_registry.get("Description")}, - "DocumentRef": {"type": "list", "schema": {"type": "dict", "schema": schema_registry.get("DocumentRef")}} - }) - - schema_registry.add("MethodDef", { - "OID": {"type": "string", "required": True}, - "Name": {"type": "string", "required": True}, - "Type": {"type": "string", "required": True, "allowed": ["Computation", "Imputation", "Transpose", "Other"]}, - "Description": {"type": "dict", "schema": schema_registry.get("Description")}, - "FormalExpression": {"type": "list", "schema": {"type": "dict", "schema": schema_registry.get("FormalExpression")}}, - "Alias": {"type": "list", "schema": {"type": "dict", "schema": schema_registry.get("Alias")}}, - "DocumentRef": {"type": "list", "schema": {"type": "dict", "schema": schema_registry.get("DocumentRef")}} - }) - - schema_registry.add("Standard", { - "OID": {"type": "string", "required": True}, - "Name": {"type": "string", "required": True}, - "Type": {"type": "string", "required": True}, - "PublishingSet": {"type": "string"}, - "Version": {"type": "string", "required": True}, - "Status": {"type": "string"}, - "CommentOID": {"type": "string"} - }) - - schema_registry.add("Standards", { - "Standard": {"type": "list", "schema": {"type": "dict", "schema": schema_registry.get("Standard")}} - }) - - schema_registry.add("MetaDataVersion", { - "OID": {"type": "string", "required": True}, - "Name": {"type": "string", "required": True}, - "Description": {"type": "string"}, - "DefineVersion": {"type": "string", "required": True}, - "CommentOID": {"type": "string"}, - "Standards": {"type": "dict", "schema": schema_registry.get("Standards")}, - "AnnotatedCRF": {"type": "dict", "schema": schema_registry.get("AnnotatedCRF")}, - "SupplementalDoc": {"type": "dict", "schema": schema_registry.get("SupplementalDoc")}, - "ValueListDef": {"type": "list", "schema": {"type": "dict", "schema": schema_registry.get("ValueListDef")}}, - "WhereClauseDef": {"type": "list", "schema": {"type": "dict", "schema": schema_registry.get("WhereClauseDef")}}, - "ItemGroupDef": {"type": "list", "schema": {"type": "dict", "schema": schema_registry.get("ItemGroupDef")}}, - "ItemDef": {"type": "list", "schema": {"type": "dict", "schema": schema_registry.get("ItemDef")}}, - "CodeList": {"type": "list", "schema": {"type": "dict", "schema": schema_registry.get("CodeList")}}, - "MethodDef": {"type": "list", "schema": {"type": "dict", "schema": schema_registry.get("MethodDef")}}, - "CommentDef": {"type": "list", "schema": {"type": "dict", "schema": schema_registry.get("CommentDef")}}, - "leaf": {"type": "list", "schema": {"type": "dict", "schema": schema_registry.get("leaf")}} - }) - - - schema_registry.add("StudyName", {"_content": {"type": "string", "required": True}}) - - schema_registry.add("StudyDescription", {"_content": {"type": "string", "required": True}}) - - schema_registry.add("ProtocolName", {"_content": {"type": "string", "required": True}}) - - schema_registry.add("GlobalVariables", { - "StudyName": {"type": "dict", "schema": schema_registry.get("StudyName")}, - "StudyDescription": {"type": "dict", "schema": schema_registry.get("StudyDescription")}, - "ProtocolName": {"type": "dict", "schema": schema_registry.get("ProtocolName")} - }) - - schema_registry.add("Study", { - "OID": {"type": "string", "required": True}, - "GlobalVariables": {"type": "dict", "schema": schema_registry.get("GlobalVariables")}, - "MetaDataVersion": {"type": "dict", "schema": schema_registry.get("MetaDataVersion")} - }) + schema_registry.add( + "leaf", + { + "ID": {"type": "string", "required": True}, + "href": {"type": "string", "required": True}, + "title": {"type": "dict", "schema": schema_registry.get("title")}, + }, + ) + + schema_registry.add( + "WhereClauseRef", {"WhereClauseOID": {"type": "string", "required": True}} + ) + + schema_registry.add( + "ValueListRef", {"ValueListOID": {"type": "string", "required": True}} + ) + + schema_registry.add( + "PDFPageRef", + { + "Type": { + "type": "string", + "required": True, + "allowed": ["PhysicalRef", "NamedDestination"], + }, + "PageRefs": {"type": "string"}, + "FirstPage": {"type": "integer"}, + "LastPage": {"type": "integer"}, + "Title": {"type": "string"}, + }, + ) + + schema_registry.add( + "DocumentRef", + { + "leafID": {"type": "string", "required": True}, + "PDFPageRef": { + "type": "list", + "schema": { + "type": "dict", + "schema": schema_registry.get("PDFPageRef"), + }, + }, + }, + ) + + schema_registry.add( + "ItemRef", + { + "ItemOID": {"type": "string", "required": True}, + "OrderNumber": {"type": "integer", "required": False}, + "Mandatory": { + "type": "string", + "required": False, + "allowed": ["Yes", "No"], + }, + "KeySequence": {"type": "integer", "required": False}, + "MethodOID": {"type": "string", "required": False}, + "Role": {"type": "string", "required": False}, + "RoleCodeListOID": {"type": "string", "required": False}, + "IsNonStandard": {"type": "string", "allowed": ["Yes"]}, + "HasNoData": {"type": "string", "allowed": ["Yes"]}, + "WhereClauseRef": { + "type": "list", + "schema": { + "type": "dict", + "schema": schema_registry.get("WhereClauseRef"), + }, + }, + }, + ) + + schema_registry.add( + "SubClass", {"Name": {"type": "string"}, "ParentClass": {"type": "string"}} + ) + + schema_registry.add( + "Class", + { + "Name": {"type": "string"}, + "SubClass": { + "type": "list", + "schema": { + "type": "dict", + "schema": schema_registry.get("SubClass"), + }, + }, + }, + ) + + schema_registry.add( + "ItemGroupDef", + { + "OID": {"type": "string", "required": True}, + "Name": {"type": "string", "required": True}, + "Repeating": {"type": "string", "allowed": ["Yes", "No"]}, + "IsReferenceData": {"type": "string", "allowed": ["Yes", "No"]}, + "SASDatasetName": {"type": "string"}, + "Domain": {"type": "string"}, + "Origin": {"type": "string"}, + "Purpose": {"type": "string"}, + "Structure": {"type": "string", "required": True}, + "ArchiveLocationID": {"type": "string"}, + "CommentOID": {"type": "string"}, + "IsNonStandard": {"type": "string", "allowed": ["Yes"]}, + "StandardOID": {"type": "string"}, + "HasNoData": {"type": "string", "allowed": ["Yes"]}, + "Description": { + "type": "dict", + "schema": schema_registry.get("Description"), + }, + "ItemRef": { + "type": "list", + "schema": { + "type": "dict", + "schema": schema_registry.get("ItemRef"), + }, + }, + "Alias": { + "type": "list", + "schema": {"type": "dict", "schema": schema_registry.get("Alias")}, + }, + "Class": {"type": "dict", "schema": schema_registry.get("Class")}, + "leaf": {"type": "dict", "schema": schema_registry.get("leaf")}, + }, + ) + + schema_registry.add( + "FormalExpression", + { + "Context": {"type": "string", "required": True}, + "_content": {"type": "string", "required": True}, + }, + ) + + schema_registry.add( + "RangeCheck", + { + "Comparator": { + "type": "string", + "allowed": ["LT", "LE", "GT", "GE", "EQ", "NE", "IN", "NOTIN"], + }, + "SoftHard": {"type": "string", "allowed": ["Soft", "Hard"]}, + "ItemOID": {"type": "string", "required": True}, + "CheckValue": { + "type": "list", + "schema": { + "type": "dict", + "schema": {"_content": {"type": "string"}}, + }, + }, + }, + ) + + schema_registry.add( + "Origin", + { + "Type": { + "type": "string", + "required": True, + "allowed": [ + "Collected", + "Derived", + "Assigned", + "Protocol", + "Predecessor", + "Not Available", + ], + }, + "Source": { + "type": "string", + "allowed": ["Subject", "Investigator", "Vendor", "Sponsor"], + }, + "DocumentRef": { + "type": "list", + "schema": { + "type": "dict", + "schema": schema_registry.get("DocumentRef"), + }, + }, + "Description": { + "type": "dict", + "schema": schema_registry.get("Description"), + }, + }, + ) + + schema_registry.add("CodeListRef", {"CodeListOID": {"type": "string"}}) + + schema_registry.add( + "ItemDef", + { + "OID": {"type": "string", "required": True}, + "Name": {"type": "string", "required": True}, + "DataType": { + "type": "string", + "allowed": [ + "text", + "integer", + "float", + "date", + "time", + "datetime", + "string", + "boolean", + "double", + "hexBinary", + "base64Binary", + "hexFloat", + "base64Float", + "partialDate", + "partialTime", + "partialDatetime", + "durationDatetime", + "intervalDatetime", + "incompleteDatetime", + "incompleteDate", + "incompleteTime", + "URI", + ], + }, + "Length": {"type": "integer"}, + "SignificantDigits": {"type": "integer"}, + "SASFieldName": {"type": "string"}, + "DisplayFormat": {"type": "string"}, + "CommentOID": {"type": "string"}, + "Description": { + "type": "dict", + "schema": schema_registry.get("Description"), + }, + "CodeListRef": { + "type": "dict", + "schema": schema_registry.get("CodeListRef"), + }, + "Origin": { + "type": "list", + "schema": {"type": "dict", "schema": schema_registry.get("Origin")}, + }, + "ValueListRef": { + "type": "dict", + "schema": schema_registry.get("ValueListRef"), + }, + "Alias": { + "type": "list", + "schema": {"type": "dict", "schema": schema_registry.get("Alias")}, + }, + }, + ) + + schema_registry.add( + "CodeListItem", + { + "CodedValue": {"type": "string", "required": True}, + "Rank": {"type": "float"}, + "OrderNumber": {"type": "integer"}, + "ExtendedValue": {"type": "string", "allowed": ["Yes"]}, + "Description": { + "type": "dict", + "schema": schema_registry.get("Description"), + }, + "Decode": { + "type": "dict", + "schema": { + "TranslatedText": { + "type": "list", + "schema": { + "type": "dict", + "schema": schema_registry.get("TranslatedText"), + }, + } + }, + }, + "Alias": { + "type": "list", + "schema": {"type": "dict", "schema": schema_registry.get("Alias")}, + }, + }, + ) + + schema_registry.add( + "EnumeratedItem", + { + "CodedValue": {"type": "string", "required": True}, + "Rank": {"type": "float"}, + "OrderNumber": {"type": "integer"}, + "ExtendedValue": {"type": "string", "allowed": ["Yes"]}, + "Description": { + "type": "dict", + "schema": schema_registry.get("Description"), + }, + "Alias": { + "type": "list", + "schema": {"type": "dict", "schema": schema_registry.get("Alias")}, + }, + }, + ) + + schema_registry.add( + "ExternalCodeList", + { + "Dictionary": {"type": "string"}, + "Version": {"type": "string"}, + "ref": {"type": "string"}, + "href": {"type": "string"}, + }, + ) + + schema_registry.add( + "CodeList", + { + "OID": {"type": "string", "required": True}, + "Name": {"type": "string", "required": True}, + "DataType": { + "type": "string", + "allowed": ["text", "integer", "float", "string"], + }, + "IsNonStandard": {"type": "string", "allowed": ["Yes"]}, + "StandardOID": {"type": "string"}, + "CommentOID": {"type": "string"}, + "SASFormatName": {"type": "string"}, + "Description": { + "type": "dict", + "schema": schema_registry.get("Description"), + }, + "CodeListItem": { + "type": "list", + "schema": { + "type": "dict", + "schema": schema_registry.get("CodeListItem"), + }, + }, + "EnumeratedItem": { + "type": "list", + "schema": { + "type": "dict", + "schema": schema_registry.get("EnumeratedItem"), + }, + }, + "ExternalCodeList": { + "type": "dict", + "schema": schema_registry.get("ExternalCodeList"), + }, + "Alias": { + "type": "list", + "schema": {"type": "dict", "schema": schema_registry.get("Alias")}, + }, + }, + ) + + schema_registry.add( + "AnnotatedCRF", + { + "DocumentRef": { + "type": "dict", + "schema": schema_registry.get("DocumentRef"), + } + }, + ) + + schema_registry.add( + "SupplementalDoc", + { + "DocumentRef": { + "type": "list", + "schema": { + "type": "dict", + "schema": schema_registry.get("DocumentRef"), + }, + } + }, + ) + + schema_registry.add( + "WhereClauseDef", + { + "OID": {"type": "string", "required": True}, + "CommentOID": {"type": "string"}, + "RangeCheck": { + "type": "list", + "schema": { + "type": "dict", + "schema": schema_registry.get("RangeCheck"), + }, + }, + }, + ) + + schema_registry.add( + "ValueListDef", + { + "OID": {"type": "string", "required": True}, + "Description": { + "type": "dict", + "schema": schema_registry.get("Description"), + }, + "ItemRef": { + "type": "list", + "schema": { + "type": "dict", + "schema": schema_registry.get("ItemRef"), + }, + }, + }, + ) + + schema_registry.add( + "CommentDef", + { + "OID": {"type": "string", "required": True}, + "Description": { + "type": "dict", + "schema": schema_registry.get("Description"), + }, + "DocumentRef": { + "type": "list", + "schema": { + "type": "dict", + "schema": schema_registry.get("DocumentRef"), + }, + }, + }, + ) + + schema_registry.add( + "MethodDef", + { + "OID": {"type": "string", "required": True}, + "Name": {"type": "string", "required": True}, + "Type": { + "type": "string", + "required": True, + "allowed": ["Computation", "Imputation", "Transpose", "Other"], + }, + "Description": { + "type": "dict", + "schema": schema_registry.get("Description"), + }, + "FormalExpression": { + "type": "list", + "schema": { + "type": "dict", + "schema": schema_registry.get("FormalExpression"), + }, + }, + "Alias": { + "type": "list", + "schema": {"type": "dict", "schema": schema_registry.get("Alias")}, + }, + "DocumentRef": { + "type": "list", + "schema": { + "type": "dict", + "schema": schema_registry.get("DocumentRef"), + }, + }, + }, + ) + + schema_registry.add( + "Standard", + { + "OID": {"type": "string", "required": True}, + "Name": {"type": "string", "required": True}, + "Type": {"type": "string", "required": True}, + "PublishingSet": {"type": "string"}, + "Version": {"type": "string", "required": True}, + "Status": {"type": "string"}, + "CommentOID": {"type": "string"}, + }, + ) + + schema_registry.add( + "Standards", + { + "Standard": { + "type": "list", + "schema": { + "type": "dict", + "schema": schema_registry.get("Standard"), + }, + } + }, + ) + + schema_registry.add( + "MetaDataVersion", + { + "OID": {"type": "string", "required": True}, + "Name": {"type": "string", "required": True}, + "Description": {"type": "string"}, + "DefineVersion": {"type": "string", "required": True}, + "CommentOID": {"type": "string"}, + "Standards": { + "type": "dict", + "schema": schema_registry.get("Standards"), + }, + "AnnotatedCRF": { + "type": "dict", + "schema": schema_registry.get("AnnotatedCRF"), + }, + "SupplementalDoc": { + "type": "dict", + "schema": schema_registry.get("SupplementalDoc"), + }, + "ValueListDef": { + "type": "list", + "schema": { + "type": "dict", + "schema": schema_registry.get("ValueListDef"), + }, + }, + "WhereClauseDef": { + "type": "list", + "schema": { + "type": "dict", + "schema": schema_registry.get("WhereClauseDef"), + }, + }, + "ItemGroupDef": { + "type": "list", + "schema": { + "type": "dict", + "schema": schema_registry.get("ItemGroupDef"), + }, + }, + "ItemDef": { + "type": "list", + "schema": { + "type": "dict", + "schema": schema_registry.get("ItemDef"), + }, + }, + "CodeList": { + "type": "list", + "schema": { + "type": "dict", + "schema": schema_registry.get("CodeList"), + }, + }, + "MethodDef": { + "type": "list", + "schema": { + "type": "dict", + "schema": schema_registry.get("MethodDef"), + }, + }, + "CommentDef": { + "type": "list", + "schema": { + "type": "dict", + "schema": schema_registry.get("CommentDef"), + }, + }, + "leaf": { + "type": "list", + "schema": {"type": "dict", "schema": schema_registry.get("leaf")}, + }, + }, + ) + + schema_registry.add( + "StudyName", {"_content": {"type": "string", "required": True}} + ) + + schema_registry.add( + "StudyDescription", {"_content": {"type": "string", "required": True}} + ) + + schema_registry.add( + "ProtocolName", {"_content": {"type": "string", "required": True}} + ) + + schema_registry.add( + "GlobalVariables", + { + "StudyName": { + "type": "dict", + "schema": schema_registry.get("StudyName"), + }, + "StudyDescription": { + "type": "dict", + "schema": schema_registry.get("StudyDescription"), + }, + "ProtocolName": { + "type": "dict", + "schema": schema_registry.get("ProtocolName"), + }, + }, + ) + + schema_registry.add( + "Study", + { + "OID": {"type": "string", "required": True}, + "GlobalVariables": { + "type": "dict", + "schema": schema_registry.get("GlobalVariables"), + }, + "MetaDataVersion": { + "type": "dict", + "schema": schema_registry.get("MetaDataVersion"), + }, + }, + ) diff --git a/odmlib/define_2_1/rules/oid_ref.py b/odmlib/define_2_1/rules/oid_ref.py index 2f0808f..942617d 100644 --- a/odmlib/define_2_1/rules/oid_ref.py +++ b/odmlib/define_2_1/rules/oid_ref.py @@ -1,4 +1,3 @@ - class OIDRef: def __init__(self, skip_attrs=[], skip_elems=[]): self.oid = {} @@ -8,13 +7,23 @@ def __init__(self, skip_attrs=[], skip_elems=[]): self._init_ref_def() self.def_ref = {} self._init_def_ref() - self.skip_attr = ["FileOID", "PriorFileOID", "StudyOID", "MetaDataVersionOID", "ItemGroupOID"] + skip_attrs - self.skip_elem = ["ODM", "Study", "MetaDataVersion", "ItemGroupDef"] + skip_elems + self.skip_attr = [ + "FileOID", + "PriorFileOID", + "StudyOID", + "MetaDataVersionOID", + "ItemGroupOID", + ] + skip_attrs + self.skip_elem = [ + "ODM", + "Study", + "MetaDataVersion", + "ItemGroupDef", + ] + skip_elems self.is_verified = False - def add_oid(self, oid, element): - """ odmlib expects all OIDs to be unique within the scope of an ODM document """ + """odmlib expects all OIDs to be unique within the scope of an ODM document""" if oid in self.oid: raise ValueError(f"OID {oid} is not unique - element {element}") if element not in self.skip_elem: @@ -35,14 +44,18 @@ def check_oid_refs(self): for oid in oid_set: if attr not in self.skip_attr: if oid not in self.oid: - raise ValueError(f"OID {oid} referenced in the attribute {attr} is not found.") + raise ValueError( + f"OID {oid} referenced in the attribute {attr} is not found." + ) elif self.ref_def.get(attr) != self.oid.get(oid): - raise ValueError(f"OID reference for attribute {attr} element types do not match: " - f"{self.ref_def.get(attr)} and {self.oid.get(oid)}") + raise ValueError( + f"OID reference for attribute {attr} element types do not match: " + f"{self.ref_def.get(attr)} and {self.oid.get(oid)}" + ) return True def check_unreferenced_oids(self): - """ identify ELEMENTS that are defined but not used """ + """identify ELEMENTS that are defined but not used""" orphans = {} for oid, elem in self.oid.items(): for ref in self.def_ref[elem]: @@ -82,7 +95,6 @@ def _init_ref_def(self): self.ref_def["ArchiveLocationID"] = "leaf" self.ref_def["leafID"] = "leaf" - def _init_def_ref(self): # self.def_ref["MetaDataVersion"] = ["MetaDataVersionOID"] # self.def_ref["Study"] = ["StudyOID"] diff --git a/odmlib/define_loader.py b/odmlib/define_loader.py index 6ea5b65..055bdce 100644 --- a/odmlib/define_loader.py +++ b/odmlib/define_loader.py @@ -6,7 +6,12 @@ class XMLDefineLoader(DL.DocumentLoader): - def __init__(self, model_package="define_2_0", ns_uri="http://www.cdisc.org/ns/def/v2.0", local_model=False): + def __init__( + self, + model_package="define_2_0", + ns_uri="http://www.cdisc.org/ns/def/v2.0", + local_model=False, + ): self.filename = None self.parser = None if local_model: @@ -17,7 +22,7 @@ def __init__(self, model_package="define_2_0", ns_uri="http://www.cdisc.org/ns/d self.nsr = NS.NamespaceRegistry() def load_document(self, elem, *args): - elem_name = elem.tag[elem.tag.find('}') + 1:] + elem_name = elem.tag[elem.tag.find("}") + 1:] if elem.text and not elem.text.isspace(): attrib = {**elem.attrib, **{"_content": elem.text}} odm_obj = eval("self.DEF." + elem_name + "(**" + str(attrib) + ")") @@ -55,7 +60,9 @@ def _set_registry(self, namespace_registry): if namespace_registry: self.nsr = namespace_registry else: - NS.NamespaceRegistry(prefix="odm", uri="http://www.cdisc.org/ns/odm/v1.3", is_default=True) + NS.NamespaceRegistry( + prefix="odm", uri="http://www.cdisc.org/ns/odm/v1.3", is_default=True + ) self.nsr = NS.NamespaceRegistry(prefix="def", uri=self.ns_uri) def load_odm(self): @@ -81,7 +88,11 @@ def __init__(self, model_package="define_2_0"): self.DEF = importlib.import_module(f"odmlib.{model_package}.model") def load_document(self, odm_dict, key): - attrib = {key: value for key, value in odm_dict.items() if not isinstance(value, (list, dict))} + attrib = { + key: value + for key, value in odm_dict.items() + if not isinstance(value, (list, dict)) + } odm_obj = eval("self.DEF." + key + "(**" + str(attrib) + ")") odm_obj_items = eval("self.DEF." + key + ".__dict__.items()") for k, v in odm_obj_items: @@ -108,13 +119,17 @@ def create_document_from_string(self, odm_string): def load_odm(self): if not self.odm_dict: - raise ValueError("create_document must be used to create the document before executing load_odm") + raise ValueError( + "create_document must be used to create the document before executing load_odm" + ) odm_odmlib = self.load_document(self.odm_dict, "ODM") return odm_odmlib def load_metadataversion(self, idx=0): if not self.odm_dict: - raise ValueError("create_document must be used to create the document before executing load_metadataversion") + raise ValueError( + "create_document must be used to create the document before executing load_metadataversion" + ) if "MetaDataVersion" in self.odm_dict: mdv_dict = self.odm_dict["MetaDataVersion"] elif "Study" in self.odm_dict and "MetaDataVersion" in self.odm_dict["Study"]: @@ -126,7 +141,9 @@ def load_metadataversion(self, idx=0): def load_study(self, idx=0): if not self.odm_dict: - raise ValueError("create_document must be used to create the document before executing load_study") + raise ValueError( + "create_document must be used to create the document before executing load_study" + ) elif "Study" in self.odm_dict: study_dict = self.odm_dict["Study"] else: diff --git a/odmlib/descriptor.py b/odmlib/descriptor.py index d0895a0..356df71 100644 --- a/odmlib/descriptor.py +++ b/odmlib/descriptor.py @@ -1,7 +1,12 @@ - - class Descriptor: - def __init__(self, name=None, required=False, element_class=None, valid_values=[], namespace="odm"): + def __init__( + self, + name=None, + required=False, + element_class=None, + valid_values=[], + namespace="odm", + ): self.name = name self.required = required self.element_class = element_class @@ -11,8 +16,12 @@ def __init__(self, name=None, required=False, element_class=None, valid_values=[ def __get__(self, instance, cls): if instance is None: return self - elif (self.name not in instance.__dict__) and (self.name != self.__dict__["name"]): - raise ValueError(f"Missing attribute or element {self.name} in {cls.__name__}") + elif (self.name not in instance.__dict__) and ( + self.name != self.__dict__["name"] + ): + raise ValueError( + f"Missing attribute or element {self.name} in {cls.__name__}" + ) else: if self.name not in instance.__dict__: if isinstance(self, list): diff --git a/odmlib/document_loader.py b/odmlib/document_loader.py index abb8e07..675d923 100644 --- a/odmlib/document_loader.py +++ b/odmlib/document_loader.py @@ -4,24 +4,36 @@ class DocumentLoader(ABC): @abstractmethod def load_document(self, doc): - raise NotImplementedError("Attempted to execute an abstract method load_document in the DocumentLoader class") + raise NotImplementedError( + "Attempted to execute an abstract method load_document in the DocumentLoader class" + ) @abstractmethod def create_document(self, filename): - raise NotImplementedError("Attempted to execute an abstract method create_document in the DocumentLoader class") + raise NotImplementedError( + "Attempted to execute an abstract method create_document in the DocumentLoader class" + ) @abstractmethod def create_document_from_string(self, odm_string): - raise NotImplementedError("Attempted to execute an abstract method create_document_from_string in the DocumentLoader class") + raise NotImplementedError( + "Attempted to execute an abstract method create_document_from_string in the DocumentLoader class" + ) @abstractmethod def load_metadataversion(self, idx): - raise NotImplementedError("Attempted to execute an abstract method load_metadataversion in the DocumentLoader class") + raise NotImplementedError( + "Attempted to execute an abstract method load_metadataversion in the DocumentLoader class" + ) @abstractmethod def load_study(self, idx): - raise NotImplementedError("Attempted to execute an abstract method load_study in the DocumentLoader class") + raise NotImplementedError( + "Attempted to execute an abstract method load_study in the DocumentLoader class" + ) @abstractmethod def load_odm(self): - raise NotImplementedError("Attempted to execute an abstract method load_odm in the DocumentLoader class") + raise NotImplementedError( + "Attempted to execute an abstract method load_odm in the DocumentLoader class" + ) diff --git a/odmlib/loader.py b/odmlib/loader.py index 560be10..68e54a8 100644 --- a/odmlib/loader.py +++ b/odmlib/loader.py @@ -2,7 +2,8 @@ class ODMLoader: - """ loads an ODM-XML document into the object model """ + """loads an ODM-XML document into the object model""" + def __init__(self, odm_loader): if not isinstance(odm_loader, DL.DocumentLoader): raise TypeError("odm_loader argument must implement DocumentLoader") @@ -38,6 +39,10 @@ def __getattr__(self, attr): if callable(load_func): return load_func else: - raise AttributeError(f"{attr} is not callable in the loader {self.loader.__class__.__name__}") + raise AttributeError( + f"{attr} is not callable in the loader {self.loader.__class__.__name__}" + ) else: - raise AttributeError(f"The {attr} method does not exist in the loader {self.loader.__class__.__name__}") + raise AttributeError( + f"The {attr} method does not exist in the loader {self.loader.__class__.__name__}" + ) diff --git a/odmlib/ns_registry.py b/odmlib/ns_registry.py index 71fa953..5b76f43 100644 --- a/odmlib/ns_registry.py +++ b/odmlib/ns_registry.py @@ -2,7 +2,8 @@ class Borg: - """ borg provides a set of global attributes for the Namespace Registry singleton """ + """borg provides a set of global attributes for the Namespace Registry singleton""" + namespaces = {} default_namespace = {} @@ -42,7 +43,9 @@ def get_ns_attribute_name(self, name, prefix): else: return "{" + self.namespaces[prefix] + "}" + name else: - raise ValueError(f"Error: Namespace with prefix {prefix} has not been registered") + raise ValueError( + f"Error: Namespace with prefix {prefix} has not been registered" + ) def get_prefix_ns_from_uri(self, uri): for prefix, ns_uri in self.namespaces.items(): @@ -51,18 +54,18 @@ def get_prefix_ns_from_uri(self, uri): raise ValueError(f"Error: Namespace with URI {uri} has not been registered") def set_odm_namespace_attributes(self, odm_elem): - """ add NS attributes to the ODM XML element object """ + """add NS attributes to the ODM XML element object""" odm_elem.attrib["xmlns"] = list(self.default_namespace.values())[0] for prefix, uri in self.namespaces.items(): if prefix != list(self.default_namespace.keys())[0]: odm_elem.attrib["xmlns:" + prefix] = uri def set_odm_namespace_attributes_string(self, odm_str): - """ add NS attributes to the ODM XML element object """ - ns_str = " Element: def parse_tree(self) -> ElementTree: self.register_namespaces() - #return ET.ElementTree(ET.fromstring(self.odm_string)) + # return ET.ElementTree(ET.fromstring(self.odm_string)) return ET.fromstring(self.odm_string) diff --git a/odmlib/oid_index.py b/odmlib/oid_index.py index 2b0687f..b1a6086 100644 --- a/odmlib/oid_index.py +++ b/odmlib/oid_index.py @@ -1,17 +1,18 @@ - class OIDIndex: def __init__(self): self.oid_index = {} def add_oid(self, oid, element): - """ odmlib expects all OIDs to be unique within the scope of an ODM document """ + """odmlib expects all OIDs to be unique within the scope of an ODM document""" if oid not in self.oid_index: self.oid_index[oid] = [] self.oid_index[oid].append(element) def find_all(self, oid): if not self.oid_index: - raise ValueError(f"The OID index is empty. Build the index prior to using find_all. OID {oid} not found.") + raise ValueError( + f"The OID index is empty. Build the index prior to using find_all. OID {oid} not found." + ) elif oid not in self.oid_index: raise ValueError(f"OID {oid} not found in the OID index.") return self.oid_index[oid] diff --git a/odmlib/typed.py b/odmlib/typed.py index 6b662b4..552acd9 100644 --- a/odmlib/typed.py +++ b/odmlib/typed.py @@ -11,7 +11,9 @@ class Typed(DESC.Descriptor): def __set__(self, instance, value): if (value is not None) and not isinstance(value, self.odm_type): - raise TypeError(f"Expected type {str(self.odm_type)} for {self.name} with value {str(value)}") + raise TypeError( + f"Expected type {str(self.odm_type)} for {self.name} with value {str(value)}" + ) # often does not call parent, calls next on __mro__ which maybe influenced by multiple inheritance super().__set__(instance, value) @@ -23,10 +25,14 @@ def __set__(self, instance, value): try: value = int(value) except ValueError: - raise TypeError(f"Expected string to convert to integer for {self.name} with a value {str(value)}") + raise TypeError( + f"Expected string to convert to integer for {self.name} with a value {str(value)}" + ) odm_type = int if (value is not None) and not isinstance(value, odm_type): - raise TypeError(f"Expected type {str(odm_type)} for {self.name} with value {str(value)}") + raise TypeError( + f"Expected type {str(odm_type)} for {self.name} with value {str(value)}" + ) super().__set__(instance, value) @@ -37,12 +43,16 @@ def __set__(self, instance, value): try: value = float(value) except ValueError: - raise TypeError(f"Expected string to convert to float for {self.name} with a value {str(value)}") + raise TypeError( + f"Expected string to convert to float for {self.name} with a value {str(value)}" + ) # convert integer into a float (e.g. 1 to 1.0) elif isinstance(value, int): value = float(value) if (value is not None) and not isinstance(value, float): - raise TypeError(f"Expected type float for {self.name} with value {str(value)}") + raise TypeError( + f"Expected type float for {self.name} with value {str(value)}" + ) super().__set__(instance, value) @@ -88,7 +98,9 @@ def __set__(self, instance, value): class NonNegative(DESC.Descriptor): def __set__(self, instance, value): if (value is not None) and value < 0: - raise TypeError(f"Expected value >= 0 for type Non-negative for {self.name}") + raise TypeError( + f"Expected value >= 0 for type Non-negative for {self.name}" + ) super().__set__(instance, value) @@ -116,8 +128,10 @@ def __init__(self, *args, max_length, **kwargs): def __set__(self, instance, value): if (value is not None) and (len(value) > self.max_length): - raise ValueError(f"{self.name} has a length of {len(value)} and exceeds the maximum length of " - f"{self.max_length}") + raise ValueError( + f"{self.name} has a length of {len(value)} and exceeds the maximum length of " + f"{self.max_length}" + ) super().__set__(instance, value) @@ -126,7 +140,8 @@ class SizedString(String, Sized): class Regex(DESC.Descriptor): - """ pattern matching """ + """pattern matching""" + def __init__(self, *args, pat, **kwargs): # takes a pattern arg named pat and passes the remaining arguments along self.pat = re.compile(pat) @@ -144,81 +159,101 @@ class SizedRegexString(SizedString, Regex): class DateTimeString(DESC.Descriptor): def __set__(self, instance, value): - iso_pat = r'^(-?(?:[1-9][0-9]*)?[0-9]{4})-(1[0-2]|0[1-9])-(3[01]|0[1-9]|[12][0-9])T(2[0-3]|[01][0-9]):([0-5][0-9]):([0-5][0-9])(\.[0-9]+)?(Z|[+-](?:2[0-3]|[01][0-9]):[0-5][0-9])?$' + iso_pat = r"^(-?(?:[1-9][0-9]*)?[0-9]{4})-(1[0-2]|0[1-9])-(3[01]|0[1-9]|[12][0-9])T(2[0-3]|[01][0-9]):([0-5][0-9]):([0-5][0-9])(\.[0-9]+)?(Z|[+-](?:2[0-3]|[01][0-9]):[0-5][0-9])?$" # noqa: 501 pat = re.compile(iso_pat) if (value is not None) and (not pat.match(value)): - raise ValueError(f"Expected type datetime for {self.name}, found value {value}") + raise ValueError( + f"Expected type datetime for {self.name}, found value {value}" + ) super().__set__(instance, value) class PartialDateTimeString(DESC.Descriptor): def __set__(self, instance, value): - part_pat = r'^((([0-9][0-9][0-9][0-9])((-(([0][1-9])|([1][0-2])))((-(([0][1-9])|([1-2][0-9])|([3][0-1])))(T((([0-1][0-9])|([2][0-3]))((:([0-5][0-9]))(((:([0-5][0-9]))((\.[0-9]+)?))?)?)?((((\+|-)(([0-1][0-9])|([2][0-3])):[0-5][0-9])|(Z)))?))?)?)?))$' + part_pat = r"^((([0-9][0-9][0-9][0-9])((-(([0][1-9])|([1][0-2])))((-(([0][1-9])|([1-2][0-9])|([3][0-1])))(T((([0-1][0-9])|([2][0-3]))((:([0-5][0-9]))(((:([0-5][0-9]))((\.[0-9]+)?))?)?)?((((\+|-)(([0-1][0-9])|([2][0-3])):[0-5][0-9])|(Z)))?))?)?)?))$" # noqa: 501 compiled_part_pat = re.compile(part_pat) if (value is not None) and (not compiled_part_pat.match(value)): - raise ValueError(f"Expected type PartialDateTime for {self.name}, found value {value}") + raise ValueError( + f"Expected type PartialDateTime for {self.name}, found value {value}" + ) super().__set__(instance, value) class PartialDateString(DESC.Descriptor): def __set__(self, instance, value): - if value and value.count('-') == 2: + if value and value.count("-") == 2: try: - datetime.datetime.strptime(value, '%Y-%m-%d') + datetime.datetime.strptime(value, "%Y-%m-%d") except ValueError: - raise ValueError(f"Expected type PartialDate for {self.name}, found value {value}") + raise ValueError( + f"Expected type PartialDate for {self.name}, found value {value}" + ) else: - part_pat = r'^(([0-9][0-9][0-9][0-9])(-(([0][1-9])|([1][0-2])))?)$' + part_pat = r"^(([0-9][0-9][0-9][0-9])(-(([0][1-9])|([1][0-2])))?)$" compiled_part_pat = re.compile(part_pat) if (value is not None) and (not compiled_part_pat.match(value)): - raise ValueError(f"Expected type PartialDate for {self.name}, found value {value}") + raise ValueError( + f"Expected type PartialDate for {self.name}, found value {value}" + ) super().__set__(instance, value) class PartialTimeString(DESC.Descriptor): def __set__(self, instance, value): - time_pat = r'^(2[0-3]|[01][0-9]):([0-5][0-9]):([0-5][0-9])(\.[0-9]+)?(Z|[+-](?:2[0-3]|[01][0-9]):[0-5][0-9])?$' + time_pat = r"^(2[0-3]|[01][0-9]):([0-5][0-9]):([0-5][0-9])(\.[0-9]+)?(Z|[+-](?:2[0-3]|[01][0-9]):[0-5][0-9])?$" compiled_time_pat = re.compile(time_pat) - part_pat = r'^((([0-1][0-9])|([2][0-3]))(:[0-5][0-9])?(((\+|-)(([0-1][0-9])|([2][0-3])):[0-5][0-9])|(Z))?)$' + part_pat = r"^((([0-1][0-9])|([2][0-3]))(:[0-5][0-9])?(((\+|-)(([0-1][0-9])|([2][0-3])):[0-5][0-9])|(Z))?)$" compiled_part_pat = re.compile(part_pat) - if (value is not None) and (not (compiled_time_pat.match(value) or compiled_part_pat.match(value))): - raise ValueError(f"Expected type IncompleteTime for {self.name}, found value {value}") + if (value is not None) and ( + not (compiled_time_pat.match(value) or compiled_part_pat.match(value)) + ): + raise ValueError( + f"Expected type IncompleteTime for {self.name}, found value {value}" + ) super().__set__(instance, value) class IncompleteDateTimeString(DESC.Descriptor): def __set__(self, instance, value): - inc_pat = r'^(((([0-9][0-9][0-9][0-9]))|-)-(((([0][1-9])|([1][0-2])))|-)-(((([0][1-9])|([1-2][0-9])|([3][0-1])))|-)T(((([0-1][0-9])|([2][0-3])))|-):((([0-5][0-9]))|-):((([0-5][0-9](\.[0-9]+)?))|-)((((\+|-)(([0-1][0-9])|([2][0-3])):[0-5][0-9])|Z|-))?)$' + inc_pat = r"^(((([0-9][0-9][0-9][0-9]))|-)-(((([0][1-9])|([1][0-2])))|-)-(((([0][1-9])|([1-2][0-9])|([3][0-1])))|-)T(((([0-1][0-9])|([2][0-3])))|-):((([0-5][0-9]))|-):((([0-5][0-9](\.[0-9]+)?))|-)((((\+|-)(([0-1][0-9])|([2][0-3])):[0-5][0-9])|Z|-))?)$" # noqa: 501 compiled_inc_pat = re.compile(inc_pat) if (value is not None) and (not compiled_inc_pat.match(value)): - raise ValueError(f"Expected type IncompleteDateTime for {self.name}, found value {value}") + raise ValueError( + f"Expected type IncompleteDateTime for {self.name}, found value {value}" + ) super().__set__(instance, value) class IncompleteDateString(DESC.Descriptor): def __set__(self, instance, value): - inc_pat = r'^(((([0-9][0-9][0-9][0-9]))|-)-(((([0][1-9])|([1][0-2])))|-)-(((([0][1-9])|([1-2][0-9])|([3][0-1])))|-))$' + inc_pat = r"^(((([0-9][0-9][0-9][0-9]))|-)-(((([0][1-9])|([1][0-2])))|-)-(((([0][1-9])|([1-2][0-9])|([3][0-1])))|-))$" # noqa: 501 compiled_inc_pat = re.compile(inc_pat) if (value is not None) and (not compiled_inc_pat.match(value)): - raise ValueError(f"Expected type IncompleteDate for {self.name}, found value {value}") + raise ValueError( + f"Expected type IncompleteDate for {self.name}, found value {value}" + ) super().__set__(instance, value) class IncompleteTimeString(DESC.Descriptor): def __set__(self, instance, value): - inc_pat = r'^((((([0-1][0-9])|([2][0-3])))|-):((([0-5][0-9]))|-):((([0-5][0-9](\.[0-9]+)?))|-)((((\+|-)(([0-1][0-9])|([2][0-3])):[0-5][0-9])|Z|-))?)$' + inc_pat = r"^((((([0-1][0-9])|([2][0-3])))|-):((([0-5][0-9]))|-):((([0-5][0-9](\.[0-9]+)?))|-)((((\+|-)(([0-1][0-9])|([2][0-3])):[0-5][0-9])|Z|-))?)$" # noqa: 501 compiled_inc_pat = re.compile(inc_pat) if (value is not None) and (not compiled_inc_pat.match(value)): - raise ValueError(f"Expected type IncompleteTime for {self.name}, found value {value}") + raise ValueError( + f"Expected type IncompleteTime for {self.name}, found value {value}" + ) super().__set__(instance, value) class DurationDateTimeString(DESC.Descriptor): def __set__(self, instance, value): - dur_pat = r'^((\+ | -)?P([0-9]([0-9]+)?)W)$' + dur_pat = r"^((\+ | -)?P([0-9]([0-9]+)?)W)$" pat = re.compile(dur_pat) if (value is not None) and (not pat.match(value)): - raise ValueError(f"Expected type DurationDateTime for {self.name}, found value {value}") + raise ValueError( + f"Expected type DurationDateTime for {self.name}, found value {value}" + ) super().__set__(instance, value) @@ -226,9 +261,11 @@ class DateString(DESC.Descriptor): def __set__(self, instance, value): try: if value is not None: - datetime.datetime.strptime(value, '%Y-%m-%d') + datetime.datetime.strptime(value, "%Y-%m-%d") except ValueError: - raise ValueError(f"Expected type date (YYYY-MM-DD) for {self.name}, found value {value}") + raise ValueError( + f"Expected type date (YYYY-MM-DD) for {self.name}, found value {value}" + ) super().__set__(instance, value) @@ -272,17 +309,23 @@ def __set__(self, instance, value): class ValidValues(DESC.Descriptor): def __set__(self, instance, value): - if (value is not None) and value not in VS.ValueSet.value_set(type(instance).__name__ + "." + self.name): - raise TypeError(f"Invalid value {value} for {self.name}. Value must be one of " - f"{', '.join(VS.ValueSet.value_set(self.name))}") + if (value is not None) and value not in VS.ValueSet.value_set( + type(instance).__name__ + "." + self.name + ): + raise TypeError( + f"Invalid value {value} for {self.name}. Value must be one of " + f"{', '.join(VS.ValueSet.value_set(self.name))}" + ) super().__set__(instance, value) class ExtendedValidValues(DESC.Descriptor): def __set__(self, instance, value): if (value is not None) and value not in self.valid_values: - raise TypeError(f"Invalid value {value} for {self.name}. Value must be one of " - f"{', '.join(self.valid_values)}") + raise TypeError( + f"Invalid value {value} for {self.name}. Value must be one of " + f"{', '.join(self.valid_values)}" + ) super().__set__(instance, value) @@ -291,7 +334,7 @@ class ValueSetString(ValidValues, String): class ODMObject(DESC.Descriptor): - def __init__(self, *args, element_class, **kwargs): + def __init__(self, *args, element_class, **kwargs): self.obj_type = element_class kwargs["element_class"] = element_class self.namespace = kwargs.get("namespace", "") @@ -308,7 +351,9 @@ def __set__(self, instance, value): if isinstance(value, list): for obj in value: if not isinstance(obj, self.obj_type): - raise TypeError(f"Every {self.name} object in the list must be of type {self.obj_type}") + raise TypeError( + f"Every {self.name} object in the list must be of type {self.obj_type}" + ) else: raise TypeError(f"The {self.name} object must be a list") super().__set__(instance, value) diff --git a/odmlib/valueset.py b/odmlib/valueset.py index 1ed2c3c..43d57ef 100644 --- a/odmlib/valueset.py +++ b/odmlib/valueset.py @@ -2,8 +2,10 @@ class ValueSet: _value_set = { "MetaDataVersion.DefineVersion": ["2.0.0", "2.0", "2.1.0", "2.1"], - "ItemGroupDef.Class": ["SPECIAL PURPOSE", "FINDINGS", "EVENTS", "INTERVENTIONS", "TRIAL DESIGN", "RELATIONSHIP", - "SUBJECT LEVEL ANALYSIS DATASET", "BASIC DATA STRUCTURE", "ADAM OTHER"], + "ItemGroupDef.Class": ["SPECIAL PURPOSE", "FINDINGS", "EVENTS", + "INTERVENTIONS", "TRIAL DESIGN", "RELATIONSHIP", + "SUBJECT LEVEL ANALYSIS DATASET", + "BASIC DATA STRUCTURE", "ADAM OTHER"], "PDFPageRef.Type": ["NamedDestination", "PhysicalRef"], "Origin.Type": ["CRF", "Derived", "Assigned", "Protocol", "eDT", "Predecessor"], "RangeCheck.Comparator": ["LT", "LE", "GT", "GE", "EQ", "NE", "IN", "NOTIN"], @@ -75,4 +77,5 @@ def value_set(cls, attribute): if attribute in cls._value_set: return cls._value_set[attribute] else: - raise ValueError(f"Unknown value {attribute} in ValueSet. Unable to check value.") + raise ValueError( + f"Unknown value {attribute} in ValueSet. Unable to check value.") diff --git a/requirements-dev.txt b/requirements-dev.txt index c022575..e3a3b32 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -3,3 +3,4 @@ sphinx flake8 tox pytest +coverage diff --git a/setup.py b/setup.py index edb7017..fa16418 100644 --- a/setup.py +++ b/setup.py @@ -7,8 +7,10 @@ setup( name='odmlib', version='0.1.4', - packages=['odmlib', 'tests', 'odmlib.odm_1_3_2', 'odmlib.odm_1_3_2.rules', 'odmlib.define_2_0', - 'odmlib.define_2_0.rules', 'odmlib.define_2_1', 'odmlib.define_2_1.rules', 'odmlib.ct_1_1_1', + packages=['odmlib', 'tests', 'odmlib.odm_1_3_2', + 'odmlib.odm_1_3_2.rules', 'odmlib.define_2_0', + 'odmlib.define_2_0.rules', 'odmlib.define_2_1', + 'odmlib.define_2_1.rules', 'odmlib.ct_1_1_1', 'odmlib.dataset_1_0_1', ], url='https://github.com/swhume/odmlib', license='MIT', diff --git a/tests/data/ae_test.json b/tests/data/ae_test.json index aa2ddb7..73aae30 100644 --- a/tests/data/ae_test.json +++ b/tests/data/ae_test.json @@ -1 +1 @@ -{"FileOID": "ODM.DATASET.001", "AsOfDateTime": "2022-04-05T11:51:36.757671+00:00", "DatasetXMLVersion": "1.0.0", "CreationDateTime": "2022-04-05T11:51:36.757671+00:00", "ODMVersion": "1.3.2", "FileType": "Snapshot", "Originator": "swhume", "SourceSystem": "odmlib", "SourceSystemVersion": "0.1", "ClinicalData": {"StudyOID": "cdisc.odmlib.001", "MetaDataVersionOID": "MDV.001", "ItemGroupData": [{"ItemGroupOID": "IG.AE", "ItemGroupDataSeq": 1, "ItemData": [{"ItemOID": "IT.STUDYID", "Value": "CDISC01"}, {"ItemOID": "IT.AE.DOMAIN", "Value": "AE"}, {"ItemOID": "IT.USUBJID", "Value": "CDISC01.100008"}, {"ItemOID": "IT.AE.AESEQ", "Value": "1"}, {"ItemOID": "IT.AE.AETERM", "Value": "AGITATED"}, {"ItemOID": "IT.AE.AEMODIFY", "Value": "AGITATION"}, {"ItemOID": "IT.AE.AEDECOD", "Value": "Agitation"}, {"ItemOID": "IT.AE.AESEV", "Value": "MILD"}, {"ItemOID": "IT.AE.AESER", "Value": "N"}, {"ItemOID": "IT.AE.AEACN", "Value": "DOSE NOT CHANGED"}, {"ItemOID": "IT.AE.AEREL", "Value": "POSSIBLY RELATED"}]}, {"ItemGroupOID": "IG.AE", "ItemGroupDataSeq": 2, "ItemData": [{"ItemOID": "IT.STUDYID", "Value": "CDISC01"}, {"ItemOID": "IT.AE.DOMAIN", "Value": "AE"}, {"ItemOID": "IT.USUBJID", "Value": "CDISC01.100008"}, {"ItemOID": "IT.AE.AESEQ", "Value": "2"}, {"ItemOID": "IT.AE.AETERM", "Value": "ANXIETY"}, {"ItemOID": "IT.AE.AEMODIFY", "Value": "AGITATION"}, {"ItemOID": "IT.AE.AEDECOD", "Value": "Anxiety"}, {"ItemOID": "IT.AE.AESEV", "Value": "MODERATE"}, {"ItemOID": "IT.AE.AESER", "Value": "N"}, {"ItemOID": "IT.AE.AEACN", "Value": "DOSE NOT CHANGED"}, {"ItemOID": "IT.AE.AEREL", "Value": "POSSIBLY RELATED"}]}]}} \ No newline at end of file +{"FileOID": "ODM.DATASET.001", "AsOfDateTime": "2022-04-06T10:11:06.012099+00:00", "DatasetXMLVersion": "1.0.0", "CreationDateTime": "2022-04-06T10:11:06.012099+00:00", "ODMVersion": "1.3.2", "FileType": "Snapshot", "Originator": "swhume", "SourceSystem": "odmlib", "SourceSystemVersion": "0.1", "ClinicalData": {"StudyOID": "cdisc.odmlib.001", "MetaDataVersionOID": "MDV.001", "ItemGroupData": [{"ItemGroupOID": "IG.AE", "ItemGroupDataSeq": 1, "ItemData": [{"ItemOID": "IT.STUDYID", "Value": "CDISC01"}, {"ItemOID": "IT.AE.DOMAIN", "Value": "AE"}, {"ItemOID": "IT.USUBJID", "Value": "CDISC01.100008"}, {"ItemOID": "IT.AE.AESEQ", "Value": "1"}, {"ItemOID": "IT.AE.AETERM", "Value": "AGITATED"}, {"ItemOID": "IT.AE.AEMODIFY", "Value": "AGITATION"}, {"ItemOID": "IT.AE.AEDECOD", "Value": "Agitation"}, {"ItemOID": "IT.AE.AESEV", "Value": "MILD"}, {"ItemOID": "IT.AE.AESER", "Value": "N"}, {"ItemOID": "IT.AE.AEACN", "Value": "DOSE NOT CHANGED"}, {"ItemOID": "IT.AE.AEREL", "Value": "POSSIBLY RELATED"}]}, {"ItemGroupOID": "IG.AE", "ItemGroupDataSeq": 2, "ItemData": [{"ItemOID": "IT.STUDYID", "Value": "CDISC01"}, {"ItemOID": "IT.AE.DOMAIN", "Value": "AE"}, {"ItemOID": "IT.USUBJID", "Value": "CDISC01.100008"}, {"ItemOID": "IT.AE.AESEQ", "Value": "2"}, {"ItemOID": "IT.AE.AETERM", "Value": "ANXIETY"}, {"ItemOID": "IT.AE.AEMODIFY", "Value": "AGITATION"}, {"ItemOID": "IT.AE.AEDECOD", "Value": "Anxiety"}, {"ItemOID": "IT.AE.AESEV", "Value": "MODERATE"}, {"ItemOID": "IT.AE.AESER", "Value": "N"}, {"ItemOID": "IT.AE.AEACN", "Value": "DOSE NOT CHANGED"}, {"ItemOID": "IT.AE.AEREL", "Value": "POSSIBLY RELATED"}]}]}} \ No newline at end of file diff --git a/tests/data/ae_test.xml b/tests/data/ae_test.xml index 14a4583..400ed14 100644 --- a/tests/data/ae_test.xml +++ b/tests/data/ae_test.xml @@ -1,2 +1,2 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/tests/data/defineV21-SDTM-test.json b/tests/data/defineV21-SDTM-test.json index 8722bc8..7c4375a 100644 --- a/tests/data/defineV21-SDTM-test.json +++ b/tests/data/defineV21-SDTM-test.json @@ -1 +1 @@ -{"FileOID": "DEFINE.TEST.IGD.001", "FileType": "Snapshot", "AsOfDateTime": "2022-04-05T11:51:37.167651+00:00", "CreationDateTime": "2022-04-05T11:51:37.167661+00:00", "ODMVersion": "1.3.2", "Originator": "CDISC 360", "SourceSystem": "WS2", "SourceSystemVersion": "0.1", "Context": "Other", "Study": {"OID": "ST.TEST.IGD.001", "GlobalVariables": {"StudyName": {"_content": "TEST ODM ItemGroupDef"}, "StudyDescription": {"_content": "ItemGroupDef 001"}, "ProtocolName": {"_content": "ODM ItemGroupDef"}}, "MetaDataVersion": {"OID": "MDV.TEST.IGD.001", "Name": "ItemGroupDefTest001", "Description": "ItemGroupDef Test 001", "DefineVersion": "2.1.0", "ItemGroupDef": [{"OID": "IG.VS", "Name": "VS", "Repeating": "Yes", "Domain": "VS", "SASDatasetName": "VS", "IsReferenceData": "No", "Purpose": "Tabulation", "ArchiveLocationID": "LF.VS", "Structure": "One record per vital sign measurement per visit per subject", "StandardOID": "STD.1", "IsNonStandard": "Yes", "HasNoData": "Yes", "Description": {"TranslatedText": [{"_content": "this is the first test description", "lang": "en"}]}, "ItemRef": [{"ItemOID": "IT.STUDYID", "Mandatory": "Yes", "OrderNumber": 1, "KeySequence": 1}, {"ItemOID": "IT.TA.DOMAIN", "Mandatory": "Yes", "OrderNumber": 2}, {"ItemOID": "IT.TA.ARMCD", "Mandatory": "Yes", "OrderNumber": 3, "KeySequence": 2}, {"ItemOID": "IT.TA.ARM", "Mandatory": "Yes", "OrderNumber": 4}, {"ItemOID": "IT.TA.TAETORD", "Mandatory": "Yes", "OrderNumber": 5, "KeySequence": 3}, {"ItemOID": "IT.TA.ETCD", "Mandatory": "Yes", "OrderNumber": 6}, {"ItemOID": "IT.TA.ELEMENT", "Mandatory": "No", "OrderNumber": 7}, {"ItemOID": "IT.TA.TABRANCH", "Mandatory": "No", "OrderNumber": 8}, {"ItemOID": "IT.TA.TATRANS", "Mandatory": "No", "OrderNumber": 9}, {"ItemOID": "IT.TA.EPOCH", "Mandatory": "No", "OrderNumber": 10}]}]}}} \ No newline at end of file +{"FileOID": "DEFINE.TEST.IGD.001", "FileType": "Snapshot", "AsOfDateTime": "2022-04-06T10:11:06.421435+00:00", "CreationDateTime": "2022-04-06T10:11:06.421445+00:00", "ODMVersion": "1.3.2", "Originator": "CDISC 360", "SourceSystem": "WS2", "SourceSystemVersion": "0.1", "Context": "Other", "Study": {"OID": "ST.TEST.IGD.001", "GlobalVariables": {"StudyName": {"_content": "TEST ODM ItemGroupDef"}, "StudyDescription": {"_content": "ItemGroupDef 001"}, "ProtocolName": {"_content": "ODM ItemGroupDef"}}, "MetaDataVersion": {"OID": "MDV.TEST.IGD.001", "Name": "ItemGroupDefTest001", "Description": "ItemGroupDef Test 001", "DefineVersion": "2.1.0", "ItemGroupDef": [{"OID": "IG.VS", "Name": "VS", "Repeating": "Yes", "Domain": "VS", "SASDatasetName": "VS", "IsReferenceData": "No", "Purpose": "Tabulation", "ArchiveLocationID": "LF.VS", "Structure": "One record per vital sign measurement per visit per subject", "StandardOID": "STD.1", "IsNonStandard": "Yes", "HasNoData": "Yes", "Description": {"TranslatedText": [{"_content": "this is the first test description", "lang": "en"}]}, "ItemRef": [{"ItemOID": "IT.STUDYID", "Mandatory": "Yes", "OrderNumber": 1, "KeySequence": 1}, {"ItemOID": "IT.TA.DOMAIN", "Mandatory": "Yes", "OrderNumber": 2}, {"ItemOID": "IT.TA.ARMCD", "Mandatory": "Yes", "OrderNumber": 3, "KeySequence": 2}, {"ItemOID": "IT.TA.ARM", "Mandatory": "Yes", "OrderNumber": 4}, {"ItemOID": "IT.TA.TAETORD", "Mandatory": "Yes", "OrderNumber": 5, "KeySequence": 3}, {"ItemOID": "IT.TA.ETCD", "Mandatory": "Yes", "OrderNumber": 6}, {"ItemOID": "IT.TA.ELEMENT", "Mandatory": "No", "OrderNumber": 7}, {"ItemOID": "IT.TA.TABRANCH", "Mandatory": "No", "OrderNumber": 8}, {"ItemOID": "IT.TA.TATRANS", "Mandatory": "No", "OrderNumber": 9}, {"ItemOID": "IT.TA.EPOCH", "Mandatory": "No", "OrderNumber": 10}]}]}}} \ No newline at end of file diff --git a/tests/data/defineV21-SDTM-test.xml b/tests/data/defineV21-SDTM-test.xml index b1afdef..2d8684a 100644 --- a/tests/data/defineV21-SDTM-test.xml +++ b/tests/data/defineV21-SDTM-test.xml @@ -1,2 +1,2 @@ -TEST ODM ItemGroupDefItemGroupDef 001ODM ItemGroupDefthis is the first test description \ No newline at end of file +TEST ODM ItemGroupDefItemGroupDef 001ODM ItemGroupDefthis is the first test description \ No newline at end of file diff --git a/tests/data/test_clinical_data_01.xml b/tests/data/test_clinical_data_01.xml index 44c276a..2890f65 100644 --- a/tests/data/test_clinical_data_01.xml +++ b/tests/data/test_clinical_data_01.xml @@ -1,2 +1,2 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/tests/data/test_odm_002.xml b/tests/data/test_odm_002.xml deleted file mode 100644 index e40c3ee..0000000 --- a/tests/data/test_odm_002.xml +++ /dev/null @@ -1,2 +0,0 @@ - -ODM XML Test Study NameTesting the generation of an ODM XML fileODM XML Test StudyTrace-XML Test CDASH FileDate of measurementsDateResult of the vital signs measurement as originally received or collected.DiastolicNoYesSkip the BRTHMO field when BRTHYR length NE 4Concatenation of BRTHYR, BRTHMO, and BRTHDY in ISO 8601 format \ No newline at end of file diff --git a/tests/data/test_referece_data_01.xml b/tests/data/test_referece_data_01.xml index c01d929..ff7a831 100644 --- a/tests/data/test_referece_data_01.xml +++ b/tests/data/test_referece_data_01.xml @@ -1,2 +1,2 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/tests/test_attribute_order.py b/tests/test_attribute_order.py index e740a09..83699c9 100644 --- a/tests/test_attribute_order.py +++ b/tests/test_attribute_order.py @@ -17,22 +17,29 @@ def test_study_invalid_study_object_assignment(self): study = ODM.Study(OID="ST.001.Test") study.GlobalVariables = ODM.GlobalVariables() with self.assertRaises(TypeError): - study.GlobalVariables.StudyName = ODM.ProtocolName(_content="The ODM protocol name") + study.GlobalVariables.StudyName = ODM.ProtocolName( + _content="The ODM protocol name") def test_valid_order_study_objects(self): study = ODM.Study(OID="ST.001.Test") study.GlobalVariables = ODM.GlobalVariables() - study.GlobalVariables.StudyName = ODM.StudyName(_content="The ODM study name") - study.GlobalVariables.StudyDescription = ODM.StudyDescription(_content="The description of the ODM study") - study.GlobalVariables.ProtocolName = ODM.ProtocolName(_content="The ODM protocol name") + study.GlobalVariables.StudyName = ODM.StudyName( + _content="The ODM study name") + study.GlobalVariables.StudyDescription = ODM.StudyDescription( + _content="The description of the ODM study") + study.GlobalVariables.ProtocolName = ODM.ProtocolName( + _content="The ODM protocol name") self.assertTrue(study.verify_order()) def test_invalid_order_study_objects(self): study = ODM.Study(OID="ST.001.Test") study.GlobalVariables = ODM.GlobalVariables() - study.GlobalVariables.ProtocolName = ODM.ProtocolName(_content="The ODM protocol name") - study.GlobalVariables.StudyName = ODM.StudyName(_content="The ODM study name") - study.GlobalVariables.StudyDescription = ODM.StudyDescription(_content="The description of the ODM study") + study.GlobalVariables.ProtocolName = ODM.ProtocolName( + _content="The ODM protocol name") + study.GlobalVariables.StudyName = ODM.StudyName( + _content="The ODM study name") + study.GlobalVariables.StudyDescription = ODM.StudyDescription( + _content="The description of the ODM study") with self.assertRaises(ValueError): study.verify_order() @@ -41,9 +48,12 @@ def test_odm_order_incomplete(self): odm = ODM.ODM(**attrs) odm.Study.append(ODM.Study(OID="ST.001.Test")) odm.Study[0].GlobalVariables = ODM.GlobalVariables() - odm.Study[0].GlobalVariables.StudyName = ODM.StudyName(_content="The ODM study name") - odm.Study[0].GlobalVariables.StudyDescription = ODM.StudyDescription(_content="The description of the ODM study") - odm.Study[0].GlobalVariables.ProtocolName = ODM.ProtocolName(_content="The ODM protocol name") + odm.Study[0].GlobalVariables.StudyName = ODM.StudyName( + _content="The ODM study name") + odm.Study[0].GlobalVariables.StudyDescription = ODM.StudyDescription( + _content="The description of the ODM study") + odm.Study[0].GlobalVariables.ProtocolName = ODM.ProtocolName( + _content="The ODM protocol name") attrs = self.get_mdv_attributes() odm.Study[0].MetaDataVersion.append(ODM.MetaDataVersion(**attrs)) odm.Study[0].verify_order() @@ -53,29 +63,42 @@ def test_odm_order_multiple(self): odm = ODM.ODM(**attrs) odm.Study.append(ODM.Study(OID="ST.001.Test")) odm.Study[0].GlobalVariables = ODM.GlobalVariables() - odm.Study[0].GlobalVariables.StudyName = ODM.StudyName(_content="The ODM study name") - odm.Study[0].GlobalVariables.StudyDescription = ODM.StudyDescription(_content="The description of the ODM study") - odm.Study[0].GlobalVariables.ProtocolName = ODM.ProtocolName(_content="The ODM protocol name") + odm.Study[0].GlobalVariables.StudyName = ODM.StudyName( + _content="The ODM study name") + odm.Study[0].GlobalVariables.StudyDescription = ODM.StudyDescription( + _content="The description of the ODM study") + odm.Study[0].GlobalVariables.ProtocolName = ODM.ProtocolName( + _content="The ODM protocol name") attrs = self.get_mdv_attributes() odm.Study[0].MetaDataVersion.append(ODM.MetaDataVersion(**attrs)) - fd1 = ODM.FormDef(OID="ODM.F.VS", Name="Vital Signs Form", Repeating="Yes") + fd1 = ODM.FormDef( + OID="ODM.F.VS", Name="Vital Signs Form", Repeating="Yes") fd1.Description = ODM.Description() - fd1.Description.TranslatedText.append(ODM.TranslatedText(lang="en", _content="this is the test description")) - fd1.ItemGroupRef.append(ODM.ItemGroupRef(ItemGroupOID="ODM.IG.COMMON", Mandatory="Yes", OrderNumber=1)) - fd1.ItemGroupRef.append(ODM.ItemGroupRef(ItemGroupOID="ODM.IG.VS_GENERAL", Mandatory="Yes", OrderNumber=2)) - fd1.ItemGroupRef.append(ODM.ItemGroupRef(ItemGroupOID="ODM.IG.VS", Mandatory="Yes", OrderNumber=3)) - fd2 = ODM.FormDef(OID="ODM.F.DM", Name="Demographics Form", Repeating="No") - fd3 = ODM.FormDef(OID="ODM.F.AE", Name="Adverse Events Form", Repeating="Yes") + fd1.Description.TranslatedText.append(ODM.TranslatedText( + lang="en", _content="this is the test description")) + fd1.ItemGroupRef.append(ODM.ItemGroupRef( + ItemGroupOID="ODM.IG.COMMON", Mandatory="Yes", OrderNumber=1)) + fd1.ItemGroupRef.append(ODM.ItemGroupRef( + ItemGroupOID="ODM.IG.VS_GENERAL", Mandatory="Yes", OrderNumber=2)) + fd1.ItemGroupRef.append(ODM.ItemGroupRef( + ItemGroupOID="ODM.IG.VS", Mandatory="Yes", OrderNumber=3)) + fd2 = ODM.FormDef( + OID="ODM.F.DM", Name="Demographics Form", Repeating="No") + fd3 = ODM.FormDef( + OID="ODM.F.AE", Name="Adverse Events Form", Repeating="Yes") odm.Study[0].MetaDataVersion[0].FormDef = [fd1, fd2, fd3] odm.Study[0].verify_order() def test_odm_order_itemdef(self): # create an ItemDef - itd = ODM.ItemDef(OID="ODM.IT.DM.BRTHYR", Name="Birth Year", DataType="integer") + itd = ODM.ItemDef(OID="ODM.IT.DM.BRTHYR", + Name="Birth Year", DataType="integer") itd.Description = ODM.Description() - itd.Description.TranslatedText.append(ODM.TranslatedText(_content="Year of the subject's birth", lang="en")) + itd.Description.TranslatedText.append(ODM.TranslatedText( + _content="Year of the subject's birth", lang="en")) itd.Question = ODM.Question() - itd.Question.TranslatedText.append(ODM.TranslatedText(_content="Birth Year", lang="en")) + itd.Question.TranslatedText.append( + ODM.TranslatedText(_content="Birth Year", lang="en")) itd.Alias.append(ODM.Alias(Context="CDASH", Name="BRTHYR")) itd.Alias.append(ODM.Alias(Context="SDTM", Name="BRTHDTC")) self.assertTrue(itd.verify_order()) @@ -86,30 +109,39 @@ def test_odm_order_itemdef_invalid(self): odm = ODM.ODM(**attrs) odm.Study.append(ODM.Study(OID="ST.001.Test")) odm.Study[0].GlobalVariables = ODM.GlobalVariables() - odm.Study[0].GlobalVariables.StudyName = ODM.StudyName(_content="The ODM study name") - odm.Study[0].GlobalVariables.StudyDescription = ODM.StudyDescription(_content="The description of the ODM study") - odm.Study[0].GlobalVariables.ProtocolName = ODM.ProtocolName(_content="The ODM protocol name") + odm.Study[0].GlobalVariables.StudyName = ODM.StudyName( + _content="The ODM study name") + odm.Study[0].GlobalVariables.StudyDescription = ODM.StudyDescription( + _content="The description of the ODM study") + odm.Study[0].GlobalVariables.ProtocolName = ODM.ProtocolName( + _content="The ODM protocol name") attrs = self.get_mdv_attributes() odm.Study[0].MetaDataVersion.append(ODM.MetaDataVersion(**attrs)) - itd = ODM.ItemDef(OID="ODM.IT.DM.BRTHYR", Name="Birth Year", DataType="integer") + itd = ODM.ItemDef(OID="ODM.IT.DM.BRTHYR", + Name="Birth Year", DataType="integer") itd.Alias.append(ODM.Alias(Context="CDASH", Name="BRTHYR")) itd.Alias.append(ODM.Alias(Context="SDTM", Name="BRTHDTC")) itd.Description = ODM.Description() - itd.Description.TranslatedText.append(ODM.TranslatedText(_content="Year of the subject's birth", lang="en")) + itd.Description.TranslatedText.append(ODM.TranslatedText( + _content="Year of the subject's birth", lang="en")) itd.Question = ODM.Question() - itd.Question.TranslatedText.append(ODM.TranslatedText(_content="Birth Year", lang="en")) + itd.Question.TranslatedText.append( + ODM.TranslatedText(_content="Birth Year", lang="en")) odm.Study[0].MetaDataVersion[0].ItemDef.append(itd) with self.assertRaises(ValueError): self.assertTrue(odm.Study[0].MetaDataVersion[0].verify_order()) def test_reordering_object(self): - itd = ODM.ItemDef(OID="ODM.IT.DM.BRTHYR", Name="Birth Year", DataType="integer") + itd = ODM.ItemDef(OID="ODM.IT.DM.BRTHYR", + Name="Birth Year", DataType="integer") itd.Alias.append(ODM.Alias(Context="CDASH", Name="BRTHYR")) itd.Alias.append(ODM.Alias(Context="SDTM", Name="BRTHDTC")) itd.Description = ODM.Description() - itd.Description.TranslatedText.append(ODM.TranslatedText(_content="Year of the subject's birth", lang="en")) + itd.Description.TranslatedText.append(ODM.TranslatedText( + _content="Year of the subject's birth", lang="en")) itd.Question = ODM.Question() - itd.Question.TranslatedText.append(ODM.TranslatedText(_content="Birth Year", lang="en")) + itd.Question.TranslatedText.append( + ODM.TranslatedText(_content="Birth Year", lang="en")) with self.assertRaises(ValueError): itd.verify_order() itd.reorder_object() @@ -118,9 +150,12 @@ def test_reordering_object(self): def test_reorder_study_objects(self): study = ODM.Study(OID="ST.001.Test") study.GlobalVariables = ODM.GlobalVariables() - study.GlobalVariables.ProtocolName = ODM.ProtocolName(_content="The ODM protocol name") - study.GlobalVariables.StudyName = ODM.StudyName(_content="The ODM study name") - study.GlobalVariables.StudyDescription = ODM.StudyDescription(_content="The description of the ODM study") + study.GlobalVariables.ProtocolName = ODM.ProtocolName( + _content="The ODM protocol name") + study.GlobalVariables.StudyName = ODM.StudyName( + _content="The ODM study name") + study.GlobalVariables.StudyDescription = ODM.StudyDescription( + _content="The description of the ODM study") with self.assertRaises(ValueError): study.verify_order() study.GlobalVariables.reorder_object() @@ -169,10 +204,10 @@ def get_mdv_attributes(self): def get_root_attributes(self): return {"FileOID": "ODM.MDV.TEST.001", "Granularity": "Metadata", - "AsOfDateTime": "2020-07-13T00:13:51.309617+00:00", - "CreationDateTime": "2020-07-13T00:13:51.309617+00:00", "ODMVersion": "1.3.2", "FileType": "Snapshot", - "Originator": "RDS", "SourceSystem": "ODMLib", "SourceSystemVersion": "0.1", - "schemaLocation": "http://www.cdisc.org/ns/odm/v1.3 odm1-3-2.xsd"} + "AsOfDateTime": "2020-07-13T00:13:51.309617+00:00", + "CreationDateTime": "2020-07-13T00:13:51.309617+00:00", "ODMVersion": "1.3.2", "FileType": "Snapshot", + "Originator": "RDS", "SourceSystem": "ODMLib", "SourceSystemVersion": "0.1", + "schemaLocation": "http://www.cdisc.org/ns/odm/v1.3 odm1-3-2.xsd"} def add_ITD(self): # ItemDef 1 @@ -183,12 +218,14 @@ def add_ITD(self): q1 = ODM.Question() q1.TranslatedText = [ttq1] a1 = ODM.Alias(Context="CDASH", Name="VSDAT") - itd1 = ODM.ItemDef(OID="ODM.IT.VS.VSDAT", Name="Date", DataType="partialDate") + itd1 = ODM.ItemDef(OID="ODM.IT.VS.VSDAT", + Name="Date", DataType="partialDate") itd1.Description = desc1 itd1.Question = q1 itd1.Alias = [a1] # ItemDef 2 - ttd2 = ODM.TranslatedText(_content="Result of the vital signs measurement as originally received or collected.", lang="en") + ttd2 = ODM.TranslatedText( + _content="Result of the vital signs measurement as originally received or collected.", lang="en") ttq2 = ODM.TranslatedText(_content="Diastolic", lang="en") desc2 = ODM.Description() desc2.TranslatedText = [ttd2] @@ -196,7 +233,8 @@ def add_ITD(self): q2.TranslatedText = [ttq2] a2a = ODM.Alias(Context="CDASH", Name="BP.DIABP.VSORRES") a2b = ODM.Alias(Context="CDASH/SDTM", Name="VSORRES+VSORRESU") - itd2 = ODM.ItemDef(OID="ODM.IT.VS.BP.VSORRESU", Name="BP Units", DataType="text") + itd2 = ODM.ItemDef(OID="ODM.IT.VS.BP.VSORRESU", + Name="BP Units", DataType="text") itd2.Description = desc2 itd2.Question = q2 itd2.Alias = [a2a, a2b] @@ -204,24 +242,33 @@ def add_ITD(self): def add_IGD(self): itr1 = ODM.ItemRef(ItemOID="ODM.IT.VS.VSDAT", Mandatory="Yes") - itr2 = ODM.ItemRef(ItemOID="ODM.IT.VS.BP.DIABP.VSORRES", Mandatory="Yes") - itr3 = ODM.ItemRef(ItemOID="ODM.IT.VS.BP.SYSBP.VSORRES", Mandatory="Yes") - igd1 = ODM.ItemGroupDef(OID="ODM.IG.VS", Name="Vital Sign Measurement", Repeating="Yes") + itr2 = ODM.ItemRef( + ItemOID="ODM.IT.VS.BP.DIABP.VSORRES", Mandatory="Yes") + itr3 = ODM.ItemRef( + ItemOID="ODM.IT.VS.BP.SYSBP.VSORRES", Mandatory="Yes") + igd1 = ODM.ItemGroupDef( + OID="ODM.IG.VS", Name="Vital Sign Measurement", Repeating="Yes") igd1.ItemRef = [itr1, itr2, itr3] igr4 = ODM.ItemRef(ItemOID="ODM.IT.DM.BRTHYR", Mandatory="Yes") igr5 = ODM.ItemRef(ItemOID="ODM.IT.DM.SEX", Mandatory="Yes") - igd2 = ODM.ItemGroupDef(OID="ODM.IG.DM", Name="Demographics", Repeating="No") + igd2 = ODM.ItemGroupDef( + OID="ODM.IG.DM", Name="Demographics", Repeating="No") igd2.ItemRef = [igr4, igr5] return [igd1, igd2] def add_FD(self): - igr1 = ODM.ItemGroupRef(ItemGroupOID="ODM.IG.Common", Mandatory="Yes", OrderNumber=1) - igr2 = ODM.ItemGroupRef(ItemGroupOID="ODM.IG.VS_GENERAL", Mandatory="Yes", OrderNumber=2) - igr3 = ODM.ItemGroupRef(ItemGroupOID="ODM.IG.VS", Mandatory="Yes", OrderNumber=3) + igr1 = ODM.ItemGroupRef( + ItemGroupOID="ODM.IG.Common", Mandatory="Yes", OrderNumber=1) + igr2 = ODM.ItemGroupRef( + ItemGroupOID="ODM.IG.VS_GENERAL", Mandatory="Yes", OrderNumber=2) + igr3 = ODM.ItemGroupRef(ItemGroupOID="ODM.IG.VS", + Mandatory="Yes", OrderNumber=3) fd1 = ODM.FormDef(OID="ODM.F.VS", Name="Vital Signs", Repeating="No") fd1.ItemGroupRef = [igr1, igr2, igr3] - igr4 = ODM.ItemGroupRef(ItemGroupOID="ODM.IG.Common", Mandatory="Yes", OrderNumber=1) - igr5 = ODM.ItemGroupRef(ItemGroupOID="ODM.IG.DM", Mandatory="Yes", OrderNumber=2) + igr4 = ODM.ItemGroupRef( + ItemGroupOID="ODM.IG.Common", Mandatory="Yes", OrderNumber=1) + igr5 = ODM.ItemGroupRef(ItemGroupOID="ODM.IG.DM", + Mandatory="Yes", OrderNumber=2) fd2 = ODM.FormDef(OID="ODM.F.DM", Name="Demographics", Repeating="No") fd2.ItemGroupRef = [igr4, igr5] return [fd1, fd2] @@ -230,17 +277,21 @@ def add_SED(self): fr1 = ODM.FormRef(FormOID="ODM.F.DM", Mandatory="Yes", OrderNumber=1) fr2 = ODM.FormRef(FormOID="ODM.F.VS", Mandatory="Yes", OrderNumber=2) fr3 = ODM.FormRef(FormOID="ODM.F.AE", Mandatory="Yes", OrderNumber=3) - ser1 = ODM.StudyEventDef(OID="BASELINE", Name="Baseline Visit", Repeating="No", Type="Scheduled") + ser1 = ODM.StudyEventDef( + OID="BASELINE", Name="Baseline Visit", Repeating="No", Type="Scheduled") ser1.FormRef = [fr1, fr2, fr3] - ser2 = ODM.StudyEventDef(OID="FOLLOW-UP", Name="Follow-up Visit", Repeating="Yes", Type="Scheduled") + ser2 = ODM.StudyEventDef( + OID="FOLLOW-UP", Name="Follow-up Visit", Repeating="Yes", Type="Scheduled") ser2.FormRef = [fr1, fr2, fr3] return [ser1, ser2] def add_MD(self): - tt1 = ODM.TranslatedText(_content="Concatenation of BRTHYR, BRTHMO, and BRTHDY in ISO 8601 format", lang="en") + tt1 = ODM.TranslatedText( + _content="Concatenation of BRTHYR, BRTHMO, and BRTHDY in ISO 8601 format", lang="en") desc = ODM.Description() desc.TranslatedText = [tt1] - md = ODM.MethodDef(OID="ODM.MT.DOB", Name="Create BRTHDTC from date ELEMENTS", Type="Computation") + md = ODM.MethodDef( + OID="ODM.MT.DOB", Name="Create BRTHDTC from date ELEMENTS", Type="Computation") md.Description = desc return [md] @@ -255,18 +306,23 @@ def add_CL(self): dc2.TranslatedText = [tt2] cli2 = ODM.CodeListItem(CodedValue="Y") cli2.Decode = dc2 - cl = ODM.CodeList(OID="ODM.CL.NY_SUB_Y_N", Name="No Yes Response", DataType="text") + cl = ODM.CodeList(OID="ODM.CL.NY_SUB_Y_N", + Name="No Yes Response", DataType="text") cl.CodeListItem = [cli1, cli2] return [cl] def add_protocol(self): p = ODM.Protocol() # invalid order for protocol content to test re-order - p.Alias = [ODM.Alias(Context="ClinicalTrials.gov", Name="trace-protocol")] - tt = ODM.TranslatedText(_content="Trace-XML Test CDASH File", lang="en") + p.Alias = [ODM.Alias(Context="ClinicalTrials.gov", + Name="trace-protocol")] + tt = ODM.TranslatedText( + _content="Trace-XML Test CDASH File", lang="en") p.Description = ODM.Description() p.Description.TranslatedText = [tt] - ser1 = ODM.StudyEventRef(StudyEventOID="BASELINE", OrderNumber=1, Mandatory="Yes") - ser2 = ODM.StudyEventRef(StudyEventOID="FOLLOW-UP", OrderNumber=2, Mandatory="Yes") + ser1 = ODM.StudyEventRef( + StudyEventOID="BASELINE", OrderNumber=1, Mandatory="Yes") + ser2 = ODM.StudyEventRef( + StudyEventOID="FOLLOW-UP", OrderNumber=2, Mandatory="Yes") p.StudyEventRef = [ser1, ser2] return p diff --git a/tests/test_bottom_up.py b/tests/test_bottom_up.py index a5c33ea..33fcb1c 100644 --- a/tests/test_bottom_up.py +++ b/tests/test_bottom_up.py @@ -24,7 +24,8 @@ def test_create_init_objects(self): def add_study(self): study_name = ODM.StudyName(_content="ODM XML Test Study Name") protocol_name = ODM.ProtocolName(_content="ODM XML Test Study") - study_description = ODM.StudyDescription(_content="Testing the generation of an ODM XML file") + study_description = ODM.StudyDescription( + _content="Testing the generation of an ODM XML file") gv = ODM.GlobalVariables() gv.StudyName = study_name gv.StudyDescription = study_description @@ -49,18 +50,22 @@ def add_mdv(self): return mdv def add_CD(self): - tt1 = ODM.TranslatedText(_content="Skip the BRTHMO field when BRTHYR length NE 4", lang="en") + tt1 = ODM.TranslatedText( + _content="Skip the BRTHMO field when BRTHYR length NE 4", lang="en") desc = ODM.Description() desc.TranslatedText = [tt1] - cd = ODM.ConditionDef(OID="ODM.CD.BRTHMO", Name="Skip BRTHMO when no BRTHYR") + cd = ODM.ConditionDef(OID="ODM.CD.BRTHMO", + Name="Skip BRTHMO when no BRTHYR") cd.Description = desc return [cd] def add_MD(self): - tt1 = ODM.TranslatedText(_content="Concatenation of BRTHYR, BRTHMO, and BRTHDY in ISO 8601 format", lang="en") + tt1 = ODM.TranslatedText( + _content="Concatenation of BRTHYR, BRTHMO, and BRTHDY in ISO 8601 format", lang="en") desc = ODM.Description() desc.TranslatedText = [tt1] - md = ODM.MethodDef(OID="ODM.MT.DOB", Name="Create BRTHDTC from date ELEMENTS", Type="Computation") + md = ODM.MethodDef( + OID="ODM.MT.DOB", Name="Create BRTHDTC from date ELEMENTS", Type="Computation") md.Description = desc return [md] @@ -75,11 +80,11 @@ def add_CL(self): dc2.TranslatedText = [tt2] cli2 = ODM.CodeListItem(CodedValue="Y") cli2.Decode = dc2 - cl = ODM.CodeList(OID="ODM.CL.NY_SUB_Y_N", Name="No Yes Response", DataType="text") + cl = ODM.CodeList(OID="ODM.CL.NY_SUB_Y_N", + Name="No Yes Response", DataType="text") cl.CodeListItem = [cli1, cli2] return [cl] - def add_ITD(self): # ItemDef 1 ttd1 = ODM.TranslatedText(_content="Date of measurements", lang="en") @@ -89,12 +94,14 @@ def add_ITD(self): q1 = ODM.Question() q1.TranslatedText = [ttq1] a1 = ODM.Alias(Context="CDASH", Name="VSDAT") - itd1 = ODM.ItemDef(OID="ODM.IT.VS.VSDAT", Name="Date", DataType="partialDate") + itd1 = ODM.ItemDef(OID="ODM.IT.VS.VSDAT", + Name="Date", DataType="partialDate") itd1.Description = desc1 itd1.Question = q1 itd1.Alias = [a1] # ItemDef 2 - ttd2 = ODM.TranslatedText(_content="Result of the vital signs measurement as originally received or collected.", lang="en") + ttd2 = ODM.TranslatedText( + _content="Result of the vital signs measurement as originally received or collected.", lang="en") ttq2 = ODM.TranslatedText(_content="Diastolic", lang="en") desc2 = ODM.Description() desc2.TranslatedText = [ttd2] @@ -102,7 +109,8 @@ def add_ITD(self): q2.TranslatedText = [ttq2] a2a = ODM.Alias(Context="CDASH", Name="BP.DIABP.VSORRES") a2b = ODM.Alias(Context="CDASH/SDTM", Name="VSORRES+VSORRESU") - itd2 = ODM.ItemDef(OID="ODM.IT.VS.BP.VSORRESU", Name="BP Units", DataType="text") + itd2 = ODM.ItemDef(OID="ODM.IT.VS.BP.VSORRESU", + Name="BP Units", DataType="text") itd2.Description = desc2 itd2.Question = q2 itd2.Alias = [a2a, a2b] @@ -110,24 +118,33 @@ def add_ITD(self): def add_IGD(self): itr1 = ODM.ItemRef(ItemOID="ODM.IT.VS.VSDAT", Mandatory="Yes") - itr2 = ODM.ItemRef(ItemOID="ODM.IT.VS.BP.DIABP.VSORRES", Mandatory="Yes") - itr3 = ODM.ItemRef(ItemOID="ODM.IT.VS.BP.SYSBP.VSORRES", Mandatory="Yes") - igd1 = ODM.ItemGroupDef(OID="ODM.IG.VS", Name="Vital Sign Measurement", Repeating="Yes") + itr2 = ODM.ItemRef( + ItemOID="ODM.IT.VS.BP.DIABP.VSORRES", Mandatory="Yes") + itr3 = ODM.ItemRef( + ItemOID="ODM.IT.VS.BP.SYSBP.VSORRES", Mandatory="Yes") + igd1 = ODM.ItemGroupDef( + OID="ODM.IG.VS", Name="Vital Sign Measurement", Repeating="Yes") igd1.ItemRef = [itr1, itr2, itr3] igr4 = ODM.ItemRef(ItemOID="ODM.IT.DM.BRTHYR", Mandatory="Yes") igr5 = ODM.ItemRef(ItemOID="ODM.IT.DM.SEX", Mandatory="Yes") - igd2 = ODM.ItemGroupDef(OID="ODM.IG.DM", Name="Demographics", Repeating="No") + igd2 = ODM.ItemGroupDef( + OID="ODM.IG.DM", Name="Demographics", Repeating="No") igd2.ItemRef = [igr4, igr5] return [igd1, igd2] def add_FD(self): - igr1 = ODM.ItemGroupRef(ItemGroupOID="ODM.IG.Common", Mandatory="Yes", OrderNumber=1) - igr2 = ODM.ItemGroupRef(ItemGroupOID="ODM.IG.VS_GENERAL", Mandatory="Yes", OrderNumber=2) - igr3 = ODM.ItemGroupRef(ItemGroupOID="ODM.IG.VS", Mandatory="Yes", OrderNumber=3) + igr1 = ODM.ItemGroupRef( + ItemGroupOID="ODM.IG.Common", Mandatory="Yes", OrderNumber=1) + igr2 = ODM.ItemGroupRef( + ItemGroupOID="ODM.IG.VS_GENERAL", Mandatory="Yes", OrderNumber=2) + igr3 = ODM.ItemGroupRef(ItemGroupOID="ODM.IG.VS", + Mandatory="Yes", OrderNumber=3) fd1 = ODM.FormDef(OID="ODM.F.VS", Name="Vital Signs", Repeating="No") fd1.ItemGroupRef = [igr1, igr2, igr3] - igr4 = ODM.ItemGroupRef(ItemGroupOID="ODM.IG.Common", Mandatory="Yes", OrderNumber=1) - igr5 = ODM.ItemGroupRef(ItemGroupOID="ODM.IG.DM", Mandatory="Yes", OrderNumber=2) + igr4 = ODM.ItemGroupRef( + ItemGroupOID="ODM.IG.Common", Mandatory="Yes", OrderNumber=1) + igr5 = ODM.ItemGroupRef(ItemGroupOID="ODM.IG.DM", + Mandatory="Yes", OrderNumber=2) fd2 = ODM.FormDef(OID="ODM.F.DM", Name="Demographics", Repeating="No") fd2.ItemGroupRef = [igr4, igr5] return [fd1, fd2] @@ -136,19 +153,24 @@ def add_SED(self): fr1 = ODM.FormRef(FormOID="ODM.F.DM", Mandatory="Yes", OrderNumber=1) fr2 = ODM.FormRef(FormOID="ODM.F.VS", Mandatory="Yes", OrderNumber=2) fr3 = ODM.FormRef(FormOID="ODM.F.AE", Mandatory="Yes", OrderNumber=3) - ser1 = ODM.StudyEventDef(OID="BASELINE", Name="Baseline Visit", Repeating="No", Type="Scheduled") + ser1 = ODM.StudyEventDef( + OID="BASELINE", Name="Baseline Visit", Repeating="No", Type="Scheduled") ser1.FormRef = [fr1, fr2, fr3] - ser2 = ODM.StudyEventDef(OID="FOLLOW-UP", Name="Follow-up Visit", Repeating="Yes", Type="Scheduled") + ser2 = ODM.StudyEventDef( + OID="FOLLOW-UP", Name="Follow-up Visit", Repeating="Yes", Type="Scheduled") ser2.FormRef = [fr1, fr2, fr3] return [ser1, ser2] def add_protocol(self): p = ODM.Protocol() - tt = ODM.TranslatedText(_content="Trace-XML Test CDASH File", lang="en") + tt = ODM.TranslatedText( + _content="Trace-XML Test CDASH File", lang="en") desc = ODM.Description() desc.TranslatedText = [tt] - ser1 = ODM.StudyEventRef(StudyEventOID="BASELINE", OrderNumber=1, Mandatory="Yes") - ser2 = ODM.StudyEventRef(StudyEventOID="FOLLOW-UP", OrderNumber=2, Mandatory="Yes") + ser1 = ODM.StudyEventRef( + StudyEventOID="BASELINE", OrderNumber=1, Mandatory="Yes") + ser2 = ODM.StudyEventRef( + StudyEventOID="FOLLOW-UP", OrderNumber=2, Mandatory="Yes") a = ODM.Alias(Context="ClinicalTrials.gov", Name="trace-protocol") p.Description = desc p.StudyEventRef = [ser1, ser2] @@ -158,5 +180,6 @@ def add_protocol(self): def set_mdv_attributes(self): return {"OID": "MDV.TRACE-XML-ODM-01", "Name": "TRACE-XML MDV", "Description": "Trace-XML Example"} + if __name__ == '__main__': unittest.main() diff --git a/tests/test_cerberus_schemas_odm.py b/tests/test_cerberus_schemas_odm.py index 0c7f48a..37cb88e 100644 --- a/tests/test_cerberus_schemas_odm.py +++ b/tests/test_cerberus_schemas_odm.py @@ -28,7 +28,8 @@ def test_TranslatedText(self): self.assertTrue(is_valid) def test_Alias(self): - schema = {"Context": {"type": "string", "required": True}, "Name": {"type": "string", "required": True}} + schema = {"Context": {"type": "string", "required": True}, + "Name": {"type": "string", "required": True}} doc = {"Context": "nci:ExtCodeID", "Name": "C64848"} check = CheckODM(schema, doc) is_valid = check.check_odm() @@ -37,23 +38,29 @@ def test_Alias(self): def test_Description(self): schema = {"TranslatedText": {"type": "list", "schema": { "type": "dict", "schema": {"lang": {"type": "string"}, "_content": {"type": "string", "required": True}}}}} - doc = {'TranslatedText': [{'_content': 'Trace-XML Test CDASH File', 'lang': 'en'}]} + doc = {'TranslatedText': [ + {'_content': 'Trace-XML Test CDASH File', 'lang': 'en'}]} check = CheckODM(schema, doc) is_valid = check.check_odm() self.assertTrue(is_valid) def test_Protocol(self): p = ODM.Protocol() - tt = ODM.TranslatedText(_content="Trace-XML Test CDASH File", lang="en") + tt = ODM.TranslatedText( + _content="Trace-XML Test CDASH File", lang="en") p.Description = ODM.Description() p.Description.TranslatedText = [tt] - ser1 = ODM.StudyEventRef(StudyEventOID="BASELINE", OrderNumber="1", Mandatory="Yes") - ser2 = ODM.StudyEventRef(StudyEventOID="FOLLOW-UP", OrderNumber="2", Mandatory="Yes") + ser1 = ODM.StudyEventRef( + StudyEventOID="BASELINE", OrderNumber="1", Mandatory="Yes") + ser2 = ODM.StudyEventRef( + StudyEventOID="FOLLOW-UP", OrderNumber="2", Mandatory="Yes") p.StudyEventRef = [ser1, ser2] - p.Alias = [ODM.Alias(Context="ClinicalTrials.gov", Name="trace-protocol")] + p.Alias = [ODM.Alias(Context="ClinicalTrials.gov", + Name="trace-protocol")] description_dict = p.Description.to_dict() print(description_dict) - is_valid = self.validator.check_conformance(description_dict, "Description") + is_valid = self.validator.check_conformance( + description_dict, "Description") self.assertTrue(is_valid) protocol_dict = p.to_dict() print(protocol_dict) @@ -62,9 +69,10 @@ def test_Protocol(self): def test_StudEventDef(self): attrs = {"OID": "ODM.SE.BASELINE", "Name": "Baseline Visit", "Repeating": "No", "Type": "Scheduled", - "Category": "Pre-treatment"} + "Category": "Pre-treatment"} sed = ODM.StudyEventDef(**attrs) - tt1 = ODM.TranslatedText(_content="this is the first test description", lang="en") + tt1 = ODM.TranslatedText( + _content="this is the first test description", lang="en") sed.Description = ODM.Description() sed.Description.TranslatedText = [tt1] fr1 = ODM.FormRef(FormOID="ODM.F.VS", Mandatory="Yes", OrderNumber="1") @@ -80,15 +88,20 @@ def test_StudEventDef(self): self.assertTrue(is_valid) def test_FormDef(self): - formdef = ODM.FormDef(OID="ODM.F.VS", Name="Vital Signs Form", Repeating="Yes") - tt1 = ODM.TranslatedText(_content="this is the first test description", lang="en") + formdef = ODM.FormDef( + OID="ODM.F.VS", Name="Vital Signs Form", Repeating="Yes") + tt1 = ODM.TranslatedText( + _content="this is the first test description", lang="en") formdef.Description = ODM.Description() formdef.Description.TranslatedText = [tt1] - igr = ODM.ItemGroupRef(ItemGroupOID="ODM.IG.COMMON", Mandatory="Yes", OrderNumber="1") + igr = ODM.ItemGroupRef(ItemGroupOID="ODM.IG.COMMON", + Mandatory="Yes", OrderNumber="1") formdef.ItemGroupRef.append(igr) - igr = ODM.ItemGroupRef(ItemGroupOID="ODM.IG.VS_GENERAL", Mandatory="Yes", OrderNumber="2") + igr = ODM.ItemGroupRef( + ItemGroupOID="ODM.IG.VS_GENERAL", Mandatory="Yes", OrderNumber="2") formdef.ItemGroupRef.append(igr) - igr = ODM.ItemGroupRef(ItemGroupOID="ODM.IG.VS", Mandatory="Yes", OrderNumber="3") + igr = ODM.ItemGroupRef(ItemGroupOID="ODM.IG.VS", + Mandatory="Yes", OrderNumber="3") formdef.ItemGroupRef.append(igr) formdef.Alias.append(ODM.Alias(Context="SDTMIG", Name="VS")) fd_dict = formdef.to_dict() @@ -98,13 +111,16 @@ def test_FormDef(self): def test_ItemGroupDef(self): attrs = {"OID": "IG.VS", "Name": "VS", "Repeating": "Yes", "Domain": "VS", "Name": "VS", "SASDatasetName": "VS", - "IsReferenceData": "No", "Purpose": "Tabulation"} + "IsReferenceData": "No", "Purpose": "Tabulation"} igd = ODM.ItemGroupDef(**attrs) - tt = ODM.TranslatedText(_content="this is the first test description", lang="en") + tt = ODM.TranslatedText( + _content="this is the first test description", lang="en") igd.Description = ODM.Description() igd.Description.TranslatedText = [tt] - ir1 = ODM.ItemRef(ItemOID="IT.STUDYID", Mandatory="Yes", OrderNumber="1") - ir2 = ODM.ItemRef(ItemOID="IT.VS.VSTEST", Mandatory="No", OrderNumber="2") + ir1 = ODM.ItemRef(ItemOID="IT.STUDYID", + Mandatory="Yes", OrderNumber="1") + ir2 = ODM.ItemRef(ItemOID="IT.VS.VSTEST", + Mandatory="No", OrderNumber="2") igd.ItemRef = [ir1, ir2] igd_dict = igd.to_dict() print(igd_dict) @@ -117,8 +133,10 @@ def test_RangeCheck(self): tt2 = ODM.TranslatedText(_content="code de test invalide", lang="fr") rc.ErrorMessage.TranslatedText = [tt1, tt2] rc.CheckValue = [ODM.CheckValue(_content="DIABP")] - rc.FormalExpression = [ODM.FormalExpression(Context="Python 3.7", _content="print('hello world')")] - rc.MeasurementUnitRef = ODM.MeasurementUnitRef(MeasurementUnitOID="ODM.MU.UNITS") + rc.FormalExpression = [ODM.FormalExpression( + Context="Python 3.7", _content="print('hello world')")] + rc.MeasurementUnitRef = ODM.MeasurementUnitRef( + MeasurementUnitOID="ODM.MU.UNITS") rc_dict = rc.to_dict() print(rc_dict) is_valid = self.validator.check_conformance(rc_dict, "RangeCheck") @@ -126,11 +144,14 @@ def test_RangeCheck(self): def test_ItemDef(self): attrs = {"OID": "ODM.IT.AE.AEYN", "Name": "Any AEs?", "DataType": "text", "Length": "1", "SASFieldName": "AEYN", - "SDSVarName": "AEYN", "Origin": "CRF", "Comment": "Data management field"} + "SDSVarName": "AEYN", "Origin": "CRF", "Comment": "Data management field"} item = ODM.ItemDef(**attrs) - item.Description.TranslatedText = [ODM.TranslatedText(_content="this is the first test description", lang="en")] - item.Question.TranslatedText = [ODM.TranslatedText(_content="Any AEs?", lang="en")] - item.CodeListRef = ODM.CodeListRef(CodeListOID="CL.NY_SUB_Y_N_2011-10-24") + item.Description.TranslatedText = [ODM.TranslatedText( + _content="this is the first test description", lang="en")] + item.Question.TranslatedText = [ + ODM.TranslatedText(_content="Any AEs?", lang="en")] + item.CodeListRef = ODM.CodeListRef( + CodeListOID="CL.NY_SUB_Y_N_2011-10-24") item_dict = item.to_dict() print(item_dict) is_valid = self.validator.check_conformance(item_dict, "ItemDef") @@ -149,7 +170,8 @@ def test_CodeListItem(self): self.assertTrue(is_valid) def test_CodeList(self): - attrs = {"OID": "ODM.CL.LBTESTCD", "Name": "Laboratory Test Code", "DataType": "text"} + attrs = {"OID": "ODM.CL.LBTESTCD", + "Name": "Laboratory Test Code", "DataType": "text"} cl = ODM.CodeList(**attrs) eni = ODM.EnumeratedItem(CodedValue="HGB", OrderNumber=1) eni.Alias = [ODM.Alias(Context="nci:ExtCodeID", Name="C64848")] @@ -161,9 +183,11 @@ def test_CodeList(self): self.assertTrue(is_valid) def test_ExternalCodeList(self): - attrs = {"OID": "ODM.CL.LBTESTCD", "Name": "Laboratory Test Code", "DataType": "text"} + attrs = {"OID": "ODM.CL.LBTESTCD", + "Name": "Laboratory Test Code", "DataType": "text"} cl = ODM.CodeList(**attrs) - excl = ODM.ExternalCodeList(Dictionary="MedDRA", Version="23.0", href="https://www.meddra.org/") + excl = ODM.ExternalCodeList( + Dictionary="MedDRA", Version="23.0", href="https://www.meddra.org/") cl.ExternalCodeList = excl cl_dict = cl.to_dict() is_valid = self.validator.check_conformance(cl_dict, "CodeList") @@ -173,24 +197,31 @@ def test_ConditionDef(self): attrs = {"OID": "ODM.CD.BRTHMO", "Name": "Skip BRTHMO when no BRTHYR"} cd = ODM.ConditionDef(**attrs) cd.Description = ODM.Description() - cd.Description.TranslatedText = [ODM.TranslatedText(_content="Skip the BRTHMO field when BRTHYR length NE 4", lang="en")] - cd.FormalExpression = [ODM.FormalExpression(Context="Python 3.7", _content="BRTHYR != 4")] + cd.Description.TranslatedText = [ODM.TranslatedText( + _content="Skip the BRTHMO field when BRTHYR length NE 4", lang="en")] + cd.FormalExpression = [ODM.FormalExpression( + Context="Python 3.7", _content="BRTHYR != 4")] cd_dict = cd.to_dict() print(cd_dict) is_valid = self.validator.check_conformance(cd_dict, "ConditionDef") self.assertTrue(is_valid) def test_MethodDef(self): - attrs = {"OID": "ODM.MT.AGE", "Name": "Algorithm to derive AGE", "Type": "Computation"} + attrs = {"OID": "ODM.MT.AGE", + "Name": "Algorithm to derive AGE", "Type": "Computation"} method = ODM.MethodDef(**attrs) method.Description = ODM.Description() - method.Description.TranslatedText.append(ODM.TranslatedText(_content="Age at Screening Date (Screening Date - Birth date)", lang="en")) - method.FormalExpression.append(ODM.FormalExpression(Context="Python 3.7", _content="print('hello world')")) - is_valid = self.validator.check_conformance(method.to_dict(), "MethodDef") + method.Description.TranslatedText.append(ODM.TranslatedText( + _content="Age at Screening Date (Screening Date - Birth date)", lang="en")) + method.FormalExpression.append(ODM.FormalExpression( + Context="Python 3.7", _content="print('hello world')")) + is_valid = self.validator.check_conformance( + method.to_dict(), "MethodDef") self.assertTrue(is_valid) def test_OID_unique(self): - attrs = {"OID": "MDV.TRACE-XML-ODM-01", "Name": "TRACE-XML MDV", "Description": "Trace-XML Example"} + attrs = {"OID": "MDV.TRACE-XML-ODM-01", + "Name": "TRACE-XML MDV", "Description": "Trace-XML Example"} self.mdv = ODM.MetaDataVersion(**attrs) self.mdv.Protocol = self.add_protocol() self.mdv.StudyEventDef = self.add_SED() @@ -205,7 +236,8 @@ def test_OID_unique(self): self.assertTrue(oid_checker.check_oid_refs()) def test_unused_OID(self): - attrs = {"OID": "MDV.TRACE-XML-ODM-01", "Name": "TRACE-XML MDV", "Description": "Trace-XML Example"} + attrs = {"OID": "MDV.TRACE-XML-ODM-01", + "Name": "TRACE-XML MDV", "Description": "Trace-XML Example"} self.mdv = ODM.MetaDataVersion(**attrs) self.mdv.Protocol = self.add_protocol() self.mdv.StudyEventDef = self.add_SED() @@ -225,7 +257,8 @@ def test_unused_OID(self): self.assertDictEqual(orphans, expected_orphans) def test_MetaDataVersion(self): - attrs = {"OID": "MDV.TRACE-XML-ODM-01", "Name": "TRACE-XML MDV", "Description": "Trace-XML Example"} + attrs = {"OID": "MDV.TRACE-XML-ODM-01", + "Name": "TRACE-XML MDV", "Description": "Trace-XML Example"} self.mdv = ODM.MetaDataVersion(**attrs) self.mdv.Protocol = self.add_protocol() self.mdv.StudyEventDef = self.add_SED() @@ -237,64 +270,84 @@ def test_MetaDataVersion(self): self.mdv.ConditionDef = self.add_CD() mdv_dict = self.mdv.to_dict() print(mdv_dict) - is_valid = self.validator.check_conformance(mdv_dict, "MetaDataVersion") + is_valid = self.validator.check_conformance( + mdv_dict, "MetaDataVersion") self.assertTrue(is_valid) def add_protocol(self): p = ODM.Protocol() - tt = ODM.TranslatedText(_content="Trace-XML Test CDASH File", lang="en") + tt = ODM.TranslatedText( + _content="Trace-XML Test CDASH File", lang="en") p.Description = ODM.Description() p.Description.TranslatedText = [tt] - ser1 = ODM.StudyEventRef(StudyEventOID="BASELINE", OrderNumber=1, Mandatory="Yes") - ser2 = ODM.StudyEventRef(StudyEventOID="FOLLOW-UP", OrderNumber=2, Mandatory="Yes") + ser1 = ODM.StudyEventRef( + StudyEventOID="BASELINE", OrderNumber=1, Mandatory="Yes") + ser2 = ODM.StudyEventRef( + StudyEventOID="FOLLOW-UP", OrderNumber=2, Mandatory="Yes") p.StudyEventRef = [ser1, ser2] - p.Alias = [ODM.Alias(Context="ClinicalTrials.gov", Name="trace-protocol")] + p.Alias = [ODM.Alias(Context="ClinicalTrials.gov", + Name="trace-protocol")] return p def add_SED(self): fr1 = ODM.FormRef(FormOID="ODM.F.DM", Mandatory="Yes", OrderNumber=1) fr2 = ODM.FormRef(FormOID="ODM.F.VS", Mandatory="Yes", OrderNumber=2) fr3 = ODM.FormRef(FormOID="ODM.F.AE", Mandatory="Yes", OrderNumber=3) - ser1 = ODM.StudyEventDef(OID="BASELINE", Name="Baseline Visit", Repeating="No", Type="Scheduled") + ser1 = ODM.StudyEventDef( + OID="BASELINE", Name="Baseline Visit", Repeating="No", Type="Scheduled") ser1.FormRef = [fr1, fr2, fr3] - ser2 = ODM.StudyEventDef(OID="FOLLOW-UP", Name="Follow-up Visit", Repeating="Yes", Type="Scheduled") + ser2 = ODM.StudyEventDef( + OID="FOLLOW-UP", Name="Follow-up Visit", Repeating="Yes", Type="Scheduled") ser2.FormRef = [fr1, fr2, fr3] return [ser1, ser2] def add_FD(self): - igr1 = ODM.ItemGroupRef(ItemGroupOID="ODM.IG.Common", Mandatory="Yes", OrderNumber=1) + igr1 = ODM.ItemGroupRef( + ItemGroupOID="ODM.IG.Common", Mandatory="Yes", OrderNumber=1) #igr2 = ODM.ItemGroupRef(ItemGroupOID="ODM.IG.VS_GENERAL", Mandatory="Yes", OrderNumber=2) - igr3 = ODM.ItemGroupRef(ItemGroupOID="ODM.IG.VS", Mandatory="Yes", OrderNumber=3) + igr3 = ODM.ItemGroupRef(ItemGroupOID="ODM.IG.VS", + Mandatory="Yes", OrderNumber=3) fd1 = ODM.FormDef(OID="ODM.F.VS", Name="Vital Signs", Repeating="No") #fd1.ItemGroupRef = [igr1, igr2, igr3] fd1.ItemGroupRef = [igr1, igr3] - igr4 = ODM.ItemGroupRef(ItemGroupOID="ODM.IG.Common", Mandatory="Yes", OrderNumber=1) - igr5 = ODM.ItemGroupRef(ItemGroupOID="ODM.IG.DM", Mandatory="Yes", OrderNumber=2) + igr4 = ODM.ItemGroupRef( + ItemGroupOID="ODM.IG.Common", Mandatory="Yes", OrderNumber=1) + igr5 = ODM.ItemGroupRef(ItemGroupOID="ODM.IG.DM", + Mandatory="Yes", OrderNumber=2) fd2 = ODM.FormDef(OID="ODM.F.DM", Name="Demographics", Repeating="No") fd2.ItemGroupRef = [igr4, igr5] - igr6 = ODM.ItemGroupRef(ItemGroupOID="ODM.IG.Common", Mandatory="Yes", OrderNumber=1) - igr7 = ODM.ItemGroupRef(ItemGroupOID="ODM.IG.AE", Mandatory="Yes", OrderNumber=2) - fd3 = ODM.FormDef(OID="ODM.F.AE", Name="Adverse Events", Repeating="No") + igr6 = ODM.ItemGroupRef( + ItemGroupOID="ODM.IG.Common", Mandatory="Yes", OrderNumber=1) + igr7 = ODM.ItemGroupRef(ItemGroupOID="ODM.IG.AE", + Mandatory="Yes", OrderNumber=2) + fd3 = ODM.FormDef( + OID="ODM.F.AE", Name="Adverse Events", Repeating="No") fd3.ItemGroupRef = [igr6, igr7] return [fd1, fd2, fd3] def add_IGD(self): itr1 = ODM.ItemRef(ItemOID="ODM.IT.VS.VSDAT", Mandatory="Yes") - itr2 = ODM.ItemRef(ItemOID="ODM.IT.VS.BP.DIABP.VSORRES", Mandatory="Yes") - itr3 = ODM.ItemRef(ItemOID="ODM.IT.VS.BP.SYSBP.VSORRES", Mandatory="Yes") - igd1 = ODM.ItemGroupDef(OID="ODM.IG.VS", Name="Vital Sign Measurement", Repeating="Yes") + itr2 = ODM.ItemRef( + ItemOID="ODM.IT.VS.BP.DIABP.VSORRES", Mandatory="Yes") + itr3 = ODM.ItemRef( + ItemOID="ODM.IT.VS.BP.SYSBP.VSORRES", Mandatory="Yes") + igd1 = ODM.ItemGroupDef( + OID="ODM.IG.VS", Name="Vital Sign Measurement", Repeating="Yes") igd1.ItemRef = [itr1, itr2, itr3] igr4 = ODM.ItemRef(ItemOID="ODM.IT.DM.BRTHYR", Mandatory="Yes") igr5 = ODM.ItemRef(ItemOID="ODM.IT.DM.SEX", Mandatory="Yes") - igd2 = ODM.ItemGroupDef(OID="ODM.IG.DM", Name="Demographics", Repeating="No") + igd2 = ODM.ItemGroupDef( + OID="ODM.IG.DM", Name="Demographics", Repeating="No") igd2.ItemRef = [igr4, igr5] igr6 = ODM.ItemRef(ItemOID="ODM.IT.Common.SubjectID", Mandatory="Yes") igr7 = ODM.ItemRef(ItemOID="ODM.IT.Common.Visit", Mandatory="Yes") - igd3 = ODM.ItemGroupDef(OID="ODM.IG.Common", Name="Common", Repeating="No") + igd3 = ODM.ItemGroupDef(OID="ODM.IG.Common", + Name="Common", Repeating="No") igd3.ItemRef = [igr6, igr7] igr8 = ODM.ItemRef(ItemOID="ODM.IT.AE.AETERM", Mandatory="Yes") igr9 = ODM.ItemRef(ItemOID="ODM.IT.AE.AESEV", Mandatory="Yes") - igd4 = ODM.ItemGroupDef(OID="ODM.IG.AE", Name="Adverse Events", Repeating="Yes") + igd4 = ODM.ItemGroupDef( + OID="ODM.IG.AE", Name="Adverse Events", Repeating="Yes") igd4.ItemRef = [igr8, igr9] return [igd1, igd2, igd3, igd4] @@ -307,12 +360,14 @@ def add_ITD(self): q1 = ODM.Question() q1.TranslatedText = [ttq1] a1 = ODM.Alias(Context="CDASH", Name="VSDAT") - itd1 = ODM.ItemDef(OID="ODM.IT.VS.VSDAT", Name="Date", DataType="partialDate") + itd1 = ODM.ItemDef(OID="ODM.IT.VS.VSDAT", + Name="Date", DataType="partialDate") itd1.Description = desc1 itd1.Question = q1 itd1.Alias = [a1] # ItemDef 2 - ttd2 = ODM.TranslatedText(_content="Result of the vital signs measurement as originally received or collected.", lang="en") + ttd2 = ODM.TranslatedText( + _content="Result of the vital signs measurement as originally received or collected.", lang="en") ttq2 = ODM.TranslatedText(_content="Diastolic", lang="en") desc2 = ODM.Description() desc2.TranslatedText = [ttd2] @@ -320,7 +375,8 @@ def add_ITD(self): q2.TranslatedText = [ttq2] a2a = ODM.Alias(Context="CDASH", Name="BP.DIABP.VSORRES") a2b = ODM.Alias(Context="CDASH/SDTM", Name="VSORRES+VSORRESU") - itd2 = ODM.ItemDef(OID="ODM.IT.VS.BP.VSORRESU", Name="BP Units", DataType="text") + itd2 = ODM.ItemDef(OID="ODM.IT.VS.BP.VSORRESU", + Name="BP Units", DataType="text") itd2.Description = desc2 itd2.Question = q2 itd2.Alias = [a2a, a2b] @@ -331,7 +387,8 @@ def add_ITD(self): desc3.TranslatedText = [ttd3] q3 = ODM.Question() q3.TranslatedText = [ttq3] - itd3 = ODM.ItemDef(OID="ODM.IT.AE.AETERM", Name="AE Term", DataType="text") + itd3 = ODM.ItemDef(OID="ODM.IT.AE.AETERM", + Name="AE Term", DataType="text") itd3.Description = desc3 itd3.Question = q3 # ItemDef 4 @@ -341,7 +398,8 @@ def add_ITD(self): desc4.TranslatedText = [ttd4] q4 = ODM.Question() q4.TranslatedText = [ttq4] - itd4 = ODM.ItemDef(OID="ODM.IT.AE.AESEV", Name="AE Severity", DataType="text") + itd4 = ODM.ItemDef(OID="ODM.IT.AE.AESEV", + Name="AE Severity", DataType="text") itd4.Description = desc4 itd4.Question = q4 # ItemDef 5 @@ -351,17 +409,20 @@ def add_ITD(self): desc5.TranslatedText = [ttd5] q5 = ODM.Question() q5.TranslatedText = [ttq5] - itd5 = ODM.ItemDef(OID="ODM.IT.Common.SubjectID", Name="Subject ID", DataType="text") + itd5 = ODM.ItemDef(OID="ODM.IT.Common.SubjectID", + Name="Subject ID", DataType="text") itd5.Description = desc5 itd5.Question = q5 # ItemDef 6 - ttd6 = ODM.TranslatedText(_content="Diastolic Blood Pressure Result", lang="en") + ttd6 = ODM.TranslatedText( + _content="Diastolic Blood Pressure Result", lang="en") ttq6 = ODM.TranslatedText(_content="Diastolic BP", lang="en") desc6 = ODM.Description() desc6.TranslatedText = [ttd6] q6 = ODM.Question() q6.TranslatedText = [ttq6] - itd6 = ODM.ItemDef(OID="ODM.IT.VS.BP.DIABP.VSORRES", Name="DBP Result", DataType="text") + itd6 = ODM.ItemDef(OID="ODM.IT.VS.BP.DIABP.VSORRES", + Name="DBP Result", DataType="text") itd6.Description = desc6 itd6.Question = q6 # ItemDef 7 @@ -371,7 +432,8 @@ def add_ITD(self): desc7.TranslatedText = [ttd7] q7 = ODM.Question() q7.TranslatedText = [ttq7] - itd7 = ODM.ItemDef(OID="ODM.IT.DM.BRTHYR", Name="Birth Year", DataType="text") + itd7 = ODM.ItemDef(OID="ODM.IT.DM.BRTHYR", + Name="Birth Year", DataType="text") itd7.Description = desc7 itd7.Question = q7 # ItemDef 8 @@ -381,7 +443,8 @@ def add_ITD(self): desc8.TranslatedText = [ttd8] q8 = ODM.Question() q8.TranslatedText = [ttq8] - itd8 = ODM.ItemDef(OID="ODM.IT.Common.Visit", Name="Visit", DataType="text") + itd8 = ODM.ItemDef(OID="ODM.IT.Common.Visit", + Name="Visit", DataType="text") itd8.Description = desc8 itd8.Question = q8 # ItemDef 9 @@ -395,30 +458,36 @@ def add_ITD(self): itd9.Description = desc9 itd9.Question = q9 # ItemDef 10 - ttd10 = ODM.TranslatedText(_content="Systolic Blood Pressure Result", lang="en") + ttd10 = ODM.TranslatedText( + _content="Systolic Blood Pressure Result", lang="en") ttq10 = ODM.TranslatedText(_content="Systolic BP", lang="en") desc10 = ODM.Description() desc10.TranslatedText = [ttd10] q10 = ODM.Question() q10.TranslatedText = [ttq10] - itd10 = ODM.ItemDef(OID="ODM.IT.VS.BP.SYSBP.VSORRES", Name="Systolic BP Result", DataType="text") + itd10 = ODM.ItemDef(OID="ODM.IT.VS.BP.SYSBP.VSORRES", + Name="Systolic BP Result", DataType="text") itd10.Description = desc10 itd10.Question = q10 return [itd1, itd2, itd3, itd4, itd5, itd6, itd7, itd8, itd9, itd10] def add_CD(self): - tt1 = ODM.TranslatedText(_content="Skip the BRTHMO field when BRTHYR length NE 4", lang="en") + tt1 = ODM.TranslatedText( + _content="Skip the BRTHMO field when BRTHYR length NE 4", lang="en") desc = ODM.Description() desc.TranslatedText = [tt1] - cd = ODM.ConditionDef(OID="ODM.CD.BRTHMO", Name="Skip BRTHMO when no BRTHYR") + cd = ODM.ConditionDef(OID="ODM.CD.BRTHMO", + Name="Skip BRTHMO when no BRTHYR") cd.Description = desc return [cd] def add_MD(self): - tt1 = ODM.TranslatedText(_content="Concatenation of BRTHYR, BRTHMO, and BRTHDY in ISO 8601 format", lang="en") + tt1 = ODM.TranslatedText( + _content="Concatenation of BRTHYR, BRTHMO, and BRTHDY in ISO 8601 format", lang="en") desc = ODM.Description() desc.TranslatedText = [tt1] - md = ODM.MethodDef(OID="ODM.MT.DOB", Name="Create BRTHDTC from date ELEMENTS", Type="Computation") + md = ODM.MethodDef( + OID="ODM.MT.DOB", Name="Create BRTHDTC from date ELEMENTS", Type="Computation") md.Description = desc return [md] @@ -433,6 +502,7 @@ def add_CL(self): dc2.TranslatedText = [tt2] cli2 = ODM.CodeListItem(CodedValue="Y") cli2.Decode = dc2 - cl = ODM.CodeList(OID="ODM.CL.NY_SUB_Y_N", Name="No Yes Response", DataType="text") + cl = ODM.CodeList(OID="ODM.CL.NY_SUB_Y_N", + Name="No Yes Response", DataType="text") cl.CodeListItem = [cli1, cli2] return [cl] diff --git a/tests/test_clinicalData.py b/tests/test_clinicalData.py index 6f9fc4b..f94ce67 100644 --- a/tests/test_clinicalData.py +++ b/tests/test_clinicalData.py @@ -10,7 +10,6 @@ from tests import get_data_file_path - class TestClinicalData(TestCase): def setUp(self) -> None: self.odm_test_file = get_data_file_path('test_clinical_data_01.xml') @@ -18,37 +17,61 @@ def setUp(self) -> None: def test_clinical_data_to_xml(self): cd = [] - cd.append(ODM.ClinicalData(StudyOID="P2006-101", MetaDataVersionOID="101.01")) - cd[0].SubjectData.append(ODM.SubjectData(SubjectKey="1000", TransactionType="Insert")) - cd[0].SubjectData[0].StudyEventData.append(ODM.StudyEventData(StudyEventOID="Screen")) - cd[0].SubjectData[0].StudyEventData[0].FormData.append(ODM.FormData(FormOID="DEMOG")) - cd[0].SubjectData[0].StudyEventData[0].FormData[0].ItemGroupData.append(ODM.ItemGroupData(ItemGroupOID="DM")) - cd[0].SubjectData[0].StudyEventData[0].FormData[0].ItemGroupData[0].ItemData.append(ODM.ItemData(ItemOID="USUBJID", Value="101-001-001")) - cd[0].SubjectData[0].StudyEventData[0].FormData[0].ItemGroupData[0].ItemData.append(ODM.ItemData(ItemOID="SEX", Value="F")) - cd[0].SubjectData[0].StudyEventData[0].FormData.append(ODM.FormData(FormOID="LABDATA")) - cd[0].SubjectData[0].StudyEventData[0].FormData[1].ItemGroupData.append(ODM.ItemGroupData(ItemGroupOID="LB")) - cd[0].SubjectData[0].StudyEventData[0].FormData[1].ItemGroupData[0].ItemData.append(ODM.ItemData(ItemOID="LBDTC", Value="2006-07-14T14:48")) - cd[0].SubjectData[0].StudyEventData[0].FormData[1].ItemGroupData[0].ItemData.append(ODM.ItemData(ItemOID="LBTESTCD", Value="ALT")) - cd[0].SubjectData[0].StudyEventData[0].FormData[1].ItemGroupData[0].ItemData.append(ODM.ItemData(ItemOID="LBORRES", Value="245")) - - cd.append(ODM.ClinicalData(StudyOID="P2006-101", MetaDataVersionOID="101.02")) - cd[1].SubjectData.append(ODM.SubjectData(SubjectKey="1000", TransactionType="Insert")) - cd[1].SubjectData[0].StudyEventData.append(ODM.StudyEventData(StudyEventOID="VISIT_1")) - cd[1].SubjectData[0].StudyEventData[0].FormData.append(ODM.FormData(FormOID="AENONSER")) - cd[1].SubjectData[0].StudyEventData[0].FormData[0].ItemGroupData.append(ODM.ItemGroupData(ItemGroupOID="AE")) - cd[1].SubjectData[0].StudyEventData[0].FormData[0].ItemGroupData[0].ItemData.append(ODM.ItemData(ItemOID="AETERM", Value="Fever")) - cd[1].SubjectData[0].StudyEventData[0].FormData[0].ItemGroupData[0].ItemData.append(ODM.ItemData(ItemOID="AESTDTC", Value="2006-08-21")) - cd[1].SubjectData[0].StudyEventData[0].FormData.append(ODM.FormData(FormOID="LABDATA")) - cd[1].SubjectData[0].StudyEventData[0].FormData[1].ItemGroupData.append(ODM.ItemGroupData(ItemGroupOID="LB")) - cd[1].SubjectData[0].StudyEventData[0].FormData[1].ItemGroupData[0].ItemData.append(ODM.ItemData(ItemOID="LBDTC", Value="2006-07-14T14:48")) - cd[1].SubjectData[0].StudyEventData[0].FormData[1].ItemGroupData[0].ItemData.append(ODM.ItemData(ItemOID="LBTESTCD", Value="ALT")) - cd[1].SubjectData[0].StudyEventData[0].FormData[1].ItemGroupData[0].ItemData.append(ODM.ItemData(ItemOID="LBORRES", Value="300")) + cd.append(ODM.ClinicalData( + StudyOID="P2006-101", MetaDataVersionOID="101.01")) + cd[0].SubjectData.append(ODM.SubjectData( + SubjectKey="1000", TransactionType="Insert")) + cd[0].SubjectData[0].StudyEventData.append( + ODM.StudyEventData(StudyEventOID="Screen")) + cd[0].SubjectData[0].StudyEventData[0].FormData.append( + ODM.FormData(FormOID="DEMOG")) + cd[0].SubjectData[0].StudyEventData[0].FormData[0].ItemGroupData.append( + ODM.ItemGroupData(ItemGroupOID="DM")) + cd[0].SubjectData[0].StudyEventData[0].FormData[0].ItemGroupData[0].ItemData.append( + ODM.ItemData(ItemOID="USUBJID", Value="101-001-001")) + cd[0].SubjectData[0].StudyEventData[0].FormData[0].ItemGroupData[0].ItemData.append( + ODM.ItemData(ItemOID="SEX", Value="F")) + cd[0].SubjectData[0].StudyEventData[0].FormData.append( + ODM.FormData(FormOID="LABDATA")) + cd[0].SubjectData[0].StudyEventData[0].FormData[1].ItemGroupData.append( + ODM.ItemGroupData(ItemGroupOID="LB")) + cd[0].SubjectData[0].StudyEventData[0].FormData[1].ItemGroupData[0].ItemData.append( + ODM.ItemData(ItemOID="LBDTC", Value="2006-07-14T14:48")) + cd[0].SubjectData[0].StudyEventData[0].FormData[1].ItemGroupData[0].ItemData.append( + ODM.ItemData(ItemOID="LBTESTCD", Value="ALT")) + cd[0].SubjectData[0].StudyEventData[0].FormData[1].ItemGroupData[0].ItemData.append( + ODM.ItemData(ItemOID="LBORRES", Value="245")) + + cd.append(ODM.ClinicalData( + StudyOID="P2006-101", MetaDataVersionOID="101.02")) + cd[1].SubjectData.append(ODM.SubjectData( + SubjectKey="1000", TransactionType="Insert")) + cd[1].SubjectData[0].StudyEventData.append( + ODM.StudyEventData(StudyEventOID="VISIT_1")) + cd[1].SubjectData[0].StudyEventData[0].FormData.append( + ODM.FormData(FormOID="AENONSER")) + cd[1].SubjectData[0].StudyEventData[0].FormData[0].ItemGroupData.append( + ODM.ItemGroupData(ItemGroupOID="AE")) + cd[1].SubjectData[0].StudyEventData[0].FormData[0].ItemGroupData[0].ItemData.append( + ODM.ItemData(ItemOID="AETERM", Value="Fever")) + cd[1].SubjectData[0].StudyEventData[0].FormData[0].ItemGroupData[0].ItemData.append( + ODM.ItemData(ItemOID="AESTDTC", Value="2006-08-21")) + cd[1].SubjectData[0].StudyEventData[0].FormData.append( + ODM.FormData(FormOID="LABDATA")) + cd[1].SubjectData[0].StudyEventData[0].FormData[1].ItemGroupData.append( + ODM.ItemGroupData(ItemGroupOID="LB")) + cd[1].SubjectData[0].StudyEventData[0].FormData[1].ItemGroupData[0].ItemData.append( + ODM.ItemData(ItemOID="LBDTC", Value="2006-07-14T14:48")) + cd[1].SubjectData[0].StudyEventData[0].FormData[1].ItemGroupData[0].ItemData.append( + ODM.ItemData(ItemOID="LBTESTCD", Value="ALT")) + cd[1].SubjectData[0].StudyEventData[0].FormData[1].ItemGroupData[0].ItemData.append( + ODM.ItemData(ItemOID="LBORRES", Value="300")) root = self.create_odm_document(cd) odm_xml = root.to_xml() self.assertEqual(odm_xml.attrib["FileOID"], "ODM.TEST.CD.001") - self.assertListEqual(["ClinicalData", "ClinicalData"], [e.tag for e in odm_xml]) - + self.assertListEqual(["ClinicalData", "ClinicalData"], [ + e.tag for e in odm_xml]) def test_clinical_data_from_xml(self): parser = ODM_PARSER.ODMParser(self.odm_test_file) @@ -62,7 +85,8 @@ def test_clinical_data_from_xml(self): self.assertEqual(item_data[0]["Value"], "Fever") def test_load_odm_xml_data(self): - loader = LD.ODMLoader(OL.XMLODMLoader(model_package="odm_1_3_2", ns_uri="http://www.cdisc.org/ns/odm/v1.3")) + loader = LD.ODMLoader(OL.XMLODMLoader( + model_package="odm_1_3_2", ns_uri="http://www.cdisc.org/ns/odm/v1.3")) loader.open_odm_document(self.odm_test_file2) odm = loader.load_odm() self.assertEqual(odm.FileOID, "Study-Virus-20220308071610") @@ -73,7 +97,8 @@ def test_load_odm_xml_data(self): self.assertEqual(igd_0.ItemData[2].Value, "1966-02-10") def test_load_odm_xml_data_iterator(self): - loader = LD.ODMLoader(OL.XMLODMLoader(model_package="odm_1_3_2", ns_uri="http://www.cdisc.org/ns/odm/v1.3")) + loader = LD.ODMLoader(OL.XMLODMLoader( + model_package="odm_1_3_2", ns_uri="http://www.cdisc.org/ns/odm/v1.3")) loader.open_odm_document(self.odm_test_file2) odm = loader.load_odm() self.assertEqual(odm.FileOID, "Study-Virus-20220308071610") @@ -91,7 +116,8 @@ def test_load_odm_xml_data_iterator(self): self.assertListEqual(subjects, test_subjects) def test_find_record(self): - loader = LD.ODMLoader(OL.XMLODMLoader(model_package="odm_1_3_2", ns_uri="http://www.cdisc.org/ns/odm/v1.3")) + loader = LD.ODMLoader(OL.XMLODMLoader( + model_package="odm_1_3_2", ns_uri="http://www.cdisc.org/ns/odm/v1.3")) loader.open_odm_document(self.odm_test_file) odm = loader.load_odm() subject = odm.ClinicalData[0].find("SubjectData", "SubjectKey", "1000") @@ -99,7 +125,8 @@ def test_find_record(self): self.assertEqual(subject.SubjectKey, "1000") def test_audit_record(self): - ar = ODM.AuditRecord(EditPoint="Monitoring", UsedImputationMethod="Yes", ID="1") + ar = ODM.AuditRecord(EditPoint="Monitoring", + UsedImputationMethod="Yes", ID="1") ar.UserRef = ODM.UserRef(UserOID="USER.001") ar.LocationRef = ODM.LocationRef(LocationOID="SITE.101") ar.DateTimeStamp = ODM.DateTimeStamp(_content="2020-12-01T10:28:16") @@ -108,21 +135,29 @@ def test_audit_record(self): ar_dict = ar.to_dict() print(ar_dict) self.assertEqual(ar_dict["EditPoint"], "Monitoring") - id = ODM.ItemData(ItemOID="IT.AE.AETERM", TransactionType="Insert", Value="Fever") + id = ODM.ItemData(ItemOID="IT.AE.AETERM", + TransactionType="Insert", Value="Fever") id.AuditRecord = ar id_dict = id.to_dict() - self.assertEqual(id_dict["AuditRecord"]["UserRef"]["UserOID"], "USER.001") + self.assertEqual(id_dict["AuditRecord"] + ["UserRef"]["UserOID"], "USER.001") def test_annotation(self): - annotation = ODM.Annotation(SeqNum="1", TransactionType="Insert", ID="ANN001") - annotation.Comment = ODM.Comment(SponsorOrSite="Site", _content="Transferred from EHR") + annotation = ODM.Annotation( + SeqNum="1", TransactionType="Insert", ID="ANN001") + annotation.Comment = ODM.Comment( + SponsorOrSite="Site", _content="Transferred from EHR") annotation.Flag.append(ODM.Flag()) - annotation.Flag[0].FlagValue = ODM.FlagValue(CodeListOID="CL.FLAGVALUE", _content="eSource") - annotation.Flag[0].FlagType = ODM.FlagType(CodeListOID="CL.FLAGTYPE", _content="eDT") + annotation.Flag[0].FlagValue = ODM.FlagValue( + CodeListOID="CL.FLAGVALUE", _content="eSource") + annotation.Flag[0].FlagType = ODM.FlagType( + CodeListOID="CL.FLAGTYPE", _content="eDT") annotation_dict = annotation.to_dict() print(annotation_dict) - self.assertEqual(annotation_dict["Flag"][0]["FlagValue"]["CodeListOID"], "CL.FLAGVALUE") - self.assertEqual(annotation.Flag[0].FlagValue.CodeListOID, "CL.FLAGVALUE") + self.assertEqual( + annotation_dict["Flag"][0]["FlagValue"]["CodeListOID"], "CL.FLAGVALUE") + self.assertEqual( + annotation.Flag[0].FlagValue.CodeListOID, "CL.FLAGVALUE") def test_signature(self): sig = ODM.Signature(ID="SIG.001.USER.001") @@ -136,7 +171,8 @@ def test_signature(self): self.assertEqual(sig.SignatureRef.SignatureOID, "SIG.001") def test_audit_records(self): - ar = ODM.AuditRecord(EditPoint="Monitoring", UsedImputationMethod="Yes", ID="1") + ar = ODM.AuditRecord(EditPoint="Monitoring", + UsedImputationMethod="Yes", ID="1") ar.UserRef = ODM.UserRef(UserOID="USER.001") ar.LocationRef = ODM.LocationRef(LocationOID="SITE.101") ar.DateTimeStamp = ODM.DateTimeStamp(_content="2020-12-01T10:28:16") @@ -145,7 +181,8 @@ def test_audit_records(self): ars = ODM.AuditRecords() ars.AuditRecord.append(ar) self.assertEqual(ars.AuditRecord[0].ID, "1") - self.assertEqual(ars.AuditRecord[0].LocationRef.LocationOID, "SITE.101") + self.assertEqual( + ars.AuditRecord[0].LocationRef.LocationOID, "SITE.101") def test_signatures(self): sig = ODM.Signature(ID="SIG.001.USER.001") @@ -156,21 +193,28 @@ def test_signatures(self): sigs = ODM.Signatures() sigs.Signature.append(sig) self.assertEqual(sigs.Signature[0].ID, "SIG.001.USER.001") - self.assertEqual(sigs.Signature[0].SignatureRef.SignatureOID, "SIG.001") + self.assertEqual( + sigs.Signature[0].SignatureRef.SignatureOID, "SIG.001") def test_annotations(self): - annotation = ODM.Annotation(SeqNum="1", TransactionType="Insert", ID="ANN001") - annotation.Comment = ODM.Comment(SponsorOrSite="Site", _content="Transferred from EHR") + annotation = ODM.Annotation( + SeqNum="1", TransactionType="Insert", ID="ANN001") + annotation.Comment = ODM.Comment( + SponsorOrSite="Site", _content="Transferred from EHR") annotation.Flag.append(ODM.Flag()) - annotation.Flag[0].FlagValue = ODM.FlagValue(CodeListOID="CL.FLAGVALUE", _content="eSource") - annotation.Flag[0].FlagType = ODM.FlagType(CodeListOID="CL.FLAGTYPE", _content="eDT") + annotation.Flag[0].FlagValue = ODM.FlagValue( + CodeListOID="CL.FLAGVALUE", _content="eSource") + annotation.Flag[0].FlagType = ODM.FlagType( + CodeListOID="CL.FLAGTYPE", _content="eDT") anns = ODM.Annotations() anns.Annotation.append(annotation) self.assertEqual(anns.Annotation[0].ID, "ANN001") - self.assertEqual(anns.Annotation[0].Flag[0].FlagValue.CodeListOID, "CL.FLAGVALUE") + self.assertEqual( + anns.Annotation[0].Flag[0].FlagValue.CodeListOID, "CL.FLAGVALUE") def test_oid_ref_def_check_missing_mdv(self): - loader = LD.ODMLoader(OL.XMLODMLoader(model_package="odm_1_3_2", ns_uri="http://www.cdisc.org/ns/odm/v1.3")) + loader = LD.ODMLoader(OL.XMLODMLoader( + model_package="odm_1_3_2", ns_uri="http://www.cdisc.org/ns/odm/v1.3")) loader.open_odm_document(self.odm_test_file) odm = loader.load_odm() oid_checker = OID.OIDRef() @@ -179,7 +223,8 @@ def test_oid_ref_def_check_missing_mdv(self): odm.verify_oids(oid_checker) def test_oid_ref_def_check_with_mdv(self): - loader = LD.ODMLoader(OL.XMLODMLoader(model_package="odm_1_3_2", ns_uri="http://www.cdisc.org/ns/odm/v1.3")) + loader = LD.ODMLoader(OL.XMLODMLoader( + model_package="odm_1_3_2", ns_uri="http://www.cdisc.org/ns/odm/v1.3")) loader.open_odm_document(self.odm_test_file2) odm = loader.load_odm() oid_checker = OID.OIDRef() @@ -187,7 +232,8 @@ def test_oid_ref_def_check_with_mdv(self): self.assertTrue(oid_checker.check_oid_refs()) def test_metadata_conformance_check(self): - loader = LD.ODMLoader(OL.XMLODMLoader(model_package="odm_1_3_2", ns_uri="http://www.cdisc.org/ns/odm/v1.3")) + loader = LD.ODMLoader(OL.XMLODMLoader( + model_package="odm_1_3_2", ns_uri="http://www.cdisc.org/ns/odm/v1.3")) loader.open_odm_document(self.odm_test_file2) odm = loader.load_odm() validator = METADATA.MetadataSchema() diff --git a/tests/test_codeList.py b/tests/test_codeList.py index 507eeb4..396dd09 100644 --- a/tests/test_codeList.py +++ b/tests/test_codeList.py @@ -37,18 +37,22 @@ def test_add_enumerated_item(self): self.assertDictEqual(ei_dict, self.expected_eni_dict()) def test_external_code_list(self): - self.cl.ExternalCodeList = ODM.ExternalCodeList(Dictionary="MedDRA", Version="23.0", href="https://www.meddra.org/") + self.cl.ExternalCodeList = ODM.ExternalCodeList( + Dictionary="MedDRA", Version="23.0", href="https://www.meddra.org/") ex_dict = self.cl.to_dict() self.assertDictEqual(ex_dict, self.expected_ex_dict()) def test_add_description(self): - tt1 = ODM.TranslatedText(_content="this is the first test description", lang="en") - tt2 = ODM.TranslatedText(_content="this is the second test description", lang="en") + tt1 = ODM.TranslatedText( + _content="this is the first test description", lang="en") + tt2 = ODM.TranslatedText( + _content="this is the second test description", lang="en") desc = ODM.Description() desc.TranslatedText = [tt1, tt2] self.cl.Description = desc self.assertEqual(len(self.cl.Description.TranslatedText), 2) - self.assertEqual(self.cl.Description.TranslatedText[1]._content, 'this is the second test description') + self.assertEqual( + self.cl.Description.TranslatedText[1]._content, 'this is the second test description') def test_add_alias(self): alias = ODM.Alias(Context="nci:ExtCodeID", Name="C64848") @@ -109,14 +113,14 @@ def test_to_xml_code_list_item(self): def test_to_xml_external_code_list(self): attrs = self._set_attributes() cl = ODM.CodeList(**attrs) - excl = ODM.ExternalCodeList(Dictionary="MedDRA", Version="23.0", href="https://www.meddra.org/") + excl = ODM.ExternalCodeList( + Dictionary="MedDRA", Version="23.0", href="https://www.meddra.org/") cl.ExternalCodeList = excl cl_xml = cl.to_xml() self.assertEqual(cl_xml.attrib["OID"], "ODM.CL.LBTESTCD") ecl = cl_xml.find("ExternalCodeList") self.assertEqual(ecl.attrib["Dictionary"], "MedDRA") - def _set_attributes(self): """ set some CodeList element attributes using test data @@ -153,4 +157,3 @@ def expected_json_dict(self): {'Context': 'nci:ExtCodeID', 'Name': 'C65047'}] }] } - diff --git a/tests/test_codeListItem.py b/tests/test_codeListItem.py index 38f3f5a..303ed53 100644 --- a/tests/test_codeListItem.py +++ b/tests/test_codeListItem.py @@ -14,10 +14,12 @@ def test_add_decode(self): decode.TranslatedText.append(tt1) self.cli.Decode = decode self.assertEqual(len(self.cli.Decode.TranslatedText), 1) - self.assertEqual(self.cli.Decode.TranslatedText[0]._content, 'Hemoglobin') + self.assertEqual( + self.cli.Decode.TranslatedText[0]._content, 'Hemoglobin') def test_add_alias(self): - self.cli.Alias.append(ODM.Alias(Context="nci:ExtCodeID", Name="C64848")) + self.cli.Alias.append( + ODM.Alias(Context="nci:ExtCodeID", Name="C64848")) self.assertEqual(self.cli.Alias[0].Name, "C64848") def test_missing_attribute(self): diff --git a/tests/test_codeList_define.py b/tests/test_codeList_define.py index 4695857..07bff88 100644 --- a/tests/test_codeList_define.py +++ b/tests/test_codeList_define.py @@ -9,7 +9,8 @@ def setUp(self) -> None: self.cl = DEFINE.CodeList(**attrs) def test_add_code_list_item(self): - cli1 = DEFINE.CodeListItem(CodedValue="HGB", ExtendedValue="Yes", OrderNumber="1") + cli1 = DEFINE.CodeListItem( + CodedValue="HGB", ExtendedValue="Yes", OrderNumber="1") cli1.Alias.append(DEFINE.Alias(Context="nci:ExtCodeID", Name="C64848")) self.cl.CodeListItem.append(cli1) cli2 = DEFINE.CodeListItem(CodedValue="VITB12", OrderNumber="2") @@ -23,7 +24,8 @@ def test_add_code_list_item(self): self.assertDictEqual(cl_dict, self.expected_cli_dict()) def test_add_enumerated_item(self): - eni1 = DEFINE.EnumeratedItem(CodedValue="HGB", ExtendedValue="Yes", OrderNumber=1) + eni1 = DEFINE.EnumeratedItem( + CodedValue="HGB", ExtendedValue="Yes", OrderNumber=1) eni1.Alias = [DEFINE.Alias(Context="nci:ExtCodeID", Name="C64848")] self.cl.EnumeratedItem.append(eni1) eni2 = DEFINE.EnumeratedItem(CodedValue="VITB12", OrderNumber=2) @@ -37,18 +39,22 @@ def test_add_enumerated_item(self): self.assertDictEqual(ei_dict, self.expected_eni_dict()) def test_external_code_list(self): - self.cl.ExternalCodeList = DEFINE.ExternalCodeList(Dictionary="MedDRA", Version="23.0", href="https://www.meddra.org/") + self.cl.ExternalCodeList = DEFINE.ExternalCodeList( + Dictionary="MedDRA", Version="23.0", href="https://www.meddra.org/") ex_dict = self.cl.to_dict() self.assertDictEqual(ex_dict, self.expected_ex_dict()) def test_add_description(self): - tt1 = DEFINE.TranslatedText(_content="this is the first test description", lang="en") - tt2 = DEFINE.TranslatedText(_content="this is the second test description", lang="en") + tt1 = DEFINE.TranslatedText( + _content="this is the first test description", lang="en") + tt2 = DEFINE.TranslatedText( + _content="this is the second test description", lang="en") desc = DEFINE.Description() desc.TranslatedText = [tt1, tt2] self.cl.Description = desc self.assertEqual(len(self.cl.Description.TranslatedText), 2) - self.assertEqual(self.cl.Description.TranslatedText[1]._content, 'this is the second test description') + self.assertEqual( + self.cl.Description.TranslatedText[1]._content, 'this is the second test description') def test_add_alias(self): alias = DEFINE.Alias(Context="nci:ExtCodeID", Name="C64848") @@ -109,7 +115,8 @@ def test_to_xml_code_list_item(self): def test_to_xml_external_code_list(self): attrs = self._set_attributes() cl = DEFINE.CodeList(**attrs) - excl = DEFINE.ExternalCodeList(Dictionary="MedDRA", Version="23.0", href="https://www.meddra.org/") + excl = DEFINE.ExternalCodeList( + Dictionary="MedDRA", Version="23.0", href="https://www.meddra.org/") cl.ExternalCodeList = excl cl_xml = cl.to_xml() self.assertEqual(cl_xml.attrib["OID"], "ODM.CL.LBTESTCD") @@ -152,4 +159,3 @@ def expected_json_dict(self): {'Context': 'nci:ExtCodeID', 'Name': 'C65047'}] }] } - diff --git a/tests/test_conditiondef.py b/tests/test_conditiondef.py index a898e08..c8ce1ff 100644 --- a/tests/test_conditiondef.py +++ b/tests/test_conditiondef.py @@ -9,16 +9,20 @@ def setUp(self) -> None: self.cond_def = ODM.ConditionDef(**attrs) def test_add_formal_expression(self): - fex = ODM.FormalExpression(Context="Python 3.7", _content="BRTHYR != 4") + fex = ODM.FormalExpression( + Context="Python 3.7", _content="BRTHYR != 4") self.cond_def.FormalExpression = [fex] - self.assertEqual(self.cond_def.FormalExpression[0]._content, "BRTHYR != 4") + self.assertEqual( + self.cond_def.FormalExpression[0]._content, "BRTHYR != 4") def test_add_description(self): - tt1 = ODM.TranslatedText(_content="Skip the BRTHMO field when BRTHYR length NE 4", lang="en") + tt1 = ODM.TranslatedText( + _content="Skip the BRTHMO field when BRTHYR length NE 4", lang="en") self.cond_def.Description = ODM.Description() self.cond_def.Description.TranslatedText = [tt1] self.assertEqual(len(self.cond_def.Description.TranslatedText), 1) - self.assertEqual(self.cond_def.Description.TranslatedText[0]._content, 'Skip the BRTHMO field when BRTHYR length NE 4') + self.assertEqual( + self.cond_def.Description.TranslatedText[0]._content, 'Skip the BRTHMO field when BRTHYR length NE 4') def test_add_alias(self): a = ODM.Alias(Context="CDASH", Name="AGE") @@ -28,10 +32,12 @@ def test_add_alias(self): def test_to_json(self): attrs = self.set_attributes() cd = ODM.ConditionDef(**attrs) - tt1 = ODM.TranslatedText(_content="Skip the BRTHMO field when BRTHYR length NE 4", lang="en") + tt1 = ODM.TranslatedText( + _content="Skip the BRTHMO field when BRTHYR length NE 4", lang="en") cd.Description = ODM.Description() cd.Description.TranslatedText.append(tt1) - cd.FormalExpression = [ODM.FormalExpression(Context="Python 3.7", _content="BRTHYR != 4")] + cd.FormalExpression = [ODM.FormalExpression( + Context="Python 3.7", _content="BRTHYR != 4")] cd_dict = cd.to_dict() print(cd_dict) method_json = cd.to_json() @@ -43,8 +49,10 @@ def test_to_dict(self): attrs = self.set_attributes() cd = ODM.ConditionDef(**attrs) cd.Description = ODM.Description() - cd.Description.TranslatedText = [ODM.TranslatedText(_content="Skip the BRTHMO field when BRTHYR length NE 4", lang="en")] - cd.FormalExpression = [ODM.FormalExpression(Context="Python 3.7", _content="BRTHYR != 4")] + cd.Description.TranslatedText = [ODM.TranslatedText( + _content="Skip the BRTHMO field when BRTHYR length NE 4", lang="en")] + cd.FormalExpression = [ODM.FormalExpression( + Context="Python 3.7", _content="BRTHYR != 4")] cd_dict = cd.to_dict() print(cd_dict) self.assertEqual(cd_dict["OID"], "ODM.CD.BRTHMO") @@ -54,8 +62,10 @@ def test_to_xml(self): attrs = self.set_attributes() cd = ODM.ConditionDef(**attrs) cd.Description = ODM.Description() - cd.Description.TranslatedText = [ODM.TranslatedText(_content="Skip the BRTHMO field when BRTHYR length NE 4", lang="en")] - cd.FormalExpression = [ODM.FormalExpression(Context="Python 3.7", _content="BRTHYR != 4")] + cd.Description.TranslatedText = [ODM.TranslatedText( + _content="Skip the BRTHMO field when BRTHYR length NE 4", lang="en")] + cd.FormalExpression = [ODM.FormalExpression( + Context="Python 3.7", _content="BRTHYR != 4")] cd_xml = cd.to_xml() self.assertEqual(cd_xml.attrib["OID"], "ODM.CD.BRTHMO") fex_xml = cd_xml.find("FormalExpression") @@ -70,5 +80,6 @@ def set_attributes(self): def expected_dict(self): return {'OID': 'ODM.CD.BRTHMO', 'Name': 'Skip BRTHMO when no BRTHYR', 'Description': - {'TranslatedText': [{'_content': 'Skip the BRTHMO field when BRTHYR length NE 4', 'lang': 'en'}]}, - 'FormalExpression': [{'Context': 'Python 3.7', '_content': 'BRTHYR != 4'}]} + {'TranslatedText': [ + {'_content': 'Skip the BRTHMO field when BRTHYR length NE 4', 'lang': 'en'}]}, + 'FormalExpression': [{'Context': 'Python 3.7', '_content': 'BRTHYR != 4'}]} diff --git a/tests/test_conformance_checks_odm.py b/tests/test_conformance_checks_odm.py index e66728d..7736b83 100644 --- a/tests/test_conformance_checks_odm.py +++ b/tests/test_conformance_checks_odm.py @@ -10,7 +10,8 @@ def setUp(self): self.oid_checker = OID.OIDRef() def test_OID_unique_clean(self): - attrs = {"OID": "MDV.TRACE-XML-ODM-01", "Name": "TRACE-XML MDV", "Description": "Trace-XML Example"} + attrs = {"OID": "MDV.TRACE-XML-ODM-01", + "Name": "TRACE-XML MDV", "Description": "Trace-XML Example"} self.mdv = ODM.MetaDataVersion(**attrs) self.mdv.Protocol = self.add_protocol() self.mdv.StudyEventDef = self.add_SED() @@ -26,7 +27,8 @@ def test_OID_unique_clean(self): self.assertTrue(self.oid_checker.check_oid_refs()) def test_refdef_orphans(self): - attrs = {"OID": "MDV.TRACE-XML-ODM-01", "Name": "TRACE-XML MDV", "Description": "Trace-XML Example"} + attrs = {"OID": "MDV.TRACE-XML-ODM-01", + "Name": "TRACE-XML MDV", "Description": "Trace-XML Example"} self.mdv = ODM.MetaDataVersion(**attrs) self.mdv.Protocol = self.add_protocol() self.mdv.StudyEventDef = self.add_SED() @@ -45,7 +47,8 @@ def test_refdef_orphans(self): self.assertDictEqual(orphans, expected_orphans) def test_refdef_orphans_unitialized(self): - attrs = {"OID": "MDV.TRACE-XML-ODM-01", "Name": "TRACE-XML MDV", "Description": "Trace-XML Example"} + attrs = {"OID": "MDV.TRACE-XML-ODM-01", + "Name": "TRACE-XML MDV", "Description": "Trace-XML Example"} self.mdv = ODM.MetaDataVersion(**attrs) self.mdv.Protocol = self.add_protocol() self.mdv.StudyEventDef = self.add_SED() @@ -65,7 +68,8 @@ def test_refdef_orphans_unitialized(self): self.assertDictEqual(orphans, expected_orphans) def test_OID_unique_dirty(self): - attrs = {"OID": "MDV.TRACE-XML-ODM-01", "Name": "TRACE-XML MDV", "Description": "Trace-XML Example"} + attrs = {"OID": "MDV.TRACE-XML-ODM-01", + "Name": "TRACE-XML MDV", "Description": "Trace-XML Example"} self.mdv = ODM.MetaDataVersion(**attrs) self.mdv.Protocol = self.add_protocol() self.mdv.StudyEventDef = self.add_SED() @@ -81,7 +85,8 @@ def test_OID_unique_dirty(self): self.mdv.verify_oids(oid_checker) def test_OID_refdef_dirty(self): - attrs = {"OID": "MDV.TRACE-XML-ODM-01", "Name": "TRACE-XML MDV", "Description": "Trace-XML Example"} + attrs = {"OID": "MDV.TRACE-XML-ODM-01", + "Name": "TRACE-XML MDV", "Description": "Trace-XML Example"} self.mdv = ODM.MetaDataVersion(**attrs) self.mdv.Protocol = self.add_protocol() self.mdv.StudyEventDef = self.add_SED() @@ -97,27 +102,33 @@ def test_OID_refdef_dirty(self): self.mdv.verify_oids(oid_checker) def test_OID_creation_dirty(self): - attrs = {"OID": "MDV.TRACE-XML-ODM-01", "Description": "Trace-XML Example"} + attrs = {"OID": "MDV.TRACE-XML-ODM-01", + "Description": "Trace-XML Example"} with self.assertRaises(ValueError): # MDV creation should fail due to missing Name attribute self.mdv = ODM.MetaDataVersion(**attrs) def test_MethodDef_missing_Description(self): - attrs = {"OID": "MDV.TRACE-XML-ODM-01", "Name": "TRACE-XML MDV", "Description": "Trace-XML Example"} + attrs = {"OID": "MDV.TRACE-XML-ODM-01", + "Name": "TRACE-XML MDV", "Description": "Trace-XML Example"} self.mdv = ODM.MetaDataVersion(**attrs) - tt1 = ODM.TranslatedText(_content="Age at Screening Date (Screening Date - Birth date)", lang="en") + tt1 = ODM.TranslatedText( + _content="Age at Screening Date (Screening Date - Birth date)", lang="en") desc1 = ODM.Description() desc1.TranslatedText.append(tt1) # MethodDef creation that includes the Description object - md1 = ODM.MethodDef(OID="ODM.MT.AGE", Name="Algorithm to dervive AGE", Type="Computation", Description=desc1) + md1 = ODM.MethodDef(OID="ODM.MT.AGE", Name="Algorithm to dervive AGE", + Type="Computation", Description=desc1) self.mdv.MethodDef.append(md1) # MethodDef creation succeeds without Description object - md2 = ODM.MethodDef(OID="ODM.MT.DOB", Name="Create BRTHDTC from date ELEMENTS", Type="Computation") + md2 = ODM.MethodDef( + OID="ODM.MT.DOB", Name="Create BRTHDTC from date ELEMENTS", Type="Computation") self.mdv.MethodDef.append(md2) # test that both MethodDefs were added to MDV self.assertEqual(len(self.mdv.MethodDef), 2) # test that the content of Description exists for the first MethodDef - self.assertEqual(self.mdv.MethodDef[0].Description.TranslatedText[0]._content, "Age at Screening Date (Screening Date - Birth date)") + self.assertEqual(self.mdv.MethodDef[0].Description.TranslatedText[0]._content, + "Age at Screening Date (Screening Date - Birth date)") validator = METADATA.MetadataSchema() # this is how you check conformance that includes required elements, not just attributes is_valid = self.mdv.MethodDef[0].verify_conformance(validator) @@ -131,79 +142,104 @@ def test_MethodDef_missing_Description(self): def add_protocol(self): p = ODM.Protocol() - tt = ODM.TranslatedText(_content="Trace-XML Test CDASH File", lang="en") + tt = ODM.TranslatedText( + _content="Trace-XML Test CDASH File", lang="en") p.Description = ODM.Description() p.Description.TranslatedText = [tt] - ser1 = ODM.StudyEventRef(StudyEventOID="BASELINE", OrderNumber=1, Mandatory="Yes") - ser2 = ODM.StudyEventRef(StudyEventOID="FOLLOW-UP", OrderNumber=2, Mandatory="Yes") + ser1 = ODM.StudyEventRef( + StudyEventOID="BASELINE", OrderNumber=1, Mandatory="Yes") + ser2 = ODM.StudyEventRef( + StudyEventOID="FOLLOW-UP", OrderNumber=2, Mandatory="Yes") p.StudyEventRef = [ser1, ser2] - p.Alias = [ODM.Alias(Context="ClinicalTrials.gov", Name="trace-protocol")] + p.Alias = [ODM.Alias(Context="ClinicalTrials.gov", + Name="trace-protocol")] return p def add_SED(self): fr1 = ODM.FormRef(FormOID="ODM.F.DM", Mandatory="Yes", OrderNumber=1) fr2 = ODM.FormRef(FormOID="ODM.F.VS", Mandatory="Yes", OrderNumber=2) fr3 = ODM.FormRef(FormOID="ODM.F.AE", Mandatory="Yes", OrderNumber=3) - ser1 = ODM.StudyEventDef(OID="BASELINE", Name="Baseline Visit", Repeating="No", Type="Scheduled") + ser1 = ODM.StudyEventDef( + OID="BASELINE", Name="Baseline Visit", Repeating="No", Type="Scheduled") ser1.FormRef = [fr1, fr2, fr3] - ser2 = ODM.StudyEventDef(OID="FOLLOW-UP", Name="Follow-up Visit", Repeating="Yes", Type="Scheduled") + ser2 = ODM.StudyEventDef( + OID="FOLLOW-UP", Name="Follow-up Visit", Repeating="Yes", Type="Scheduled") ser2.FormRef = [fr1, fr2, fr3] return [ser1, ser2] def add_FD(self): - igr1 = ODM.ItemGroupRef(ItemGroupOID="ODM.IG.Common", Mandatory="Yes", OrderNumber=1) + igr1 = ODM.ItemGroupRef( + ItemGroupOID="ODM.IG.Common", Mandatory="Yes", OrderNumber=1) #igr2 = ODM.ItemGroupRef(ItemGroupOID="ODM.IG.VS_GENERAL", Mandatory="Yes", OrderNumber=2) - igr3 = ODM.ItemGroupRef(ItemGroupOID="ODM.IG.VS", Mandatory="Yes", OrderNumber=3) + igr3 = ODM.ItemGroupRef(ItemGroupOID="ODM.IG.VS", + Mandatory="Yes", OrderNumber=3) fd1 = ODM.FormDef(OID="ODM.F.VS", Name="Vital Signs", Repeating="No") #fd1.ItemGroupRef = [igr1, igr2, igr3] fd1.ItemGroupRef = [igr1, igr3] - igr4 = ODM.ItemGroupRef(ItemGroupOID="ODM.IG.Common", Mandatory="Yes", OrderNumber=1) - igr5 = ODM.ItemGroupRef(ItemGroupOID="ODM.IG.DM", Mandatory="Yes", OrderNumber=2) + igr4 = ODM.ItemGroupRef( + ItemGroupOID="ODM.IG.Common", Mandatory="Yes", OrderNumber=1) + igr5 = ODM.ItemGroupRef(ItemGroupOID="ODM.IG.DM", + Mandatory="Yes", OrderNumber=2) fd2 = ODM.FormDef(OID="ODM.F.DM", Name="Demographics", Repeating="No") fd2.ItemGroupRef = [igr4, igr5] - igr6 = ODM.ItemGroupRef(ItemGroupOID="ODM.IG.Common", Mandatory="Yes", OrderNumber=1) - igr7 = ODM.ItemGroupRef(ItemGroupOID="ODM.IG.AE", Mandatory="Yes", OrderNumber=2) - fd3 = ODM.FormDef(OID="ODM.F.AE", Name="Adverse Events", Repeating="No") + igr6 = ODM.ItemGroupRef( + ItemGroupOID="ODM.IG.Common", Mandatory="Yes", OrderNumber=1) + igr7 = ODM.ItemGroupRef(ItemGroupOID="ODM.IG.AE", + Mandatory="Yes", OrderNumber=2) + fd3 = ODM.FormDef( + OID="ODM.F.AE", Name="Adverse Events", Repeating="No") fd3.ItemGroupRef = [igr6, igr7] return [fd1, fd2, fd3] def add_IGD(self): itr1 = ODM.ItemRef(ItemOID="ODM.IT.VS.VSDAT", Mandatory="Yes") - itr2 = ODM.ItemRef(ItemOID="ODM.IT.VS.BP.DIABP.VSORRES", Mandatory="Yes") - itr3 = ODM.ItemRef(ItemOID="ODM.IT.VS.BP.SYSBP.VSORRES", Mandatory="Yes") - igd1 = ODM.ItemGroupDef(OID="ODM.IG.VS", Name="Vital Sign Measurement", Repeating="Yes") + itr2 = ODM.ItemRef( + ItemOID="ODM.IT.VS.BP.DIABP.VSORRES", Mandatory="Yes") + itr3 = ODM.ItemRef( + ItemOID="ODM.IT.VS.BP.SYSBP.VSORRES", Mandatory="Yes") + igd1 = ODM.ItemGroupDef( + OID="ODM.IG.VS", Name="Vital Sign Measurement", Repeating="Yes") igd1.ItemRef = [itr1, itr2, itr3] igr4 = ODM.ItemRef(ItemOID="ODM.IT.DM.BRTHYR", Mandatory="Yes") igr5 = ODM.ItemRef(ItemOID="ODM.IT.DM.SEX", Mandatory="Yes") - igd2 = ODM.ItemGroupDef(OID="ODM.IG.DM", Name="Demographics", Repeating="No") + igd2 = ODM.ItemGroupDef( + OID="ODM.IG.DM", Name="Demographics", Repeating="No") igd2.ItemRef = [igr4, igr5] igr6 = ODM.ItemRef(ItemOID="ODM.IT.Common.SubjectID", Mandatory="Yes") igr7 = ODM.ItemRef(ItemOID="ODM.IT.Common.Visit", Mandatory="Yes") - igd3 = ODM.ItemGroupDef(OID="ODM.IG.Common", Name="Common", Repeating="No") + igd3 = ODM.ItemGroupDef(OID="ODM.IG.Common", + Name="Common", Repeating="No") igd3.ItemRef = [igr6, igr7] igr8 = ODM.ItemRef(ItemOID="ODM.IT.AE.AETERM", Mandatory="Yes") igr9 = ODM.ItemRef(ItemOID="ODM.IT.AE.AESEV", Mandatory="Yes") - igd4 = ODM.ItemGroupDef(OID="ODM.IG.AE", Name="Adverse Events", Repeating="Yes") + igd4 = ODM.ItemGroupDef( + OID="ODM.IG.AE", Name="Adverse Events", Repeating="Yes") igd4.ItemRef = [igr8, igr9] return [igd1, igd2, igd3, igd4] def add_IGD_refdef(self): itr1 = ODM.ItemRef(ItemOID="ODM.IT.VS.VSDAT", Mandatory="Yes") - itr2 = ODM.ItemRef(ItemOID="ODM.IT.VS.BP.DIABP.VSORRES_BAD", Mandatory="Yes") - itr3 = ODM.ItemRef(ItemOID="ODM.IT.VS.BP.SYSBP.VSORRES", Mandatory="Yes") - igd1 = ODM.ItemGroupDef(OID="ODM.IG.VS", Name="Vital Sign Measurement", Repeating="Yes") + itr2 = ODM.ItemRef( + ItemOID="ODM.IT.VS.BP.DIABP.VSORRES_BAD", Mandatory="Yes") + itr3 = ODM.ItemRef( + ItemOID="ODM.IT.VS.BP.SYSBP.VSORRES", Mandatory="Yes") + igd1 = ODM.ItemGroupDef( + OID="ODM.IG.VS", Name="Vital Sign Measurement", Repeating="Yes") igd1.ItemRef = [itr1, itr2, itr3] igr4 = ODM.ItemRef(ItemOID="ODM.IT.DM.BRTHYR", Mandatory="Yes") igr5 = ODM.ItemRef(ItemOID="ODM.IT.DM.SEX", Mandatory="Yes") - igd2 = ODM.ItemGroupDef(OID="ODM.IG.DM", Name="Demographics", Repeating="No") + igd2 = ODM.ItemGroupDef( + OID="ODM.IG.DM", Name="Demographics", Repeating="No") igd2.ItemRef = [igr4, igr5] igr6 = ODM.ItemRef(ItemOID="ODM.IT.Common.SubjectID", Mandatory="Yes") igr7 = ODM.ItemRef(ItemOID="ODM.IT.Common.Visit", Mandatory="Yes") - igd3 = ODM.ItemGroupDef(OID="ODM.IG.Common", Name="Common", Repeating="No") + igd3 = ODM.ItemGroupDef(OID="ODM.IG.Common", + Name="Common", Repeating="No") igd3.ItemRef = [igr6, igr7] igr8 = ODM.ItemRef(ItemOID="ODM.IT.AE.AETERM", Mandatory="Yes") igr9 = ODM.ItemRef(ItemOID="ODM.IT.AE.AESEV", Mandatory="Yes") - igd4 = ODM.ItemGroupDef(OID="ODM.IG.AE", Name="Adverse Events", Repeating="Yes") + igd4 = ODM.ItemGroupDef( + OID="ODM.IG.AE", Name="Adverse Events", Repeating="Yes") igd4.ItemRef = [igr8, igr9] return [igd1, igd2, igd3, igd4] @@ -216,12 +252,14 @@ def add_ITD(self): q1 = ODM.Question() q1.TranslatedText = [ttq1] a1 = ODM.Alias(Context="CDASH", Name="VSDAT") - itd1 = ODM.ItemDef(OID="ODM.IT.VS.VSDAT", Name="Date", DataType="partialDate") + itd1 = ODM.ItemDef(OID="ODM.IT.VS.VSDAT", + Name="Date", DataType="partialDate") itd1.Description = desc1 itd1.Question = q1 itd1.Alias = [a1] # ItemDef 2 - ttd2 = ODM.TranslatedText(_content="Result of the vital signs measurement as originally received or collected.", lang="en") + ttd2 = ODM.TranslatedText( + _content="Result of the vital signs measurement as originally received or collected.", lang="en") ttq2 = ODM.TranslatedText(_content="Diastolic", lang="en") desc2 = ODM.Description() desc2.TranslatedText = [ttd2] @@ -229,7 +267,8 @@ def add_ITD(self): q2.TranslatedText = [ttq2] a2a = ODM.Alias(Context="CDASH", Name="BP.DIABP.VSORRES") a2b = ODM.Alias(Context="CDASH/SDTM", Name="VSORRES+VSORRESU") - itd2 = ODM.ItemDef(OID="ODM.IT.VS.BP.VSORRESU", Name="BP Units", DataType="text") + itd2 = ODM.ItemDef(OID="ODM.IT.VS.BP.VSORRESU", + Name="BP Units", DataType="text") itd2.Description = desc2 itd2.Question = q2 itd2.Alias = [a2a, a2b] @@ -240,7 +279,8 @@ def add_ITD(self): desc3.TranslatedText = [ttd3] q3 = ODM.Question() q3.TranslatedText = [ttq3] - itd3 = ODM.ItemDef(OID="ODM.IT.AE.AETERM", Name="AE Term", DataType="text") + itd3 = ODM.ItemDef(OID="ODM.IT.AE.AETERM", + Name="AE Term", DataType="text") itd3.Description = desc3 itd3.Question = q3 # ItemDef 4 @@ -250,7 +290,8 @@ def add_ITD(self): desc4.TranslatedText = [ttd4] q4 = ODM.Question() q4.TranslatedText = [ttq4] - itd4 = ODM.ItemDef(OID="ODM.IT.AE.AESEV", Name="AE Severity", DataType="text") + itd4 = ODM.ItemDef(OID="ODM.IT.AE.AESEV", + Name="AE Severity", DataType="text") itd4.Description = desc4 itd4.Question = q4 # ItemDef 5 @@ -260,17 +301,20 @@ def add_ITD(self): desc5.TranslatedText = [ttd5] q5 = ODM.Question() q5.TranslatedText = [ttq5] - itd5 = ODM.ItemDef(OID="ODM.IT.Common.SubjectID", Name="Subject ID", DataType="text") + itd5 = ODM.ItemDef(OID="ODM.IT.Common.SubjectID", + Name="Subject ID", DataType="text") itd5.Description = desc5 itd5.Question = q5 # ItemDef 6 - ttd6 = ODM.TranslatedText(_content="Diastolic Blood Pressure Result", lang="en") + ttd6 = ODM.TranslatedText( + _content="Diastolic Blood Pressure Result", lang="en") ttq6 = ODM.TranslatedText(_content="Diastolic BP", lang="en") desc6 = ODM.Description() desc6.TranslatedText = [ttd6] q6 = ODM.Question() q6.TranslatedText = [ttq6] - itd6 = ODM.ItemDef(OID="ODM.IT.VS.BP.DIABP.VSORRES", Name="DBP Result", DataType="text") + itd6 = ODM.ItemDef(OID="ODM.IT.VS.BP.DIABP.VSORRES", + Name="DBP Result", DataType="text") itd6.Description = desc6 itd6.Question = q6 # ItemDef 7 @@ -280,7 +324,8 @@ def add_ITD(self): desc7.TranslatedText = [ttd7] q7 = ODM.Question() q7.TranslatedText = [ttq7] - itd7 = ODM.ItemDef(OID="ODM.IT.DM.BRTHYR", Name="Birth Year", DataType="text") + itd7 = ODM.ItemDef(OID="ODM.IT.DM.BRTHYR", + Name="Birth Year", DataType="text") itd7.Description = desc7 itd7.Question = q7 # ItemDef 8 @@ -290,7 +335,8 @@ def add_ITD(self): desc8.TranslatedText = [ttd8] q8 = ODM.Question() q8.TranslatedText = [ttq8] - itd8 = ODM.ItemDef(OID="ODM.IT.Common.Visit", Name="Visit", DataType="text") + itd8 = ODM.ItemDef(OID="ODM.IT.Common.Visit", + Name="Visit", DataType="text") itd8.Description = desc8 itd8.Question = q8 # ItemDef 9 @@ -304,18 +350,19 @@ def add_ITD(self): itd9.Description = desc9 itd9.Question = q9 # ItemDef 10 - ttd10 = ODM.TranslatedText(_content="Systolic Blood Pressure Result", lang="en") + ttd10 = ODM.TranslatedText( + _content="Systolic Blood Pressure Result", lang="en") ttq10 = ODM.TranslatedText(_content="Systolic BP", lang="en") desc10 = ODM.Description() desc10.TranslatedText = [ttd10] q10 = ODM.Question() q10.TranslatedText = [ttq10] - itd10 = ODM.ItemDef(OID="ODM.IT.VS.BP.SYSBP.VSORRES", Name="Systolic BP Result", DataType="text") + itd10 = ODM.ItemDef(OID="ODM.IT.VS.BP.SYSBP.VSORRES", + Name="Systolic BP Result", DataType="text") itd10.Description = desc10 itd10.Question = q10 return [itd1, itd2, itd3, itd4, itd5, itd6, itd7, itd8, itd9, itd10] - def add_ITD_nonunique(self): # ItemDef 1 ttd1 = ODM.TranslatedText(_content="Date of measurements", lang="en") @@ -325,12 +372,14 @@ def add_ITD_nonunique(self): q1 = ODM.Question() q1.TranslatedText = [ttq1] a1 = ODM.Alias(Context="CDASH", Name="VSDAT") - itd1 = ODM.ItemDef(OID="ODM.IT.VS.VSDAT", Name="Date", DataType="partialDate") + itd1 = ODM.ItemDef(OID="ODM.IT.VS.VSDAT", + Name="Date", DataType="partialDate") itd1.Description = desc1 itd1.Question = q1 itd1.Alias = [a1] # ItemDef 2 - ttd2 = ODM.TranslatedText(_content="Result of the vital signs measurement as originally received or collected.", lang="en") + ttd2 = ODM.TranslatedText( + _content="Result of the vital signs measurement as originally received or collected.", lang="en") ttq2 = ODM.TranslatedText(_content="Diastolic", lang="en") desc2 = ODM.Description() desc2.TranslatedText = [ttd2] @@ -338,7 +387,8 @@ def add_ITD_nonunique(self): q2.TranslatedText = [ttq2] a2a = ODM.Alias(Context="CDASH", Name="BP.DIABP.VSORRES") a2b = ODM.Alias(Context="CDASH/SDTM", Name="VSORRES+VSORRESU") - itd2 = ODM.ItemDef(OID="ODM.IT.VS.BP.VSORRESU", Name="BP Units", DataType="text") + itd2 = ODM.ItemDef(OID="ODM.IT.VS.BP.VSORRESU", + Name="BP Units", DataType="text") itd2.Description = desc2 itd2.Question = q2 itd2.Alias = [a2a, a2b] @@ -349,7 +399,8 @@ def add_ITD_nonunique(self): desc3.TranslatedText = [ttd3] q3 = ODM.Question() q3.TranslatedText = [ttq3] - itd3 = ODM.ItemDef(OID="ODM.IT.AE.AETERM", Name="AE Term", DataType="text") + itd3 = ODM.ItemDef(OID="ODM.IT.AE.AETERM", + Name="AE Term", DataType="text") itd3.Description = desc3 itd3.Question = q3 # ItemDef 4 @@ -359,7 +410,8 @@ def add_ITD_nonunique(self): desc4.TranslatedText = [ttd4] q4 = ODM.Question() q4.TranslatedText = [ttq4] - itd4 = ODM.ItemDef(OID="ODM.IT.AE.AESEV", Name="AE Severity", DataType="text") + itd4 = ODM.ItemDef(OID="ODM.IT.AE.AESEV", + Name="AE Severity", DataType="text") itd4.Description = desc4 itd4.Question = q4 # ItemDef 5 @@ -369,17 +421,20 @@ def add_ITD_nonunique(self): desc5.TranslatedText = [ttd5] q5 = ODM.Question() q5.TranslatedText = [ttq5] - itd5 = ODM.ItemDef(OID="ODM.IT.Common.SubjectID", Name="Subject ID", DataType="text") + itd5 = ODM.ItemDef(OID="ODM.IT.Common.SubjectID", + Name="Subject ID", DataType="text") itd5.Description = desc5 itd5.Question = q5 # ItemDef 6 - ttd6 = ODM.TranslatedText(_content="Diastolic Blood Pressure Result", lang="en") + ttd6 = ODM.TranslatedText( + _content="Diastolic Blood Pressure Result", lang="en") ttq6 = ODM.TranslatedText(_content="Diastolic BP", lang="en") desc6 = ODM.Description() desc6.TranslatedText = [ttd6] q6 = ODM.Question() q6.TranslatedText = [ttq6] - itd6 = ODM.ItemDef(OID="ODM.IT.VS.BP.SYSBP.VSORRES", Name="DBP Result", DataType="text") + itd6 = ODM.ItemDef(OID="ODM.IT.VS.BP.SYSBP.VSORRES", + Name="DBP Result", DataType="text") itd6.Description = desc6 itd6.Question = q6 # ItemDef 7 @@ -389,7 +444,8 @@ def add_ITD_nonunique(self): desc7.TranslatedText = [ttd7] q7 = ODM.Question() q7.TranslatedText = [ttq7] - itd7 = ODM.ItemDef(OID="ODM.IT.DM.BRTHYR", Name="Birth Year", DataType="text") + itd7 = ODM.ItemDef(OID="ODM.IT.DM.BRTHYR", + Name="Birth Year", DataType="text") itd7.Description = desc7 itd7.Question = q7 # ItemDef 8 @@ -399,7 +455,8 @@ def add_ITD_nonunique(self): desc8.TranslatedText = [ttd8] q8 = ODM.Question() q8.TranslatedText = [ttq8] - itd8 = ODM.ItemDef(OID="ODM.IT.Common.Visit", Name="Visit", DataType="text") + itd8 = ODM.ItemDef(OID="ODM.IT.Common.Visit", + Name="Visit", DataType="text") itd8.Description = desc8 itd8.Question = q8 # ItemDef 9 @@ -413,30 +470,36 @@ def add_ITD_nonunique(self): itd9.Description = desc9 itd9.Question = q9 # ItemDef 10 - ttd10 = ODM.TranslatedText(_content="Systolic Blood Pressure Result", lang="en") + ttd10 = ODM.TranslatedText( + _content="Systolic Blood Pressure Result", lang="en") ttq10 = ODM.TranslatedText(_content="Systolic BP", lang="en") desc10 = ODM.Description() desc10.TranslatedText = [ttd10] q10 = ODM.Question() q10.TranslatedText = [ttq10] - itd10 = ODM.ItemDef(OID="ODM.IT.VS.BP.SYSBP.VSORRES", Name="Systolic BP Result", DataType="text") + itd10 = ODM.ItemDef(OID="ODM.IT.VS.BP.SYSBP.VSORRES", + Name="Systolic BP Result", DataType="text") itd10.Description = desc10 itd10.Question = q10 return [itd1, itd2, itd3, itd4, itd5, itd6, itd7, itd8, itd9, itd10] def add_CD(self): - tt1 = ODM.TranslatedText(_content="Skip the BRTHMO field when BRTHYR length NE 4", lang="en") + tt1 = ODM.TranslatedText( + _content="Skip the BRTHMO field when BRTHYR length NE 4", lang="en") desc = ODM.Description() desc.TranslatedText = [tt1] - cd = ODM.ConditionDef(OID="ODM.CD.BRTHMO", Name="Skip BRTHMO when no BRTHYR") + cd = ODM.ConditionDef(OID="ODM.CD.BRTHMO", + Name="Skip BRTHMO when no BRTHYR") cd.Description = desc return [cd] def add_MD(self): - tt1 = ODM.TranslatedText(_content="Concatenation of BRTHYR, BRTHMO, and BRTHDY in ISO 8601 format", lang="en") + tt1 = ODM.TranslatedText( + _content="Concatenation of BRTHYR, BRTHMO, and BRTHDY in ISO 8601 format", lang="en") desc = ODM.Description() desc.TranslatedText = [tt1] - md = ODM.MethodDef(OID="ODM.MT.DOB", Name="Create BRTHDTC from date ELEMENTS", Type="Computation") + md = ODM.MethodDef( + OID="ODM.MT.DOB", Name="Create BRTHDTC from date ELEMENTS", Type="Computation") md.Description = desc return [md] @@ -451,8 +514,7 @@ def add_CL(self): dc2.TranslatedText = [tt2] cli2 = ODM.CodeListItem(CodedValue="Y") cli2.Decode = dc2 - cl = ODM.CodeList(OID="ODM.CL.NY_SUB_Y_N", Name="No Yes Response", DataType="text") + cl = ODM.CodeList(OID="ODM.CL.NY_SUB_Y_N", + Name="No Yes Response", DataType="text") cl.CodeListItem = [cli1, cli2] return [cl] - - diff --git a/tests/test_create_dataset.py b/tests/test_create_dataset.py index dbc6e34..c45d9b8 100644 --- a/tests/test_create_dataset.py +++ b/tests/test_create_dataset.py @@ -9,32 +9,42 @@ ODM_XML_FILE = "ae_test.xml" ODM_JSON_FILE = "ae_test.json" + class TestCreateDataset(unittest.TestCase): def setUp(self) -> None: - current_datetime = datetime.datetime.utcnow().replace(tzinfo=datetime.timezone.utc).isoformat() + current_datetime = datetime.datetime.utcnow().replace( + tzinfo=datetime.timezone.utc).isoformat() self.root = ODM.ODM(FileOID="ODM.DATASET.001", AsOfDateTime=current_datetime, DatasetXMLVersion="1.0.0", - CreationDateTime=current_datetime, ODMVersion="1.3.2", FileType="Snapshot", - Originator="swhume", SourceSystem="odmlib", SourceSystemVersion="0.1") + CreationDateTime=current_datetime, ODMVersion="1.3.2", FileType="Snapshot", + Originator="swhume", SourceSystem="odmlib", SourceSystemVersion="0.1") - self.root.ClinicalData = ODM.ClinicalData(StudyOID="cdisc.odmlib.001", MetaDataVersionOID="MDV.001") + self.root.ClinicalData = ODM.ClinicalData( + StudyOID="cdisc.odmlib.001", MetaDataVersionOID="MDV.001") - self.root.ClinicalData.ItemGroupData.append(ODM.ItemGroupData(ItemGroupOID="IG.AE", ItemGroupDataSeq="1")) + self.root.ClinicalData.ItemGroupData.append( + ODM.ItemGroupData(ItemGroupOID="IG.AE", ItemGroupDataSeq="1")) self._generate_igd_rows_1() - self.root.ClinicalData.ItemGroupData.append(ODM.ItemGroupData(ItemGroupOID="IG.AE", ItemGroupDataSeq="2")) + self.root.ClinicalData.ItemGroupData.append( + ODM.ItemGroupData(ItemGroupOID="IG.AE", ItemGroupDataSeq="2")) self._generate_igd_rows_2() - def test_write_dataset_xml(self): self.root.write_xml(get_data_file_path(ODM_XML_FILE)) - loader = OL.XMLODMLoader(model_package="dataset_1_0_1", ns_uri="http://www.cdisc.org/ns/Dataset-XML/v1.0") - NS.NamespaceRegistry(prefix="odm", uri="http://www.cdisc.org/ns/odm/v1.3", is_default=True) - ns = NS.NamespaceRegistry(prefix="data", uri="http://www.cdisc.org/ns/Dataset-XML/v1.0") + loader = OL.XMLODMLoader( + model_package="dataset_1_0_1", ns_uri="http://www.cdisc.org/ns/Dataset-XML/v1.0") + NS.NamespaceRegistry( + prefix="odm", uri="http://www.cdisc.org/ns/odm/v1.3", is_default=True) + ns = NS.NamespaceRegistry( + prefix="data", uri="http://www.cdisc.org/ns/Dataset-XML/v1.0") loader.create_document(get_data_file_path(ODM_XML_FILE), ns) odm = loader.load_odm() self.assertEqual(odm.FileOID, "ODM.DATASET.001") - self.assertEqual(odm.ClinicalData.ItemGroupData[0].ItemGroupOID, "IG.AE") - self.assertEqual(odm.ClinicalData.ItemGroupData[0].ItemData[2].Value, "CDISC01.100008") - self.assertEqual(odm.ClinicalData.ItemGroupData[1].ItemData[4].Value, "ANXIETY") + self.assertEqual( + odm.ClinicalData.ItemGroupData[0].ItemGroupOID, "IG.AE") + self.assertEqual( + odm.ClinicalData.ItemGroupData[0].ItemData[2].Value, "CDISC01.100008") + self.assertEqual( + odm.ClinicalData.ItemGroupData[1].ItemData[4].Value, "ANXIETY") def test_write_dataset_json(self): self.root.write_json(get_data_file_path(ODM_JSON_FILE)) @@ -42,35 +52,60 @@ def test_write_dataset_json(self): loader.create_document(get_data_file_path(ODM_JSON_FILE)) odm = loader.load_odm() self.assertEqual(odm.FileOID, "ODM.DATASET.001") - self.assertEqual(odm.ClinicalData.ItemGroupData[0].ItemGroupOID, "IG.AE") - self.assertEqual(odm.ClinicalData.ItemGroupData[0].ItemData[2].Value, "CDISC01.100008") - self.assertEqual(odm.ClinicalData.ItemGroupData[1].ItemData[4].Value, "ANXIETY") + self.assertEqual( + odm.ClinicalData.ItemGroupData[0].ItemGroupOID, "IG.AE") + self.assertEqual( + odm.ClinicalData.ItemGroupData[0].ItemData[2].Value, "CDISC01.100008") + self.assertEqual( + odm.ClinicalData.ItemGroupData[1].ItemData[4].Value, "ANXIETY") def _generate_igd_rows_1(self): - self.root.ClinicalData.ItemGroupData[0].ItemData.append(ODM.ItemData(ItemOID="IT.STUDYID", Value="CDISC01")) - self.root.ClinicalData.ItemGroupData[0].ItemData.append(ODM.ItemData(ItemOID="IT.AE.DOMAIN", Value="AE")) - self.root.ClinicalData.ItemGroupData[0].ItemData.append(ODM.ItemData(ItemOID="IT.USUBJID", Value="CDISC01.100008")) - self.root.ClinicalData.ItemGroupData[0].ItemData.append(ODM.ItemData(ItemOID="IT.AE.AESEQ", Value="1")) - self.root.ClinicalData.ItemGroupData[0].ItemData.append(ODM.ItemData(ItemOID="IT.AE.AETERM", Value="AGITATED")) - self.root.ClinicalData.ItemGroupData[0].ItemData.append(ODM.ItemData(ItemOID="IT.AE.AEMODIFY", Value="AGITATION")) - self.root.ClinicalData.ItemGroupData[0].ItemData.append(ODM.ItemData(ItemOID="IT.AE.AEDECOD", Value="Agitation")) - self.root.ClinicalData.ItemGroupData[0].ItemData.append(ODM.ItemData(ItemOID="IT.AE.AESEV", Value="MILD")) - self.root.ClinicalData.ItemGroupData[0].ItemData.append(ODM.ItemData(ItemOID="IT.AE.AESER", Value="N")) - self.root.ClinicalData.ItemGroupData[0].ItemData.append(ODM.ItemData(ItemOID="IT.AE.AEACN", Value="DOSE NOT CHANGED")) - self.root.ClinicalData.ItemGroupData[0].ItemData.append(ODM.ItemData(ItemOID="IT.AE.AEREL", Value="POSSIBLY RELATED")) + self.root.ClinicalData.ItemGroupData[0].ItemData.append( + ODM.ItemData(ItemOID="IT.STUDYID", Value="CDISC01")) + self.root.ClinicalData.ItemGroupData[0].ItemData.append( + ODM.ItemData(ItemOID="IT.AE.DOMAIN", Value="AE")) + self.root.ClinicalData.ItemGroupData[0].ItemData.append( + ODM.ItemData(ItemOID="IT.USUBJID", Value="CDISC01.100008")) + self.root.ClinicalData.ItemGroupData[0].ItemData.append( + ODM.ItemData(ItemOID="IT.AE.AESEQ", Value="1")) + self.root.ClinicalData.ItemGroupData[0].ItemData.append( + ODM.ItemData(ItemOID="IT.AE.AETERM", Value="AGITATED")) + self.root.ClinicalData.ItemGroupData[0].ItemData.append( + ODM.ItemData(ItemOID="IT.AE.AEMODIFY", Value="AGITATION")) + self.root.ClinicalData.ItemGroupData[0].ItemData.append( + ODM.ItemData(ItemOID="IT.AE.AEDECOD", Value="Agitation")) + self.root.ClinicalData.ItemGroupData[0].ItemData.append( + ODM.ItemData(ItemOID="IT.AE.AESEV", Value="MILD")) + self.root.ClinicalData.ItemGroupData[0].ItemData.append( + ODM.ItemData(ItemOID="IT.AE.AESER", Value="N")) + self.root.ClinicalData.ItemGroupData[0].ItemData.append( + ODM.ItemData(ItemOID="IT.AE.AEACN", Value="DOSE NOT CHANGED")) + self.root.ClinicalData.ItemGroupData[0].ItemData.append( + ODM.ItemData(ItemOID="IT.AE.AEREL", Value="POSSIBLY RELATED")) def _generate_igd_rows_2(self): - self.root.ClinicalData.ItemGroupData[1].ItemData.append(ODM.ItemData(ItemOID="IT.STUDYID", Value="CDISC01")) - self.root.ClinicalData.ItemGroupData[1].ItemData.append(ODM.ItemData(ItemOID="IT.AE.DOMAIN", Value="AE")) - self.root.ClinicalData.ItemGroupData[1].ItemData.append(ODM.ItemData(ItemOID="IT.USUBJID", Value="CDISC01.100008")) - self.root.ClinicalData.ItemGroupData[1].ItemData.append(ODM.ItemData(ItemOID="IT.AE.AESEQ", Value="2")) - self.root.ClinicalData.ItemGroupData[1].ItemData.append(ODM.ItemData(ItemOID="IT.AE.AETERM", Value="ANXIETY")) - self.root.ClinicalData.ItemGroupData[1].ItemData.append(ODM.ItemData(ItemOID="IT.AE.AEMODIFY", Value="AGITATION")) - self.root.ClinicalData.ItemGroupData[1].ItemData.append(ODM.ItemData(ItemOID="IT.AE.AEDECOD", Value="Anxiety")) - self.root.ClinicalData.ItemGroupData[1].ItemData.append(ODM.ItemData(ItemOID="IT.AE.AESEV", Value="MODERATE")) - self.root.ClinicalData.ItemGroupData[1].ItemData.append(ODM.ItemData(ItemOID="IT.AE.AESER", Value="N")) - self.root.ClinicalData.ItemGroupData[1].ItemData.append(ODM.ItemData(ItemOID="IT.AE.AEACN", Value="DOSE NOT CHANGED")) - self.root.ClinicalData.ItemGroupData[1].ItemData.append(ODM.ItemData(ItemOID="IT.AE.AEREL", Value="POSSIBLY RELATED")) + self.root.ClinicalData.ItemGroupData[1].ItemData.append( + ODM.ItemData(ItemOID="IT.STUDYID", Value="CDISC01")) + self.root.ClinicalData.ItemGroupData[1].ItemData.append( + ODM.ItemData(ItemOID="IT.AE.DOMAIN", Value="AE")) + self.root.ClinicalData.ItemGroupData[1].ItemData.append( + ODM.ItemData(ItemOID="IT.USUBJID", Value="CDISC01.100008")) + self.root.ClinicalData.ItemGroupData[1].ItemData.append( + ODM.ItemData(ItemOID="IT.AE.AESEQ", Value="2")) + self.root.ClinicalData.ItemGroupData[1].ItemData.append( + ODM.ItemData(ItemOID="IT.AE.AETERM", Value="ANXIETY")) + self.root.ClinicalData.ItemGroupData[1].ItemData.append( + ODM.ItemData(ItemOID="IT.AE.AEMODIFY", Value="AGITATION")) + self.root.ClinicalData.ItemGroupData[1].ItemData.append( + ODM.ItemData(ItemOID="IT.AE.AEDECOD", Value="Anxiety")) + self.root.ClinicalData.ItemGroupData[1].ItemData.append( + ODM.ItemData(ItemOID="IT.AE.AESEV", Value="MODERATE")) + self.root.ClinicalData.ItemGroupData[1].ItemData.append( + ODM.ItemData(ItemOID="IT.AE.AESER", Value="N")) + self.root.ClinicalData.ItemGroupData[1].ItemData.append( + ODM.ItemData(ItemOID="IT.AE.AEACN", Value="DOSE NOT CHANGED")) + self.root.ClinicalData.ItemGroupData[1].ItemData.append( + ODM.ItemData(ItemOID="IT.AE.AEREL", Value="POSSIBLY RELATED")) if __name__ == '__main__': diff --git a/tests/test_create_odm.py b/tests/test_create_odm.py index 7b80c2b..c463a37 100644 --- a/tests/test_create_odm.py +++ b/tests/test_create_odm.py @@ -11,7 +11,9 @@ ODM_XML_FILE = get_data_file_path("simple_create.xml", check_exists=False) ODM_JSON_FILE = get_data_file_path("simple_create.json", check_exists=False) -ODM_SIMPLE_STR_FILE = get_data_file_path("simple_create_from_string.xml", check_exists=False) +ODM_SIMPLE_STR_FILE = get_data_file_path( + "simple_create_from_string.xml", check_exists=False) + class MyTestCase(unittest.TestCase): def setUp(self) -> None: @@ -166,8 +168,8 @@ def tearDown(self) -> None: Clean up the test files. """ for temp_file in [ODM_XML_FILE, - ODM_JSON_FILE, - ODM_SIMPLE_STR_FILE]: + ODM_JSON_FILE, + ODM_SIMPLE_STR_FILE]: if os.path.exists(temp_file): os.remove(temp_file) @@ -192,12 +194,12 @@ def test_read_odm_json(self): # tests the __len__ in ItemGroupDef self.assertEqual(len(igd_list), 1) - def test_xml_to_string(self): loader = LD.ODMLoader(OL.XMLODMLoader( model_package="odm_1_3_2", ns_uri="http://www.cdisc.org/ns/odm/v1.3")) - loader.open_odm_document(get_data_file_path("simple_create_from_string.xml")) + loader.open_odm_document(get_data_file_path( + "simple_create_from_string.xml")) mdv = loader.MetaDataVersion() item_list = mdv.ItemDef item = item_list[0] diff --git a/tests/test_define21_parser_metadata.py b/tests/test_define21_parser_metadata.py index 933c18b..392e30c 100644 --- a/tests/test_define21_parser_metadata.py +++ b/tests/test_define21_parser_metadata.py @@ -12,11 +12,15 @@ class TestDefine21LoaderMetaData(unittest.TestCase): def setUp(self) -> None: self.odm_file_1 = get_data_file_path('defineV21-SDTM-metadata.xml') - NS.NamespaceRegistry(prefix="odm", uri="http://www.cdisc.org/ns/odm/v1.3", is_default=True) - NS.NamespaceRegistry(prefix="def", uri="http://www.cdisc.org/ns/def/v2.1") - self.nsr = NS.NamespaceRegistry(prefix="nciodm", uri="http://ncicb.nci.nih.gov/xml/odm/EVS/CDISC") + NS.NamespaceRegistry( + prefix="odm", uri="http://www.cdisc.org/ns/odm/v1.3", is_default=True) + NS.NamespaceRegistry( + prefix="def", uri="http://www.cdisc.org/ns/def/v2.1") + self.nsr = NS.NamespaceRegistry( + prefix="nciodm", uri="http://ncicb.nci.nih.gov/xml/odm/EVS/CDISC") self.parser = P.ODMParser(self.odm_file_1, self.nsr) - self.loader = LD.ODMLoader(OL.XMLDefineLoader(model_package="define_2_1", ns_uri="http://www.cdisc.org/ns/def/v2.1")) + self.loader = LD.ODMLoader(OL.XMLDefineLoader( + model_package="define_2_1", ns_uri="http://www.cdisc.org/ns/def/v2.1")) self.root = self.loader.create_document(self.odm_file_1) self.odm = self.loader.load_odm() self.parser.parse() @@ -27,7 +31,8 @@ def test_MetaDataVersion(self): # elementree does not support using the suffix for accessing attributes not in the default ns mdv_dict = {"OID": "MDV.CDISC01_1.1.SDTMIG.3.1.2.SDTM.1.2_X", "Name": "Study CDISC01_1, Data Definitions V-1", - "Description": "Data Definitions for CDISC01-01 SDTM datasets. This metadata version contains only a subset of datasets compared to the prior version.", + "Description": "Data Definitions for CDISC01-01 SDTM datasets. This metadata " \ + "version contains only a subset of datasets compared to the prior version.", "{http://www.cdisc.org/ns/def/v2.1}DefineVersion": "2.1.0"} self.assertDictEqual(self.mdv[0].attrib, mdv_dict) self.assertEqual(mdv_dict["{http://www.cdisc.org/ns/def/v2.1}DefineVersion"], diff --git a/tests/test_define_parser_metadata.py b/tests/test_define_parser_metadata.py index 6ad6969..e5d7db6 100644 --- a/tests/test_define_parser_metadata.py +++ b/tests/test_define_parser_metadata.py @@ -10,9 +10,12 @@ class TestOdmParserMetaData(unittest.TestCase): def setUp(self) -> None: self.odm_file_1 = get_data_file_path('define2-0-0-sdtm-test.xml') - NS.NamespaceRegistry(prefix="odm", uri="http://www.cdisc.org/ns/odm/v1.3", is_default=True) - NS.NamespaceRegistry(prefix="def", uri="http://www.cdisc.org/ns/def/v2.0") - self.nsr = NS.NamespaceRegistry(prefix="nciodm", uri="http://ncicb.nci.nih.gov/xml/odm/EVS/CDISC") + NS.NamespaceRegistry( + prefix="odm", uri="http://www.cdisc.org/ns/odm/v1.3", is_default=True) + NS.NamespaceRegistry( + prefix="def", uri="http://www.cdisc.org/ns/def/v2.0") + self.nsr = NS.NamespaceRegistry( + prefix="nciodm", uri="http://ncicb.nci.nih.gov/xml/odm/EVS/CDISC") self.parser = P.ODMParser(self.odm_file_1, self.nsr) self.root = self.parser.parse() self.mdv = self.parser.MetaDataVersion() @@ -40,7 +43,8 @@ def test_ItemGroupDef(self): "{http://www.cdisc.org/ns/def/v2.0}CommentOID": "COM.DOMAIN.DM", "{http://www.cdisc.org/ns/def/v2.0}ArchiveLocationID": "LF.DM"} self.assertDictEqual(dm_itg, ig[5]['elem'].attrib) - self.assertEqual(ig[4]["{http://www.cdisc.org/ns/def/v2.0}ArchiveLocationID"], "LF.TV") + self.assertEqual( + ig[4]["{http://www.cdisc.org/ns/def/v2.0}ArchiveLocationID"], "LF.TV") def test_ItemGroupDef_nsr(self): ig = self.parser.ItemGroupDef(parent=self.mdv[0]) @@ -52,7 +56,8 @@ def test_ItemGroupDef_nsr(self): self.nsr.get_ns_attribute_name("CommentOID", "def"): "COM.DOMAIN.DM", self.nsr.get_ns_attribute_name("ArchiveLocationID", "def"): "LF.DM"} self.assertDictEqual(dm_itg, ig[5]['elem'].attrib) - self.assertEqual(ig[4][self.nsr.get_ns_attribute_name("ArchiveLocationID", "def")], "LF.TV") + self.assertEqual(ig[4][self.nsr.get_ns_attribute_name( + "ArchiveLocationID", "def")], "LF.TV") def test_ItemRef(self): ig = self.parser.ItemGroupDef(parent=self.mdv[0]) @@ -69,7 +74,8 @@ def test_ValueListDef(self): # now back to retrieving an element from the Define-XML doc that exists in the ODM namespace ir_list = self.parser.ItemRef(parent=vld[0]['elem']) self.assertEqual(len(ir_list), 2) - wc = self.parser.WhereClauseRef(parent=ir_list[0]['elem'], ns_prefix="def") + wc = self.parser.WhereClauseRef( + parent=ir_list[0]['elem'], ns_prefix="def") self.assertEqual(wc[0]["WhereClauseOID"], "WC.DA.DATESTCD.DISPAMT") diff --git a/tests/test_descriptor.py b/tests/test_descriptor.py index 9670675..24fa19a 100644 --- a/tests/test_descriptor.py +++ b/tests/test_descriptor.py @@ -20,17 +20,20 @@ def test_assignment(self): self.assertEqual(TextTest.Name, "VariableOne") def test_get_missing_attribute(self): - igd = ODM.ItemGroupDef(OID="IG.VS", Name="Vital Signs", Repeating="Yes") + igd = ODM.ItemGroupDef( + OID="IG.VS", Name="Vital Signs", Repeating="Yes") self.assertEqual(igd.OID, "IG.VS") self.assertIsNone(igd.Comment) def test_get_missing_element_with_required(self): - itd = ODM.ItemDef(OID="IT.VS.VSORRES", Name="Vital Signs Results", DataType="text") + itd = ODM.ItemDef(OID="IT.VS.VSORRES", + Name="Vital Signs Results", DataType="text") self.assertEqual(itd.OID, "IT.VS.VSORRES") self.assertIsNone(itd.CodeListRef) def test_get_missing_undefined_attribute(self): - itd = ODM.ItemDef(OID="IT.VS.VSORRES", Name="Vital Signs Results", DataType="text") + itd = ODM.ItemDef(OID="IT.VS.VSORRES", + Name="Vital Signs Results", DataType="text") self.assertEqual(itd.OID, "IT.VS.VSORRES") self.assertListEqual(itd.MeasurementUnitRef, []) result = itd.CodeListRef @@ -39,4 +42,3 @@ def test_get_missing_undefined_attribute(self): self.assertListEqual(itd.Alias, [None]) with self.assertRaises(TypeError): itd.new_thing = "hello" - diff --git a/tests/test_enumeratedItem.py b/tests/test_enumeratedItem.py index 029385c..ddab323 100644 --- a/tests/test_enumeratedItem.py +++ b/tests/test_enumeratedItem.py @@ -9,7 +9,8 @@ def setUp(self) -> None: self.eni = ODM.EnumeratedItem(**attrs) def test_add_alias(self): - self.eni.Alias.append(ODM.Alias(Context="nci:ExtCodeID", Name="C64848")) + self.eni.Alias.append( + ODM.Alias(Context="nci:ExtCodeID", Name="C64848")) self.assertEqual(self.eni.Alias[0].Context, "nci:ExtCodeID") self.assertEqual(self.eni.Alias[0].Name, "C64848") diff --git a/tests/test_extended_alias.py b/tests/test_extended_alias.py index c682925..f2bd6c4 100644 --- a/tests/test_extended_alias.py +++ b/tests/test_extended_alias.py @@ -9,7 +9,8 @@ def test_valid_standard_attribute(self): def test_invalid_standard_attribute(self): with self.assertRaises(TypeError): - ODM.Alias = ODM.Alias(Context="CDASH", Name="AEYN", Standard="HL7 FHIR") + ODM.Alias = ODM.Alias( + Context="CDASH", Name="AEYN", Standard="HL7 FHIR") if __name__ == '__main__': diff --git a/tests/test_formDef.py b/tests/test_formDef.py index 499a41c..5dd7e27 100644 --- a/tests/test_formDef.py +++ b/tests/test_formDef.py @@ -9,33 +9,46 @@ def setUp(self) -> None: self.formdef = ODM.FormDef(**attrs) def test_add_description(self): - tt1 = ODM.TranslatedText(_content="this is the first test description", lang="en") - tt2 = ODM.TranslatedText(_content="this is the second test description", lang="en") + tt1 = ODM.TranslatedText( + _content="this is the first test description", lang="en") + tt2 = ODM.TranslatedText( + _content="this is the second test description", lang="en") self.formdef.Description = ODM.Description() self.formdef.Description.TranslatedText = [tt1, tt2] self.assertEqual(len(self.formdef.Description.TranslatedText), 2) - self.assertEqual(self.formdef.Description.TranslatedText[1]._content, 'this is the second test description') + self.assertEqual( + self.formdef.Description.TranslatedText[1]._content, 'this is the second test description') def test_add_item_group_ref(self): - igr = ODM.ItemGroupRef(ItemGroupOID="ODM.IG.COMMON", Mandatory="Yes", OrderNumber=1) + igr = ODM.ItemGroupRef(ItemGroupOID="ODM.IG.COMMON", + Mandatory="Yes", OrderNumber=1) self.formdef.ItemGroupRef.append(igr) - igr = ODM.ItemGroupRef(ItemGroupOID="ODM.IG.VS_GENERAL", Mandatory="Yes", OrderNumber=2) + igr = ODM.ItemGroupRef( + ItemGroupOID="ODM.IG.VS_GENERAL", Mandatory="Yes", OrderNumber=2) self.formdef.ItemGroupRef.append(igr) - igr = ODM.ItemGroupRef(ItemGroupOID="ODM.IG.VS", Mandatory="Yes", OrderNumber=3) + igr = ODM.ItemGroupRef(ItemGroupOID="ODM.IG.VS", + Mandatory="Yes", OrderNumber=3) self.formdef.ItemGroupRef.append(igr) - self.assertEqual(self.formdef.ItemGroupRef[0].ItemGroupOID, "ODM.IG.COMMON") + self.assertEqual( + self.formdef.ItemGroupRef[0].ItemGroupOID, "ODM.IG.COMMON") self.assertEqual(self.formdef.ItemGroupRef[2].OrderNumber, 3) def test_append_item_group_ref(self): - fd = ODM.FormDef(OID="ODM.F.VS", Name="Vital Signs Form", Repeating="Yes") + fd = ODM.FormDef( + OID="ODM.F.VS", Name="Vital Signs Form", Repeating="Yes") fd.Description = ODM.Description() - fd.Description.TranslatedText.append(ODM.TranslatedText(_content="this is the first test description", lang="en")) - fd.ItemGroupRef.append(ODM.ItemGroupRef(ItemGroupOID="ODM.IG.COMMON", Mandatory="Yes", OrderNumber=1)) - fd.ItemGroupRef.append(ODM.ItemGroupRef(ItemGroupOID="ODM.IG.VS_GENERAL", Mandatory="Yes", OrderNumber=2)) - fd.ItemGroupRef.append(ODM.ItemGroupRef(ItemGroupOID="ODM.IG.VS", Mandatory="Yes", OrderNumber=3)) + fd.Description.TranslatedText.append(ODM.TranslatedText( + _content="this is the first test description", lang="en")) + fd.ItemGroupRef.append(ODM.ItemGroupRef( + ItemGroupOID="ODM.IG.COMMON", Mandatory="Yes", OrderNumber=1)) + fd.ItemGroupRef.append(ODM.ItemGroupRef( + ItemGroupOID="ODM.IG.VS_GENERAL", Mandatory="Yes", OrderNumber=2)) + fd.ItemGroupRef.append(ODM.ItemGroupRef( + ItemGroupOID="ODM.IG.VS", Mandatory="Yes", OrderNumber=3)) self.assertEqual(fd.ItemGroupRef[0].ItemGroupOID, "ODM.IG.COMMON") self.assertEqual(fd.ItemGroupRef[2].OrderNumber, 3) - self.assertEqual(fd.Description.TranslatedText[0]._content, "this is the first test description") + self.assertEqual( + fd.Description.TranslatedText[0]._content, "this is the first test description") def test_add_alias(self): self.formdef.Alias.append(ODM.Alias(Context="SDTMIG", Name="VS")) @@ -44,24 +57,30 @@ def test_add_alias(self): self.assertEqual(self.formdef.Alias[1].Context, "CDASHIG") def test_add_not_alias(self): - item = ODM.ItemDef(OID="ODM.IT.VSPOS", Name="VS Position", DataType="text") + item = ODM.ItemDef(OID="ODM.IT.VSPOS", + Name="VS Position", DataType="text") with self.assertRaises(TypeError): self.formdef.Alias = [item] self.formdef.Alias.append(ODM.Alias(Context="SDTMIG", Name="VS")) # list accepts invalid objects - self.formdef.Alias.append(ODM.ItemDef(OID="ODM.IT.VSDT", Name="VS Date", DataType="text")) + self.formdef.Alias.append(ODM.ItemDef( + OID="ODM.IT.VSDT", Name="VS Date", DataType="text")) self.assertEqual(len(self.formdef.Alias), 2) self.assertEqual(self.formdef.Alias[0].Context, "SDTMIG") def test_to_json(self): attrs = self.set_formdef_attributes() fd = ODM.FormDef(**attrs) - tt = ODM.TranslatedText(_content="this is the first test description", lang="en") + tt = ODM.TranslatedText( + _content="this is the first test description", lang="en") fd.Description = ODM.Description() fd.Description.TranslatedText = [tt] - fd.ItemGroupRef.append(ODM.ItemGroupRef(ItemGroupOID="ODM.IG.COMMON", Mandatory="Yes", OrderNumber=1)) - fd.ItemGroupRef.append(ODM.ItemGroupRef(ItemGroupOID="ODM.IG.VS_GENERAL", Mandatory="Yes", OrderNumber=2)) - fd.ItemGroupRef.append(ODM.ItemGroupRef(ItemGroupOID="ODM.IG.VS", Mandatory="Yes", OrderNumber=3)) + fd.ItemGroupRef.append(ODM.ItemGroupRef( + ItemGroupOID="ODM.IG.COMMON", Mandatory="Yes", OrderNumber=1)) + fd.ItemGroupRef.append(ODM.ItemGroupRef( + ItemGroupOID="ODM.IG.VS_GENERAL", Mandatory="Yes", OrderNumber=2)) + fd.ItemGroupRef.append(ODM.ItemGroupRef( + ItemGroupOID="ODM.IG.VS", Mandatory="Yes", OrderNumber=3)) fd.Alias.append(ODM.Alias(Context="SDTMIG", Name="VS")) fd_json = fd.to_json() fd_dict = json.loads(fd_json) @@ -73,10 +92,14 @@ def test_to_dict(self): attrs = self.set_formdef_attributes() fd = ODM.FormDef(**attrs) fd.Description = ODM.Description() - fd.Description.TranslatedText.append(ODM.TranslatedText(_content="this is the first test description", lang="en")) - fd.ItemGroupRef.append(ODM.ItemGroupRef(ItemGroupOID="ODM.IG.COMMON", Mandatory="Yes", OrderNumber=1)) - fd.ItemGroupRef.append(ODM.ItemGroupRef(ItemGroupOID="ODM.IG.VS_GENERAL", Mandatory="Yes", OrderNumber=2)) - fd.ItemGroupRef.append(ODM.ItemGroupRef(ItemGroupOID="ODM.IG.VS", Mandatory="Yes", OrderNumber=3)) + fd.Description.TranslatedText.append(ODM.TranslatedText( + _content="this is the first test description", lang="en")) + fd.ItemGroupRef.append(ODM.ItemGroupRef( + ItemGroupOID="ODM.IG.COMMON", Mandatory="Yes", OrderNumber=1)) + fd.ItemGroupRef.append(ODM.ItemGroupRef( + ItemGroupOID="ODM.IG.VS_GENERAL", Mandatory="Yes", OrderNumber=2)) + fd.ItemGroupRef.append(ODM.ItemGroupRef( + ItemGroupOID="ODM.IG.VS", Mandatory="Yes", OrderNumber=3)) fd.Alias.append(ODM.Alias(Context="SDTMIG", Name="VS")) fd_dict = fd.to_dict() self.assertEqual(fd_dict["OID"], "ODM.F.VS") @@ -86,16 +109,21 @@ def test_to_xml(self): attrs = self.set_formdef_attributes() fd = ODM.FormDef(**attrs) fd.Description = ODM.Description() - fd.Description.TranslatedText.append(ODM.TranslatedText(_content="this is the first test description", lang="en")) - fd.ItemGroupRef.append(ODM.ItemGroupRef(ItemGroupOID="ODM.IG.COMMON", Mandatory="Yes", OrderNumber=1)) - fd.ItemGroupRef.append(ODM.ItemGroupRef(ItemGroupOID="ODM.IG.VS_GENERAL", Mandatory="Yes", OrderNumber=2)) - fd.ItemGroupRef.append(ODM.ItemGroupRef(ItemGroupOID="ODM.IG.VS", Mandatory="Yes", OrderNumber=3)) + fd.Description.TranslatedText.append(ODM.TranslatedText( + _content="this is the first test description", lang="en")) + fd.ItemGroupRef.append(ODM.ItemGroupRef( + ItemGroupOID="ODM.IG.COMMON", Mandatory="Yes", OrderNumber=1)) + fd.ItemGroupRef.append(ODM.ItemGroupRef( + ItemGroupOID="ODM.IG.VS_GENERAL", Mandatory="Yes", OrderNumber=2)) + fd.ItemGroupRef.append(ODM.ItemGroupRef( + ItemGroupOID="ODM.IG.VS", Mandatory="Yes", OrderNumber=3)) fd.Alias.append(ODM.Alias(Context="SDTMIG", Name="VS")) fd_xml = fd.to_xml() self.assertEqual(fd_xml.attrib["OID"], "ODM.F.VS") igr = fd_xml.findall("ItemGroupRef") self.assertEqual(len(igr), 3) - self.assertEqual(igr[0].attrib, {"ItemGroupOID": "ODM.IG.COMMON", "Mandatory": "Yes", "OrderNumber": "1"}) + self.assertEqual(igr[0].attrib, { + "ItemGroupOID": "ODM.IG.COMMON", "Mandatory": "Yes", "OrderNumber": "1"}) @staticmethod def set_formdef_attributes(): @@ -109,7 +137,8 @@ def set_formdef_attributes(): def expected_dict(): return {'OID': 'ODM.F.VS', 'Name': 'Vital Signs Form', 'Repeating': 'Yes', 'ItemGroupRef': [{'ItemGroupOID': 'ODM.IG.COMMON', 'Mandatory': 'Yes', 'OrderNumber': 1}, - {'ItemGroupOID': 'ODM.IG.VS_GENERAL', 'Mandatory': 'Yes', 'OrderNumber': 2}, + {'ItemGroupOID': 'ODM.IG.VS_GENERAL', + 'Mandatory': 'Yes', 'OrderNumber': 2}, {'ItemGroupOID': 'ODM.IG.VS', 'Mandatory': 'Yes', 'OrderNumber': 3}], 'Description': {'TranslatedText': [{'_content': 'this is the first test description', 'lang': 'en'}]}, diff --git a/tests/test_insert_item.py b/tests/test_insert_item.py index 14485c1..22953f5 100644 --- a/tests/test_insert_item.py +++ b/tests/test_insert_item.py @@ -12,7 +12,7 @@ def setUp(self): self.odm_file = get_data_file_path('cdash-odm-test.xml') self.odm_file_out = get_data_file_path('cdash-odm-test-insert.xml') self.loader = LD.ODMLoader(OL.XMLODMLoader()) - + def test_insert_item_with_none_element(self): attrs = self.set_item_attributes() item = ODM.ItemDef(**attrs) @@ -48,7 +48,8 @@ def test_insert_item_with_none_element(self): def add_study(self, mdv): study_name = ODM.StudyName(_content="ODM XML Test Study Name") protocol_name = ODM.ProtocolName(_content="ODM XML Test Study") - study_description = ODM.StudyDescription(_content="Testing the generation of an ODM XML file") + study_description = ODM.StudyDescription( + _content="Testing the generation of an ODM XML file") gv = ODM.GlobalVariables() gv.StudyName = study_name gv.StudyDescription = study_description @@ -59,7 +60,7 @@ def add_study(self, mdv): return study def set_item_attributes(self): - return {"OID": "ODM.IT.AE.TEST", "Name": "Any AEs?", "DataType": "text", "Length": 1, "SASFieldName": "AEYN", + return {"OID": "ODM.IT.AE.TEST", "Name": "Any AEs?", "DataType": "text", "Length": 1, "SASFieldName": "AEYN", "SDSVarName": "AEYN", "Origin": "CRF", "Comment": None} def get_root_attributes(self): diff --git a/tests/test_itemDef.py b/tests/test_itemDef.py index 1f966be..46f8a4b 100644 --- a/tests/test_itemDef.py +++ b/tests/test_itemDef.py @@ -9,7 +9,8 @@ def setUp(self) -> None: self.item = ODM.ItemDef(**attrs) def test_required_attributes_only(self): - attrs = {"OID": "ODM.IT.AE.AEYN", "Name": "Any AEs?", "DataType": "text"} + attrs = {"OID": "ODM.IT.AE.AEYN", + "Name": "Any AEs?", "DataType": "text"} self.item = ODM.ItemDef(**attrs) self.assertEqual(self.item.OID, "ODM.IT.AE.AEYN") @@ -18,15 +19,19 @@ def test_add_range_check(self): tt1 = ODM.TranslatedText(_content="invalid test code", lang="en") tt2 = ODM.TranslatedText(_content="code de test invalide", lang="fr") rc.CheckValue = [ODM.CheckValue(_content="DIABP")] - rc.FormalExpression = [ODM.FormalExpression(Context="Python 3.7", _content="print('hello world')")] - rc.MeasurementUnitRef = ODM.MeasurementUnitRef(MeasurementUnitOID="ODM.MU.UNITS") + rc.FormalExpression = [ODM.FormalExpression( + Context="Python 3.7", _content="print('hello world')")] + rc.MeasurementUnitRef = ODM.MeasurementUnitRef( + MeasurementUnitOID="ODM.MU.UNITS") attrs = {"Comparator": "GT", "SoftHard": "Soft"} rc1 = ODM.RangeCheck(**attrs) tt1 = ODM.TranslatedText(_content="invalid test code", lang="en") rc1.ErrorMessage.TranslatedText = [tt1] rc1.CheckValue = [ODM.CheckValue(_content="SYSBP")] - rc1.FormalExpression = [ODM.FormalExpression(Context="Python 3.7", _content="print('hello world')")] - rc1.MeasurementUnitRef = ODM.MeasurementUnitRef(MeasurementUnitOID="ODM.MU.UNITS") + rc1.FormalExpression = [ODM.FormalExpression( + Context="Python 3.7", _content="print('hello world')")] + rc1.MeasurementUnitRef = ODM.MeasurementUnitRef( + MeasurementUnitOID="ODM.MU.UNITS") self.item.RangeCheck = [rc, rc1] self.assertEqual(self.item.RangeCheck[0].Comparator, "EQ") self.assertEqual(self.item.RangeCheck[1].Comparator, "GT") @@ -36,7 +41,8 @@ def test_set_description(self): tt1 = ODM.TranslatedText(**attrs) self.item.Description.TranslatedText.append(tt1) self.assertEqual(self.item.Description.TranslatedText[0].lang, "en") - self.assertEqual(self.item.Description.TranslatedText[0]._content, "this is the first test description") + self.assertEqual( + self.item.Description.TranslatedText[0]._content, "this is the first test description") def test_set_invalid_description(self): rc = ODM.RangeCheck(Comparator="EQ", SoftHard="Soft") @@ -45,8 +51,10 @@ def test_set_invalid_description(self): rc.ErrorMessage.TranslatedText.append(tt1) rc.ErrorMessage.TranslatedText.append(tt2) rc.CheckValue = [ODM.CheckValue(_content="DIABP")] - rc.FormalExpression = [ODM.FormalExpression(Context="Python 3.7", _content="print('hello world')")] - rc.MeasurementUnitRef = [ODM.MeasurementUnitRef(MeasurementUnitOID="ODM.MU.UNITS")] + rc.FormalExpression = [ODM.FormalExpression( + Context="Python 3.7", _content="print('hello world')")] + rc.MeasurementUnitRef = [ODM.MeasurementUnitRef( + MeasurementUnitOID="ODM.MU.UNITS")] self.item.RangeCheck = [rc] # Description requires a Description object, not a RangeCheck object with self.assertRaises(TypeError): @@ -59,25 +67,33 @@ def test_add_description(self): tt2 = ODM.TranslatedText(**attrs) self.item.Description.TranslatedText.append(tt1) self.item.Description.TranslatedText.append(tt2) - self.assertEqual(self.item.Description.TranslatedText[1]._content, "this is the second test description") + self.assertEqual( + self.item.Description.TranslatedText[1]._content, "this is the second test description") def test_add_question(self): tt1 = ODM.TranslatedText(_content="Any AEs?", lang="en") self.item.Question.TranslatedText.append(tt1) - self.assertEqual(self.item.Question.TranslatedText[0]._content, "Any AEs?") + self.assertEqual( + self.item.Question.TranslatedText[0]._content, "Any AEs?") def test_external_question(self): - self.item.ExternalQuestion = ODM.ExternalQuestion(Dictionary="SF36", Version="12", Code="Walks 1-mile") + self.item.ExternalQuestion = ODM.ExternalQuestion( + Dictionary="SF36", Version="12", Code="Walks 1-mile") self.assertEqual(self.item.ExternalQuestion.Dictionary, "SF36") def test_add_measurement_unit_ref(self): - self.item.MeasurementUnitRef.append(ODM.MeasurementUnitRef(MeasurementUnitOID="MU.UNITS")) - self.item.MeasurementUnitRef.append(ODM.MeasurementUnitRef(MeasurementUnitOID="MU2.UNITS2")) - self.assertEqual(self.item.MeasurementUnitRef[0].MeasurementUnitOID, "MU.UNITS") + self.item.MeasurementUnitRef.append( + ODM.MeasurementUnitRef(MeasurementUnitOID="MU.UNITS")) + self.item.MeasurementUnitRef.append( + ODM.MeasurementUnitRef(MeasurementUnitOID="MU2.UNITS2")) + self.assertEqual( + self.item.MeasurementUnitRef[0].MeasurementUnitOID, "MU.UNITS") def test_codelist_ref(self): - self.item.CodeListRef = ODM.CodeListRef(CodeListOID="CL.NY_SUB_Y_N_2011-10-24") - self.assertEqual(self.item.CodeListRef.CodeListOID, "CL.NY_SUB_Y_N_2011-10-24") + self.item.CodeListRef = ODM.CodeListRef( + CodeListOID="CL.NY_SUB_Y_N_2011-10-24") + self.assertEqual(self.item.CodeListRef.CodeListOID, + "CL.NY_SUB_Y_N_2011-10-24") def test_codelist_ref_exists_check(self): attrs = self.set_item_attributes() @@ -96,9 +112,12 @@ def test_add_alias(self): def test_to_json(self): attrs = self.set_item_attributes() item = ODM.ItemDef(**attrs) - item.Description.TranslatedText.append(ODM.TranslatedText(_content="this is the first test description", lang="en")) - item.Question.TranslatedText = [ODM.TranslatedText(_content="Any AEs?", lang="en")] - item.CodeListRef = ODM.CodeListRef(CodeListOID="CL.NY_SUB_Y_N_2011-10-24") + item.Description.TranslatedText.append(ODM.TranslatedText( + _content="this is the first test description", lang="en")) + item.Question.TranslatedText = [ + ODM.TranslatedText(_content="Any AEs?", lang="en")] + item.CodeListRef = ODM.CodeListRef( + CodeListOID="CL.NY_SUB_Y_N_2011-10-24") item_json = item.to_json() item_dict = json.loads(item_json) self.assertEqual(item_dict["OID"], "ODM.IT.AE.AEYN") @@ -106,22 +125,27 @@ def test_to_json(self): def test_to_dict(self): attrs = self.set_item_attributes() item = ODM.ItemDef(**attrs) - item.Description.TranslatedText = [ODM.TranslatedText(_content="this is the first test description", lang="en")] - item.Question.TranslatedText = [ODM.TranslatedText(_content="Any AEs?", lang="en")] - item.CodeListRef = ODM.CodeListRef(CodeListOID="CL.NY_SUB_Y_N_2011-10-24") + item.Description.TranslatedText = [ODM.TranslatedText( + _content="this is the first test description", lang="en")] + item.Question.TranslatedText = [ + ODM.TranslatedText(_content="Any AEs?", lang="en")] + item.CodeListRef = ODM.CodeListRef( + CodeListOID="CL.NY_SUB_Y_N_2011-10-24") item_dict = item.to_dict() expected_dict = {'OID': 'ODM.IT.AE.AEYN', 'Name': 'Any AEs?', 'DataType': 'text', 'Length': 1, 'SASFieldName': 'AEYN', 'SDSVarName': 'AEYN', 'Origin': 'CRF', 'Comment': 'Data management field', 'Description': {'TranslatedText': [{'_content': 'this is the first test description', - 'lang': 'en'}]}, + 'lang': 'en'}]}, 'Question': {'TranslatedText': [{'_content': 'Any AEs?', 'lang': 'en'}]}, 'CodeListRef': {'CodeListOID': 'CL.NY_SUB_Y_N_2011-10-24'}} self.assertDictEqual(item_dict, expected_dict) def test_to_dict_description(self): - tt1 = ODM.TranslatedText(_content="this is the first test description", lang="en") - tt2 = ODM.TranslatedText(_content="this is the second test description", lang="en") + tt1 = ODM.TranslatedText( + _content="this is the first test description", lang="en") + tt2 = ODM.TranslatedText( + _content="this is the second test description", lang="en") desc = ODM.Description() desc.TranslatedText = [tt1, tt2] desc_dict = desc.to_dict() @@ -130,8 +154,10 @@ def test_to_dict_description(self): {'_content': 'this is the second test description', 'lang': 'en'}) def test_to_xml_description(self): - tt1 = ODM.TranslatedText(_content="this is the first test description", lang="en") - tt2 = ODM.TranslatedText(_content="this is the second test description", lang="en") + tt1 = ODM.TranslatedText( + _content="this is the first test description", lang="en") + tt2 = ODM.TranslatedText( + _content="this is the second test description", lang="en") desc = ODM.Description() desc.TranslatedText = [tt1, tt2] desc_xml = desc.to_xml() @@ -141,15 +167,22 @@ def test_to_xml_description(self): def test_to_xml(self): attrs = self.set_item_attributes() item = ODM.ItemDef(**attrs) - item.Description.TranslatedText = [ODM.TranslatedText(_content="this is the first test description", lang="en")] - item.Question.TranslatedText = [ODM.TranslatedText(_content="Any AEs?", lang="en")] - item.ExternalQuestion = ODM.ExternalQuestion(Dictionary="SF36", Version="12", Code="Walks 1-mile") - item.CodeListRef = ODM.CodeListRef(CodeListOID="CL.NY_SUB_Y_N_2011-10-24") + item.Description.TranslatedText = [ODM.TranslatedText( + _content="this is the first test description", lang="en")] + item.Question.TranslatedText = [ + ODM.TranslatedText(_content="Any AEs?", lang="en")] + item.ExternalQuestion = ODM.ExternalQuestion( + Dictionary="SF36", Version="12", Code="Walks 1-mile") + item.CodeListRef = ODM.CodeListRef( + CodeListOID="CL.NY_SUB_Y_N_2011-10-24") item.RangeCheck = [ODM.RangeCheck(Comparator="EQ", SoftHard="Soft")] item.RangeCheck[0].CheckValue = [ODM.CheckValue(_content="DIABP")] - item.RangeCheck[0].FormalExpression = [ODM.FormalExpression(Context="Python 3.7", _content="print('hello world')")] - item.RangeCheck[0].MeasurementUnitRef = ODM.MeasurementUnitRef(MeasurementUnitOID="ODM.MU.UNITS") - item.RangeCheck[0].ErrorMessage.TranslatedText = [ODM.TranslatedText(_content="invalid test code", lang="en")] + item.RangeCheck[0].FormalExpression = [ODM.FormalExpression( + Context="Python 3.7", _content="print('hello world')")] + item.RangeCheck[0].MeasurementUnitRef = ODM.MeasurementUnitRef( + MeasurementUnitOID="ODM.MU.UNITS") + item.RangeCheck[0].ErrorMessage.TranslatedText = [ + ODM.TranslatedText(_content="invalid test code", lang="en")] item_xml = item.to_xml() self.assertEqual(item_xml.attrib["OID"], "ODM.IT.AE.AEYN") cv = item_xml.find("*/CheckValue") @@ -157,14 +190,14 @@ def test_to_xml(self): dt = item_xml.findall("Description/TranslatedText") self.assertEqual(len(dt), 1) - def test_missing_itemdef_attributes(self): attrs = {"OID": "ODM.IT.AE.AEYN", "Name": "Any AEs?"} with self.assertRaises(ValueError): ODM.ItemDef(**attrs) def test_invalid_attribute_data_type(self): - attrs = {"OID": "ODM.IT.AE.AEYN", "Name": "Any AEs?", "DataType": "text", "SignificantDigits": "A"} + attrs = {"OID": "ODM.IT.AE.AEYN", "Name": "Any AEs?", + "DataType": "text", "SignificantDigits": "A"} with self.assertRaises(TypeError): self.item = ODM.ItemDef(**attrs) @@ -175,4 +208,3 @@ def set_item_attributes(self): """ return {"OID": "ODM.IT.AE.AEYN", "Name": "Any AEs?", "DataType": "text", "Length": 1, "SASFieldName": "AEYN", "SDSVarName": "AEYN", "Origin": "CRF", "Comment": "Data management field"} - diff --git a/tests/test_itemDef_define.py b/tests/test_itemDef_define.py index dfb09f6..72e9de1 100644 --- a/tests/test_itemDef_define.py +++ b/tests/test_itemDef_define.py @@ -9,15 +9,18 @@ def setUp(self) -> None: self.item = DEFINE.ItemDef(**attrs) def test_required_attributes_only(self): - attrs = {"OID": "ODM.IT.AE.AEYN", "Name": "Any AEs?", "DataType": "text"} + attrs = {"OID": "ODM.IT.AE.AEYN", + "Name": "Any AEs?", "DataType": "text"} self.item = DEFINE.ItemDef(**attrs) self.assertEqual(self.item.OID, "ODM.IT.AE.AEYN") def test_add_value_list_ref(self): vlr = DEFINE.ValueListRef(ValueListOID="VL.DA.DAORRES") - attrs = {"OID": "IT.DA.DAORRES", "Name": "DAORRES", "DataType": "text", "Length": "2", "SASFieldName": "DAORRES"} + attrs = {"OID": "IT.DA.DAORRES", "Name": "DAORRES", + "DataType": "text", "Length": "2", "SASFieldName": "DAORRES"} itd = DEFINE.ItemDef(**attrs) - tt1 = DEFINE.TranslatedText(_content="Assessment Result in Original Units", lang="en") + tt1 = DEFINE.TranslatedText( + _content="Assessment Result in Original Units", lang="en") desc = DEFINE.Description() desc.TranslatedText.append(tt1) itd.Description = desc @@ -30,10 +33,12 @@ def test_set_description(self): tt1 = DEFINE.TranslatedText(**attrs) self.item.Description.TranslatedText.append(tt1) self.assertEqual(self.item.Description.TranslatedText[0].lang, "en") - self.assertEqual(self.item.Description.TranslatedText[0]._content, "Assessment Result in Original Units") + self.assertEqual( + self.item.Description.TranslatedText[0]._content, "Assessment Result in Original Units") def test_set_invalid_description(self): - rc = DEFINE.RangeCheck(Comparator="EQ", SoftHard="Soft", ItemOID="IT.DA.DAORRES") + rc = DEFINE.RangeCheck( + Comparator="EQ", SoftHard="Soft", ItemOID="IT.DA.DAORRES") rc.CheckValue = [DEFINE.CheckValue(_content="DIABP")] self.item.RangeCheck = [rc] # Description requires a Description object, not a RangeCheck object @@ -47,11 +52,14 @@ def test_add_description(self): tt2 = DEFINE.TranslatedText(**attrs) self.item.Description.TranslatedText.append(tt1) self.item.Description.TranslatedText.append(tt2) - self.assertEqual(self.item.Description.TranslatedText[1]._content, "this is the second test description") + self.assertEqual( + self.item.Description.TranslatedText[1]._content, "this is the second test description") def test_codelist_ref(self): - self.item.CodeListRef = DEFINE.CodeListRef(CodeListOID="CL.NY_SUB_Y_N_2011-10-24") - self.assertEqual(self.item.CodeListRef.CodeListOID, "CL.NY_SUB_Y_N_2011-10-24") + self.item.CodeListRef = DEFINE.CodeListRef( + CodeListOID="CL.NY_SUB_Y_N_2011-10-24") + self.assertEqual(self.item.CodeListRef.CodeListOID, + "CL.NY_SUB_Y_N_2011-10-24") def test_origin(self): self.item.Origin = DEFINE.Origin(Type="Assigned") @@ -64,9 +72,12 @@ def test_add_alias(self): def test_to_json(self): attrs = self.set_item_attributes() item = DEFINE.ItemDef(**attrs) - item.Description.TranslatedText.append(DEFINE.TranslatedText(_content="this is the first test description", lang="en")) - item.Question.TranslatedText = [DEFINE.TranslatedText(_content="Any AEs?", lang="en")] - item.CodeListRef = DEFINE.CodeListRef(CodeListOID="CL.NY_SUB_Y_N_2011-10-24") + item.Description.TranslatedText.append(DEFINE.TranslatedText( + _content="this is the first test description", lang="en")) + item.Question.TranslatedText = [ + DEFINE.TranslatedText(_content="Any AEs?", lang="en")] + item.CodeListRef = DEFINE.CodeListRef( + CodeListOID="CL.NY_SUB_Y_N_2011-10-24") item_json = item.to_json() item_dict = json.loads(item_json) self.assertEqual(item_dict["OID"], "ODM.IT.AE.AEYN") @@ -74,21 +85,26 @@ def test_to_json(self): def test_to_dict(self): attrs = self.set_item_attributes() item = DEFINE.ItemDef(**attrs) - item.Description.TranslatedText = [DEFINE.TranslatedText(_content="this is the first test description", lang="en")] - item.Question.TranslatedText = [DEFINE.TranslatedText(_content="Any AEs?", lang="en")] - item.CodeListRef = DEFINE.CodeListRef(CodeListOID="CL.NY_SUB_Y_N_2011-10-24") + item.Description.TranslatedText = [DEFINE.TranslatedText( + _content="this is the first test description", lang="en")] + item.Question.TranslatedText = [ + DEFINE.TranslatedText(_content="Any AEs?", lang="en")] + item.CodeListRef = DEFINE.CodeListRef( + CodeListOID="CL.NY_SUB_Y_N_2011-10-24") item_dict = item.to_dict() expected_dict = {'OID': 'ODM.IT.AE.AEYN', 'Name': 'Any AEs?', 'DataType': 'text', 'Length': 1, 'SASFieldName': 'AEYN', "CommentOID": "ODM.CO.120", 'Description': {'TranslatedText': [{'_content': 'this is the first test description', - 'lang': 'en'}]}, + 'lang': 'en'}]}, 'Question': {'TranslatedText': [{'_content': 'Any AEs?', 'lang': 'en'}]}, 'CodeListRef': {'CodeListOID': 'CL.NY_SUB_Y_N_2011-10-24'}} self.assertDictEqual(item_dict, expected_dict) def test_to_dict_description(self): - tt1 = DEFINE.TranslatedText(_content="this is the first test description", lang="en") - tt2 = DEFINE.TranslatedText(_content="this is the second test description", lang="en") + tt1 = DEFINE.TranslatedText( + _content="this is the first test description", lang="en") + tt2 = DEFINE.TranslatedText( + _content="this is the second test description", lang="en") desc = DEFINE.Description() desc.TranslatedText = [tt1, tt2] desc_dict = desc.to_dict() @@ -97,8 +113,10 @@ def test_to_dict_description(self): {'_content': 'this is the second test description', 'lang': 'en'}) def test_to_xml_description(self): - tt1 = DEFINE.TranslatedText(_content="this is the first test description", lang="en") - tt2 = DEFINE.TranslatedText(_content="this is the second test description", lang="en") + tt1 = DEFINE.TranslatedText( + _content="this is the first test description", lang="en") + tt2 = DEFINE.TranslatedText( + _content="this is the second test description", lang="en") desc = DEFINE.Description() desc.TranslatedText = [tt1, tt2] desc_xml = desc.to_xml() @@ -108,9 +126,12 @@ def test_to_xml_description(self): def test_to_xml(self): attrs = self.set_item_attributes() item = DEFINE.ItemDef(**attrs) - item.Description.TranslatedText = [DEFINE.TranslatedText(_content="this is the first test description", lang="en")] - item.CodeListRef = DEFINE.CodeListRef(CodeListOID="CL.NY_SUB_Y_N_2011-10-24") - item.RangeCheck = [DEFINE.RangeCheck(Comparator="EQ", SoftHard="Soft", ItemOID="IT.DA.DAORRES")] + item.Description.TranslatedText = [DEFINE.TranslatedText( + _content="this is the first test description", lang="en")] + item.CodeListRef = DEFINE.CodeListRef( + CodeListOID="CL.NY_SUB_Y_N_2011-10-24") + item.RangeCheck = [DEFINE.RangeCheck( + Comparator="EQ", SoftHard="Soft", ItemOID="IT.DA.DAORRES")] item.RangeCheck[0].CheckValue = [DEFINE.CheckValue(_content="DIABP")] item_xml = item.to_xml() self.assertEqual(item_xml.attrib["OID"], "ODM.IT.AE.AEYN") @@ -119,14 +140,14 @@ def test_to_xml(self): dt = item_xml.findall("Description/TranslatedText") self.assertEqual(len(dt), 1) - def test_missing_itemdef_attributes(self): attrs = {"OID": "ODM.IT.AE.AEYN", "Name": "Any AEs?"} with self.assertRaises(ValueError): DEFINE.ItemDef(**attrs) def test_invalid_attribute_data_type(self): - attrs = {"OID": "ODM.IT.AE.AEYN", "Name": "Any AEs?", "DataType": "text", "SignificantDigits": "A"} + attrs = {"OID": "ODM.IT.AE.AEYN", "Name": "Any AEs?", + "DataType": "text", "SignificantDigits": "A"} with self.assertRaises(TypeError): self.item = DEFINE.ItemDef(**attrs) @@ -137,4 +158,3 @@ def set_item_attributes(self): """ return {"OID": "ODM.IT.AE.AEYN", "Name": "Any AEs?", "DataType": "text", "Length": 1, "SASFieldName": "AEYN", "CommentOID": "ODM.CO.120"} - diff --git a/tests/test_itemGroupDef.py b/tests/test_itemGroupDef.py index 258891f..e7e9a89 100644 --- a/tests/test_itemGroupDef.py +++ b/tests/test_itemGroupDef.py @@ -11,33 +11,42 @@ class TestItemGroupDef(TestCase): def setUp(self) -> None: attrs = self.set_itemgroupdef_attributes() self.igd = ODM.ItemGroupDef(**attrs) - self.odm_test_file = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'data', 'test_igd_001.xml') + self.odm_test_file = os.path.join(os.path.dirname( + os.path.realpath(__file__)), 'data', 'test_igd_001.xml') def test_item_group_valid_kwargs_only(self): igd = ODM.ItemGroupDef(OID="IG.VS", Name="VS", Repeating="Yes") self.assertEqual(igd.OID, "IG.VS") def test_add_description(self): - tt1 = ODM.TranslatedText(_content="this is the first test description", lang="en") - tt2 = ODM.TranslatedText(_content="this is the second test description", lang="en") + tt1 = ODM.TranslatedText( + _content="this is the first test description", lang="en") + tt2 = ODM.TranslatedText( + _content="this is the second test description", lang="en") self.igd.Description = ODM.Description() self.igd.Description.TranslatedText = [tt1, tt2] self.assertEqual(len(self.igd.Description.TranslatedText), 2) - self.assertEqual(self.igd.Description.TranslatedText[1]._content, 'this is the second test description') + self.assertEqual( + self.igd.Description.TranslatedText[1]._content, 'this is the second test description') def test_add_item_ref(self): - self.igd.ItemRef.append(ODM.ItemRef(ItemOID="IT.STUDYID", Mandatory="Yes", OrderNumber=1)) - self.igd.ItemRef.append(ODM.ItemRef(ItemOID="IT.VS.VSTEST", Mandatory="No", OrderNumber=2)) - self.igd.ItemRef.append(ODM.ItemRef(ItemOID="IT.VS.VSORRES", Mandatory="Yes", OrderNumber=3, MethodOID="MT.METHODFEX")) + self.igd.ItemRef.append(ODM.ItemRef( + ItemOID="IT.STUDYID", Mandatory="Yes", OrderNumber=1)) + self.igd.ItemRef.append(ODM.ItemRef( + ItemOID="IT.VS.VSTEST", Mandatory="No", OrderNumber=2)) + self.igd.ItemRef.append(ODM.ItemRef( + ItemOID="IT.VS.VSORRES", Mandatory="Yes", OrderNumber=3, MethodOID="MT.METHODFEX")) self.assertEqual(self.igd.ItemRef[0].ItemOID, "IT.STUDYID") self.assertEqual(self.igd.ItemRef[2].MethodOID, "MT.METHODFEX") def test_add_item_ref_list(self): ir1 = ODM.ItemRef(ItemOID="IT.STUDYID", Mandatory="Yes", OrderNumber=1) self.igd.ItemRef.append(ir1) - ir2 = ODM.ItemRef(ItemOID="IT.VS.VSTEST", Mandatory="No", OrderNumber=2) + ir2 = ODM.ItemRef(ItemOID="IT.VS.VSTEST", + Mandatory="No", OrderNumber=2) self.igd.ItemRef.append(ir2) - ir3 = ODM.ItemRef(ItemOID="IT.VS.VSORRES", Mandatory="Yes", OrderNumber=3, MethodOID="MT.METHODFEX") + ir3 = ODM.ItemRef(ItemOID="IT.VS.VSORRES", Mandatory="Yes", + OrderNumber=3, MethodOID="MT.METHODFEX") self.igd.ItemRef.append(ir3) self.assertEqual(self.igd.ItemRef[0].ItemOID, "IT.STUDYID") self.assertEqual(self.igd.ItemRef[2].MethodOID, "MT.METHODFEX") @@ -48,10 +57,12 @@ def test_add_item_ref_missing_kwarg(self): def test_add_item_ref_invalid_kwarg(self): with self.assertRaises(TypeError): - self.igd.ItemRef = [ODM.ItemRef(ItemOID="IT.STUDYID", Mandatory="Yes", InValid="Yes")] + self.igd.ItemRef = [ODM.ItemRef( + ItemOID="IT.STUDYID", Mandatory="Yes", InValid="Yes")] def test_item_ref_exists(self): - self.igd.ItemRef = [ODM.ItemRef(ItemOID="IT.VS.VSTESTCD", Mandatory="Yes", OrderNumber=4)] + self.igd.ItemRef = [ODM.ItemRef( + ItemOID="IT.VS.VSTESTCD", Mandatory="Yes", OrderNumber=4)] self.assertEqual(self.igd.ItemRef[0].ItemOID, "IT.VS.VSTESTCD") def test_add_alias(self): @@ -64,11 +75,13 @@ def test_add_alias(self): def test_to_json(self): attrs = self.set_itemgroupdef_attributes() igd = ODM.ItemGroupDef(**attrs) - tt = ODM.TranslatedText(_content="this is the first test description", lang="en") + tt = ODM.TranslatedText( + _content="this is the first test description", lang="en") igd.Description = ODM.Description() igd.Description.TranslatedText = [tt] ir1 = ODM.ItemRef(ItemOID="IT.STUDYID", Mandatory="Yes", OrderNumber=1) - ir2 = ODM.ItemRef(ItemOID="IT.VS.VSTEST", Mandatory="No", OrderNumber=2) + ir2 = ODM.ItemRef(ItemOID="IT.VS.VSTEST", + Mandatory="No", OrderNumber=2) igd.ItemRef = [ir1, ir2] igd_json = igd.to_json() igd_dict = json.loads(igd_json) @@ -79,16 +92,19 @@ def test_to_json(self): def test_to_xml(self): attrs = self.set_itemgroupdef_attributes() igd = ODM.ItemGroupDef(**attrs) - tt = ODM.TranslatedText(_content="this is the first test description", lang="en") + tt = ODM.TranslatedText( + _content="this is the first test description", lang="en") desc = ODM.Description() desc.TranslatedText = [tt] igd.Description = desc ir1 = ODM.ItemRef(ItemOID="IT.STUDYID", Mandatory="Yes", OrderNumber=1) - ir2 = ODM.ItemRef(ItemOID="IT.VS.VSTEST", Mandatory="No", OrderNumber=2) + ir2 = ODM.ItemRef(ItemOID="IT.VS.VSTEST", + Mandatory="No", OrderNumber=2) igd.ItemRef = [ir1, ir2] igd_xml = igd.to_xml() self.assertEqual(igd_xml.attrib["OID"], "IG.VS") - self.assertListEqual(["Description", "ItemRef", "ItemRef"], [e.tag for e in igd_xml]) + self.assertListEqual(["Description", "ItemRef", "ItemRef"], [ + e.tag for e in igd_xml]) def test_itemgroupdef_round_trip(self): """ system test to create and serialize an ItemGroupDef object """ @@ -149,7 +165,8 @@ def add_itemrefs(self, igd, item_refs): :param item_refs: list of ItemRef dictionaries containing ItemRef attributes """ for it in item_refs: - attrs = {"ItemOID": it.oid, "Mandatory": it.mandatory, "OrderNumber": it.order_number} + attrs = {"ItemOID": it.oid, "Mandatory": it.mandatory, + "OrderNumber": it.order_number} if it.key_sequence: attrs["KeySequence"] = it.key_sequence if it.method: @@ -164,29 +181,40 @@ def set_itemgroupdef_attributes(self): return {"OID": "IG.VS", "Name": "VS", "Repeating": "Yes", "Domain": "VS", "Name": "VS", "SASDatasetName": "VS", "IsReferenceData": "No", "Purpose": "Tabulation"} - def set_itemrefs(self): """ set some ItemRef element attributes using test data :return: return a list of ItemRef named tuples """ itemrefs = [] - itemrefs.append(ODM.ItemRef(ItemOID="IT.STUDYID", Mandatory="Yes", OrderNumber=1, KeySequence=1)) - itemrefs.append(ODM.ItemRef(ItemOID="IT.VS.DOMAIN", Mandatory="Yes", OrderNumber=2)) - itemrefs.append(ODM.ItemRef(ItemOID="IT.VS.USUBJID", Mandatory="Yes", OrderNumber=3, KeySequence=2, MethodOID="MT.USUBJID")) - itemrefs.append(ODM.ItemRef(ItemOID="IT.VS.VSSEQ", Mandatory="Yes", OrderNumber=4, MethodOID="MT.SEQ")) - itemrefs.append(ODM.ItemRef(ItemOID="IT.VS.VSTESTCD", Mandatory="Yes", OrderNumber=5, KeySequence=3)) - itemrefs.append(ODM.ItemRef(ItemOID="IT.VS.VSTEST", Mandatory="Yes", OrderNumber=6)) - itemrefs.append(ODM.ItemRef(ItemOID="IT.VS.VSPOS", Mandatory="No", OrderNumber=7)) - itemrefs.append(ODM.ItemRef(ItemOID="IT.VS.VSORRES", Mandatory="No", OrderNumber=8)) - itemrefs.append(ODM.ItemRef(ItemOID="IT.VS.VSORRESU", Mandatory="No", OrderNumber=9)) - itemrefs.append(ODM.ItemRef(ItemOID="IT.VS.VSSTRESC", Mandatory="No", OrderNumber=10, MethodOID="MT.VSSTRESC")) - itemrefs.append(ODM.ItemRef(ItemOID="IT.VS.VSSTRESN", Mandatory="No", OrderNumber=11, MethodOID="MT.VSSTRESN")) - itemrefs.append(ODM.ItemRef(ItemOID="IT.VS.VSSTRESU", Mandatory="No", OrderNumber=12)) - itemrefs.append(ODM.ItemRef(ItemOID="IT.VS.VSBLFL", Mandatory="No", OrderNumber=13, MethodOID="MT.VSBLFL")) + itemrefs.append(ODM.ItemRef(ItemOID="IT.STUDYID", + Mandatory="Yes", OrderNumber=1, KeySequence=1)) + itemrefs.append(ODM.ItemRef(ItemOID="IT.VS.DOMAIN", + Mandatory="Yes", OrderNumber=2)) + itemrefs.append(ODM.ItemRef(ItemOID="IT.VS.USUBJID", Mandatory="Yes", + OrderNumber=3, KeySequence=2, MethodOID="MT.USUBJID")) + itemrefs.append(ODM.ItemRef(ItemOID="IT.VS.VSSEQ", + Mandatory="Yes", OrderNumber=4, MethodOID="MT.SEQ")) + itemrefs.append(ODM.ItemRef(ItemOID="IT.VS.VSTESTCD", + Mandatory="Yes", OrderNumber=5, KeySequence=3)) + itemrefs.append(ODM.ItemRef(ItemOID="IT.VS.VSTEST", + Mandatory="Yes", OrderNumber=6)) + itemrefs.append(ODM.ItemRef(ItemOID="IT.VS.VSPOS", + Mandatory="No", OrderNumber=7)) + itemrefs.append(ODM.ItemRef(ItemOID="IT.VS.VSORRES", + Mandatory="No", OrderNumber=8)) + itemrefs.append(ODM.ItemRef(ItemOID="IT.VS.VSORRESU", + Mandatory="No", OrderNumber=9)) + itemrefs.append(ODM.ItemRef(ItemOID="IT.VS.VSSTRESC", + Mandatory="No", OrderNumber=10, MethodOID="MT.VSSTRESC")) + itemrefs.append(ODM.ItemRef(ItemOID="IT.VS.VSSTRESN", + Mandatory="No", OrderNumber=11, MethodOID="MT.VSSTRESN")) + itemrefs.append(ODM.ItemRef(ItemOID="IT.VS.VSSTRESU", + Mandatory="No", OrderNumber=12)) + itemrefs.append(ODM.ItemRef(ItemOID="IT.VS.VSBLFL", + Mandatory="No", OrderNumber=13, MethodOID="MT.VSBLFL")) return itemrefs - def create_odm_document(self, igd): """ assemble the ODM document, add the ItemGroupDef, and write it to a file @@ -202,7 +230,6 @@ def create_odm_document(self, igd): self.write_odm_file(odm_elem, self.odm_test_file) return odm_elem - def create_root(self): """ create the ODM root element object with test data @@ -214,7 +241,6 @@ def create_root(self): root = ODM.odm_root.ODMRoot("ODM.TEST.IGD.001", **root) return root - def create_study(self): """ create the ODM Study object instantiated with test data @@ -226,7 +252,6 @@ def create_study(self): study.ProtocolName = "ODM ItemGroupDef" return study - def create_mdv(self): """ create the ODM MetaDataVersion object instantiated with test data @@ -236,7 +261,6 @@ def create_mdv(self): "2.1.0") return mdv - def write_odm_file(self, odm, odm_file): """ write the ODM document to a file @@ -246,7 +270,6 @@ def write_odm_file(self, odm, odm_file): tree = ET.ElementTree(odm) tree.write(odm_file, xml_declaration=True) - def set_datetime(self): """return the current datetime in ISO 8601 format""" return datetime.datetime.utcnow().replace(tzinfo=datetime.timezone.utc).isoformat() diff --git a/tests/test_itemGroupDef_define.py b/tests/test_itemGroupDef_define.py index d2099a6..285a3ab 100644 --- a/tests/test_itemGroupDef_define.py +++ b/tests/test_itemGroupDef_define.py @@ -12,9 +12,12 @@ class TestItemGroupDef(TestCase): def setUp(self) -> None: attrs = self.set_itemgroupdef_attributes() self.igd = DEFINE.ItemGroupDef(**attrs) - self.test_file = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'data', 'define2-0-0-sdtm-test.xml') - self.nsr = NS.NamespaceRegistry(prefix="odm", uri="http://www.cdisc.org/ns/odm/v1.3", is_default=True) - self.nsr = NS.NamespaceRegistry(prefix="def", uri="http://www.cdisc.org/ns/def/v2.0") + self.test_file = os.path.join(os.path.dirname( + os.path.realpath(__file__)), 'data', 'define2-0-0-sdtm-test.xml') + self.nsr = NS.NamespaceRegistry( + prefix="odm", uri="http://www.cdisc.org/ns/odm/v1.3", is_default=True) + self.nsr = NS.NamespaceRegistry( + prefix="def", uri="http://www.cdisc.org/ns/def/v2.0") def test_item_group_valid_kwargs_only(self): attrs = self.set_itemgroupdef_attributes() @@ -22,26 +25,35 @@ def test_item_group_valid_kwargs_only(self): self.assertEqual(igd.OID, "IG.VS") def test_add_description(self): - tt1 = DEFINE.TranslatedText(_content="this is the first test description", lang="en") - tt2 = DEFINE.TranslatedText(_content="this is the second test description", lang="en") + tt1 = DEFINE.TranslatedText( + _content="this is the first test description", lang="en") + tt2 = DEFINE.TranslatedText( + _content="this is the second test description", lang="en") self.igd.Description = DEFINE.Description() self.igd.Description.TranslatedText = [tt1, tt2] self.assertEqual(len(self.igd.Description.TranslatedText), 2) - self.assertEqual(self.igd.Description.TranslatedText[1]._content, 'this is the second test description') + self.assertEqual( + self.igd.Description.TranslatedText[1]._content, 'this is the second test description') def test_add_item_ref(self): - self.igd.ItemRef.append(DEFINE.ItemRef(ItemOID="IT.STUDYID", Mandatory="Yes", OrderNumber=1)) - self.igd.ItemRef.append(DEFINE.ItemRef(ItemOID="IT.VS.VSTEST", Mandatory="No", OrderNumber=2)) - self.igd.ItemRef.append(DEFINE.ItemRef(ItemOID="IT.VS.VSORRES", Mandatory="Yes", OrderNumber=3, MethodOID="MT.METHODFEX")) + self.igd.ItemRef.append(DEFINE.ItemRef( + ItemOID="IT.STUDYID", Mandatory="Yes", OrderNumber=1)) + self.igd.ItemRef.append(DEFINE.ItemRef( + ItemOID="IT.VS.VSTEST", Mandatory="No", OrderNumber=2)) + self.igd.ItemRef.append(DEFINE.ItemRef( + ItemOID="IT.VS.VSORRES", Mandatory="Yes", OrderNumber=3, MethodOID="MT.METHODFEX")) self.assertEqual(self.igd.ItemRef[0].ItemOID, "IT.STUDYID") self.assertEqual(self.igd.ItemRef[2].MethodOID, "MT.METHODFEX") def test_add_item_ref_list(self): - ir1 = DEFINE.ItemRef(ItemOID="IT.STUDYID", Mandatory="Yes", OrderNumber=1) + ir1 = DEFINE.ItemRef(ItemOID="IT.STUDYID", + Mandatory="Yes", OrderNumber=1) self.igd.ItemRef.append(ir1) - ir2 = DEFINE.ItemRef(ItemOID="IT.VS.VSTEST", Mandatory="No", OrderNumber=2) + ir2 = DEFINE.ItemRef(ItemOID="IT.VS.VSTEST", + Mandatory="No", OrderNumber=2) self.igd.ItemRef.append(ir2) - ir3 = DEFINE.ItemRef(ItemOID="IT.VS.VSORRES", Mandatory="Yes", OrderNumber=3, MethodOID="MT.METHODFEX") + ir3 = DEFINE.ItemRef(ItemOID="IT.VS.VSORRES", Mandatory="Yes", + OrderNumber=3, MethodOID="MT.METHODFEX") self.igd.ItemRef.append(ir3) self.assertEqual(self.igd.ItemRef[0].ItemOID, "IT.STUDYID") self.assertEqual(self.igd.ItemRef[2].MethodOID, "MT.METHODFEX") @@ -52,10 +64,12 @@ def test_add_item_ref_missing_kwarg(self): def test_add_item_ref_invalid_kwarg(self): with self.assertRaises(TypeError): - self.igd.ItemRef = [DEFINE.ItemRef(ItemOID="IT.STUDYID", Mandatory="Yes", InValid="Yes")] + self.igd.ItemRef = [DEFINE.ItemRef( + ItemOID="IT.STUDYID", Mandatory="Yes", InValid="Yes")] def test_item_ref_exists(self): - self.igd.ItemRef = [DEFINE.ItemRef(ItemOID="IT.VS.VSTESTCD", Mandatory="Yes", OrderNumber=4)] + self.igd.ItemRef = [DEFINE.ItemRef( + ItemOID="IT.VS.VSTESTCD", Mandatory="Yes", OrderNumber=4)] self.assertEqual(self.igd.ItemRef[0].ItemOID, "IT.VS.VSTESTCD") def test_add_alias(self): @@ -68,11 +82,14 @@ def test_add_alias(self): def test_to_json(self): attrs = self.set_itemgroupdef_attributes() igd = DEFINE.ItemGroupDef(**attrs) - tt = DEFINE.TranslatedText(_content="this is the first test description", lang="en") + tt = DEFINE.TranslatedText( + _content="this is the first test description", lang="en") igd.Description = DEFINE.Description() igd.Description.TranslatedText = [tt] - ir1 = DEFINE.ItemRef(ItemOID="IT.STUDYID", Mandatory="Yes", OrderNumber=1) - ir2 = DEFINE.ItemRef(ItemOID="IT.VS.VSTEST", Mandatory="No", OrderNumber=2) + ir1 = DEFINE.ItemRef(ItemOID="IT.STUDYID", + Mandatory="Yes", OrderNumber=1) + ir2 = DEFINE.ItemRef(ItemOID="IT.VS.VSTEST", + Mandatory="No", OrderNumber=2) igd.ItemRef = [ir1, ir2] igd_json = igd.to_json() igd_dict = json.loads(igd_json) @@ -83,16 +100,20 @@ def test_to_json(self): def test_to_xml(self): attrs = self.set_itemgroupdef_attributes() igd = DEFINE.ItemGroupDef(**attrs) - tt = DEFINE.TranslatedText(_content="this is the first test description", lang="en") + tt = DEFINE.TranslatedText( + _content="this is the first test description", lang="en") desc = DEFINE.Description() desc.TranslatedText = [tt] igd.Description = desc - ir1 = DEFINE.ItemRef(ItemOID="IT.STUDYID", Mandatory="Yes", OrderNumber=1) - ir2 = DEFINE.ItemRef(ItemOID="IT.VS.VSTEST", Mandatory="No", OrderNumber=2) + ir1 = DEFINE.ItemRef(ItemOID="IT.STUDYID", + Mandatory="Yes", OrderNumber=1) + ir2 = DEFINE.ItemRef(ItemOID="IT.VS.VSTEST", + Mandatory="No", OrderNumber=2) igd.ItemRef = [ir1, ir2] igd_xml = igd.to_xml() self.assertEqual(igd_xml.attrib["OID"], "IG.VS") - self.assertListEqual(["Description", "ItemRef", "ItemRef"], [e.tag for e in igd_xml]) + self.assertListEqual(["Description", "ItemRef", "ItemRef"], [ + e.tag for e in igd_xml]) def test_itemgroupdef_round_trip(self): """ system test to create and serialize an ItemGroupDef object """ @@ -152,7 +173,8 @@ def add_itemrefs(self, igd, item_refs): :param item_refs: list of ItemRef dictionaries containing ItemRef attributes """ for it in item_refs: - attrs = {"ItemOID": it.oid, "Mandatory": it.mandatory, "OrderNumber": it.order_number} + attrs = {"ItemOID": it.oid, "Mandatory": it.mandatory, + "OrderNumber": it.order_number} if it.key_sequence: attrs["KeySequence"] = it.key_sequence if it.method: @@ -168,26 +190,34 @@ def set_itemgroupdef_attributes(self): "IsReferenceData": "No", "Purpose": "Tabulation", "Class": "FINDINGS", "ArchiveLocationID": "LF.VS", "Structure": "One record per vital sign measurement per visit per subject"} - def set_itemrefs(self): """ set some ItemRef element attributes using test data :return: return a list of ItemRef named tuples """ itemrefs = [] - itemrefs.append(DEFINE.ItemRef(ItemOID="IT.STUDYID", Mandatory="Yes", OrderNumber=1, KeySequence=1)) - itemrefs.append(DEFINE.ItemRef(ItemOID="IT.TA.DOMAIN", Mandatory="Yes", OrderNumber=2)) - itemrefs.append(DEFINE.ItemRef(ItemOID="IT.TA.ARMCD", Mandatory="Yes", OrderNumber=3, KeySequence=2)) - itemrefs.append(DEFINE.ItemRef(ItemOID="IT.TA.ARM", Mandatory="Yes", OrderNumber=4)) - itemrefs.append(DEFINE.ItemRef(ItemOID="IT.TA.TAETORD", Mandatory="Yes", OrderNumber=5, KeySequence=3)) - itemrefs.append(DEFINE.ItemRef(ItemOID="IT.TA.ETCD", Mandatory="Yes", OrderNumber=6)) - itemrefs.append(DEFINE.ItemRef(ItemOID="IT.TA.ELEMENT", Mandatory="No", OrderNumber=7)) - itemrefs.append(DEFINE.ItemRef(ItemOID="IT.TA.TABRANCH", Mandatory="No", OrderNumber=8)) - itemrefs.append(DEFINE.ItemRef(ItemOID="IT.TA.TATRANS", Mandatory="No", OrderNumber=9)) - itemrefs.append(DEFINE.ItemRef(ItemOID="IT.TA.EPOCH", Mandatory="No", OrderNumber=10)) + itemrefs.append(DEFINE.ItemRef(ItemOID="IT.STUDYID", + Mandatory="Yes", OrderNumber=1, KeySequence=1)) + itemrefs.append(DEFINE.ItemRef(ItemOID="IT.TA.DOMAIN", + Mandatory="Yes", OrderNumber=2)) + itemrefs.append(DEFINE.ItemRef(ItemOID="IT.TA.ARMCD", + Mandatory="Yes", OrderNumber=3, KeySequence=2)) + itemrefs.append(DEFINE.ItemRef(ItemOID="IT.TA.ARM", + Mandatory="Yes", OrderNumber=4)) + itemrefs.append(DEFINE.ItemRef(ItemOID="IT.TA.TAETORD", + Mandatory="Yes", OrderNumber=5, KeySequence=3)) + itemrefs.append(DEFINE.ItemRef(ItemOID="IT.TA.ETCD", + Mandatory="Yes", OrderNumber=6)) + itemrefs.append(DEFINE.ItemRef(ItemOID="IT.TA.ELEMENT", + Mandatory="No", OrderNumber=7)) + itemrefs.append(DEFINE.ItemRef( + ItemOID="IT.TA.TABRANCH", Mandatory="No", OrderNumber=8)) + itemrefs.append(DEFINE.ItemRef(ItemOID="IT.TA.TATRANS", + Mandatory="No", OrderNumber=9)) + itemrefs.append(DEFINE.ItemRef(ItemOID="IT.TA.EPOCH", + Mandatory="No", OrderNumber=10)) return itemrefs - def create_odm_document(self, igd): """ assemble the ODM document, add the ItemGroupDef, and write it to a file @@ -203,7 +233,6 @@ def create_odm_document(self, igd): self.write_odm_file(odm_elem, self.odm_test_file) return odm_elem - def create_root(self): """ create the ODM root element object with test data @@ -215,7 +244,6 @@ def create_root(self): root = DEFINE.ODM("DEFINE.TEST.IGD.001", **root) return root - def create_study(self): """ create the ODM Study object instantiated with test data @@ -227,17 +255,15 @@ def create_study(self): study.ProtocolName = "ODM ItemGroupDef" return study - def create_mdv(self): """ create the ODM MetaDataVersion object instantiated with test data :return: ODM MetaDataVersion element object """ mdv = DEFINE.MetaDataVersion("MDV.TEST.IGD.001", "ItemGroupDefTest001", "ItemGroupDef Test 001", - "2.1.0") + "2.1.0") return mdv - def write_odm_file(self, odm, odm_file): """ write the ODM document to a file @@ -247,7 +273,6 @@ def write_odm_file(self, odm, odm_file): tree = ET.ElementTree(odm) tree.write(odm_file, xml_declaration=True) - def set_datetime(self): """return the current datetime in ISO 8601 format""" return datetime.datetime.utcnow().replace(tzinfo=datetime.timezone.utc).isoformat() diff --git a/tests/test_itemGroupDef_define21.py b/tests/test_itemGroupDef_define21.py index d9408e6..caadae2 100644 --- a/tests/test_itemGroupDef_define21.py +++ b/tests/test_itemGroupDef_define21.py @@ -13,11 +13,16 @@ class TestItemGroupDef(TestCase): def setUp(self) -> None: attrs = self.set_itemgroupdef_attributes() self.igd = DEFINE.ItemGroupDef(**attrs) - self.test_file = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'data', 'defineV21-SDTM-test.xml') - self.test_file_json = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'data', 'defineV21-SDTM-test.json') - self.input_file = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'data', 'defineV21-SDTM.xml') - self.nsr = NS.NamespaceRegistry(prefix="odm", uri="http://www.cdisc.org/ns/odm/v1.3", is_default=True) - self.nsr = NS.NamespaceRegistry(prefix="def", uri="http://www.cdisc.org/ns/def/v2.1") + self.test_file = os.path.join(os.path.dirname( + os.path.realpath(__file__)), 'data', 'defineV21-SDTM-test.xml') + self.test_file_json = os.path.join(os.path.dirname( + os.path.realpath(__file__)), 'data', 'defineV21-SDTM-test.json') + self.input_file = os.path.join(os.path.dirname( + os.path.realpath(__file__)), 'data', 'defineV21-SDTM.xml') + self.nsr = NS.NamespaceRegistry( + prefix="odm", uri="http://www.cdisc.org/ns/odm/v1.3", is_default=True) + self.nsr = NS.NamespaceRegistry( + prefix="def", uri="http://www.cdisc.org/ns/def/v2.1") def test_context(self): odm = DEFINE.ODM( @@ -30,41 +35,53 @@ def test_context(self): def test_standards(self): mdv = self.create_mdv() mdv.Standards = DEFINE.Standards() - std1 = DEFINE.Standard(OID="STD.1", Name="SDTMIG", Type="IG", Version="3.1.2", Status="Final", CommentOID="COM.STD1") + std1 = DEFINE.Standard(OID="STD.1", Name="SDTMIG", Type="IG", + Version="3.1.2", Status="Final", CommentOID="COM.STD1") mdv.Standards.Standard.append(std1) - std2 = DEFINE.Standard(OID="STD.2", Name="CDISC/NCI", Type="CT", PublishingSet="SDTM", Version="2011-12-09", Status="Final") + std2 = DEFINE.Standard(OID="STD.2", Name="CDISC/NCI", Type="CT", + PublishingSet="SDTM", Version="2011-12-09", Status="Final") mdv.Standards.Standard.append(std2) self.assertEqual(mdv.Standards.Standard[0].Name, "SDTMIG") self.assertEqual(mdv.Standards.Standard[1].Version, "2011-12-09") def test_add_class(self): def_class = DEFINE.Class(Name="FINDINGS") - def_class.SubClass.append(DEFINE.SubClass(Name="SUBFINDINGS", ParentClass="FINDINGS")) + def_class.SubClass.append(DEFINE.SubClass( + Name="SUBFINDINGS", ParentClass="FINDINGS")) self.igd.Class = def_class self.assertEqual(self.igd.Class.Name, "FINDINGS") self.assertEqual(self.igd.Class.SubClass[0].ParentClass, "FINDINGS") def test_add_description(self): - tt1 = DEFINE.TranslatedText(_content="this is the first test description", lang="en") - tt2 = DEFINE.TranslatedText(_content="this is the second test description", lang="en") + tt1 = DEFINE.TranslatedText( + _content="this is the first test description", lang="en") + tt2 = DEFINE.TranslatedText( + _content="this is the second test description", lang="en") self.igd.Description = DEFINE.Description() self.igd.Description.TranslatedText = [tt1, tt2] self.assertEqual(len(self.igd.Description.TranslatedText), 2) - self.assertEqual(self.igd.Description.TranslatedText[1]._content, 'this is the second test description') + self.assertEqual( + self.igd.Description.TranslatedText[1]._content, 'this is the second test description') def test_add_item_ref(self): - self.igd.ItemRef.append(DEFINE.ItemRef(ItemOID="IT.STUDYID", Mandatory="Yes", OrderNumber=1, IsNonStandard="Yes")) - self.igd.ItemRef.append(DEFINE.ItemRef(ItemOID="IT.VS.VSTEST", Mandatory="No", OrderNumber=2, HasNoData="Yes")) - self.igd.ItemRef.append(DEFINE.ItemRef(ItemOID="IT.VS.VSORRES", Mandatory="Yes", OrderNumber=3, MethodOID="MT.METHODFEX")) + self.igd.ItemRef.append(DEFINE.ItemRef( + ItemOID="IT.STUDYID", Mandatory="Yes", OrderNumber=1, IsNonStandard="Yes")) + self.igd.ItemRef.append(DEFINE.ItemRef( + ItemOID="IT.VS.VSTEST", Mandatory="No", OrderNumber=2, HasNoData="Yes")) + self.igd.ItemRef.append(DEFINE.ItemRef( + ItemOID="IT.VS.VSORRES", Mandatory="Yes", OrderNumber=3, MethodOID="MT.METHODFEX")) self.assertEqual(self.igd.ItemRef[0].IsNonStandard, "Yes") self.assertEqual(self.igd.ItemRef[1].HasNoData, "Yes") def test_add_item_ref_list(self): - ir1 = DEFINE.ItemRef(ItemOID="IT.STUDYID", Mandatory="Yes", OrderNumber=1) + ir1 = DEFINE.ItemRef(ItemOID="IT.STUDYID", + Mandatory="Yes", OrderNumber=1) self.igd.ItemRef.append(ir1) - ir2 = DEFINE.ItemRef(ItemOID="IT.VS.VSTEST", Mandatory="No", OrderNumber=2) + ir2 = DEFINE.ItemRef(ItemOID="IT.VS.VSTEST", + Mandatory="No", OrderNumber=2) self.igd.ItemRef.append(ir2) - ir3 = DEFINE.ItemRef(ItemOID="IT.VS.VSORRES", Mandatory="Yes", OrderNumber=3, MethodOID="MT.METHODFEX") + ir3 = DEFINE.ItemRef(ItemOID="IT.VS.VSORRES", Mandatory="Yes", + OrderNumber=3, MethodOID="MT.METHODFEX") self.igd.ItemRef.append(ir3) self.assertEqual(self.igd.ItemRef[0].ItemOID, "IT.STUDYID") self.assertEqual(self.igd.ItemRef[2].MethodOID, "MT.METHODFEX") @@ -75,10 +92,12 @@ def test_add_item_ref_missing_kwarg(self): def test_add_item_ref_invalid_kwarg(self): with self.assertRaises(TypeError): - self.igd.ItemRef = [DEFINE.ItemRef(ItemOID="IT.STUDYID", Mandatory="Yes", InValid="Yes")] + self.igd.ItemRef = [DEFINE.ItemRef( + ItemOID="IT.STUDYID", Mandatory="Yes", InValid="Yes")] def test_item_ref_exists(self): - self.igd.ItemRef = [DEFINE.ItemRef(ItemOID="IT.VS.VSTESTCD", Mandatory="Yes", OrderNumber=4)] + self.igd.ItemRef = [DEFINE.ItemRef( + ItemOID="IT.VS.VSTESTCD", Mandatory="Yes", OrderNumber=4)] self.assertEqual(self.igd.ItemRef[0].ItemOID, "IT.VS.VSTESTCD") def test_add_alias(self): @@ -91,16 +110,20 @@ def test_add_alias(self): def test_to_xml(self): attrs = self.set_itemgroupdef_attributes() igd = DEFINE.ItemGroupDef(**attrs) - tt = DEFINE.TranslatedText(_content="this is the first test description", lang="en") + tt = DEFINE.TranslatedText( + _content="this is the first test description", lang="en") desc = DEFINE.Description() desc.TranslatedText = [tt] igd.Description = desc - ir1 = DEFINE.ItemRef(ItemOID="IT.STUDYID", Mandatory="Yes", OrderNumber=1) - ir2 = DEFINE.ItemRef(ItemOID="IT.VS.VSTEST", Mandatory="No", OrderNumber=2) + ir1 = DEFINE.ItemRef(ItemOID="IT.STUDYID", + Mandatory="Yes", OrderNumber=1) + ir2 = DEFINE.ItemRef(ItemOID="IT.VS.VSTEST", + Mandatory="No", OrderNumber=2) igd.ItemRef = [ir1, ir2] igd_xml = igd.to_xml() self.assertEqual(igd_xml.attrib["OID"], "IG.VS") - self.assertListEqual(["Description", "ItemRef", "ItemRef"], [e.tag for e in igd_xml]) + self.assertListEqual(["Description", "ItemRef", "ItemRef"], [ + e.tag for e in igd_xml]) def test_itemgroupdef_parse_xml(self): parser = ODM_PARSER.ODMParser(self.input_file, self.nsr) @@ -118,14 +141,16 @@ def test_itemgroupdef_parse_xml(self): def test_write_xml(self): attrs = self.set_itemgroupdef_attributes() igd = DEFINE.ItemGroupDef(**attrs) - tt = DEFINE.TranslatedText(_content="this is the first test description", lang="en") + tt = DEFINE.TranslatedText( + _content="this is the first test description", lang="en") desc = DEFINE.Description() desc.TranslatedText = [tt] igd.Description = desc ir_list = self.set_itemrefs() igd.ItemRef = ir_list self.create_odm_document(igd) - loader = LD.ODMLoader(OL.XMLDefineLoader(model_package="define_2_1", ns_uri="http://www.cdisc.org/ns/def/v2.1")) + loader = LD.ODMLoader(OL.XMLDefineLoader( + model_package="define_2_1", ns_uri="http://www.cdisc.org/ns/def/v2.1")) loader.open_odm_document(self.test_file) mdv = loader.MetaDataVersion() @@ -138,7 +163,8 @@ def test_write_xml(self): def test_write_json(self): attrs = self.set_itemgroupdef_attributes() igd = DEFINE.ItemGroupDef(**attrs) - tt = DEFINE.TranslatedText(_content="this is the first test description", lang="en") + tt = DEFINE.TranslatedText( + _content="this is the first test description", lang="en") desc = DEFINE.Description() desc.TranslatedText = [tt] igd.Description = desc @@ -163,7 +189,8 @@ def add_itemrefs(igd, item_refs): :param item_refs: list of ItemRef dictionaries containing ItemRef attributes """ for it in item_refs: - attrs = {"ItemOID": it.oid, "Mandatory": it.mandatory, "OrderNumber": it.order_number} + attrs = {"ItemOID": it.oid, "Mandatory": it.mandatory, + "OrderNumber": it.order_number} if it.key_sequence: attrs["KeySequence"] = it.key_sequence if it.method: @@ -188,16 +215,26 @@ def set_itemrefs(): :return: return a list of ItemRef named tuples """ itemrefs = [ - DEFINE.ItemRef(ItemOID="IT.STUDYID", Mandatory="Yes", OrderNumber=1, KeySequence=1), - DEFINE.ItemRef(ItemOID="IT.TA.DOMAIN", Mandatory="Yes", OrderNumber=2), - DEFINE.ItemRef(ItemOID="IT.TA.ARMCD", Mandatory="Yes", OrderNumber=3, KeySequence=2), - DEFINE.ItemRef(ItemOID="IT.TA.ARM", Mandatory="Yes", OrderNumber=4), - DEFINE.ItemRef(ItemOID="IT.TA.TAETORD", Mandatory="Yes", OrderNumber=5, KeySequence=3), - DEFINE.ItemRef(ItemOID="IT.TA.ETCD", Mandatory="Yes", OrderNumber=6), - DEFINE.ItemRef(ItemOID="IT.TA.ELEMENT", Mandatory="No", OrderNumber=7), - DEFINE.ItemRef(ItemOID="IT.TA.TABRANCH", Mandatory="No", OrderNumber=8), - DEFINE.ItemRef(ItemOID="IT.TA.TATRANS", Mandatory="No", OrderNumber=9), - DEFINE.ItemRef(ItemOID="IT.TA.EPOCH", Mandatory="No", OrderNumber=10) + DEFINE.ItemRef(ItemOID="IT.STUDYID", Mandatory="Yes", + OrderNumber=1, KeySequence=1), + DEFINE.ItemRef(ItemOID="IT.TA.DOMAIN", + Mandatory="Yes", OrderNumber=2), + DEFINE.ItemRef(ItemOID="IT.TA.ARMCD", Mandatory="Yes", + OrderNumber=3, KeySequence=2), + DEFINE.ItemRef(ItemOID="IT.TA.ARM", + Mandatory="Yes", OrderNumber=4), + DEFINE.ItemRef(ItemOID="IT.TA.TAETORD", + Mandatory="Yes", OrderNumber=5, KeySequence=3), + DEFINE.ItemRef(ItemOID="IT.TA.ETCD", + Mandatory="Yes", OrderNumber=6), + DEFINE.ItemRef(ItemOID="IT.TA.ELEMENT", + Mandatory="No", OrderNumber=7), + DEFINE.ItemRef(ItemOID="IT.TA.TABRANCH", + Mandatory="No", OrderNumber=8), + DEFINE.ItemRef(ItemOID="IT.TA.TATRANS", + Mandatory="No", OrderNumber=9), + DEFINE.ItemRef(ItemOID="IT.TA.EPOCH", + Mandatory="No", OrderNumber=10) ] return itemrefs @@ -240,7 +277,6 @@ def create_root(self): root = DEFINE.ODM(**root) return root - @staticmethod def create_study(): """ @@ -248,12 +284,14 @@ def create_study(): :return: ODM Study element object """ study = DEFINE.Study(OID="ST.TEST.IGD.001") - study.GlobalVariables.StudyName = DEFINE.StudyName(_content="TEST ODM ItemGroupDef") - study.GlobalVariables.StudyDescription = DEFINE.StudyDescription(_content="ItemGroupDef 001") - study.GlobalVariables.ProtocolName = DEFINE.ProtocolName(_content="ODM ItemGroupDef") + study.GlobalVariables.StudyName = DEFINE.StudyName( + _content="TEST ODM ItemGroupDef") + study.GlobalVariables.StudyDescription = DEFINE.StudyDescription( + _content="ItemGroupDef 001") + study.GlobalVariables.ProtocolName = DEFINE.ProtocolName( + _content="ODM ItemGroupDef") return study - @staticmethod def create_mdv(): """ @@ -264,7 +302,6 @@ def create_mdv(): Description="ItemGroupDef Test 001", DefineVersion="2.1.0") return mdv - @staticmethod def write_odm_file(odm, odm_file): """ @@ -275,7 +312,6 @@ def write_odm_file(odm, odm_file): tree = ET.ElementTree(odm) tree.write(odm_file, xml_declaration=True) - @staticmethod def set_datetime(): """return the current datetime in ISO 8601 format""" diff --git a/tests/test_json_from_xml.py b/tests/test_json_from_xml.py index 1da85e8..454ac9d 100644 --- a/tests/test_json_from_xml.py +++ b/tests/test_json_from_xml.py @@ -4,10 +4,12 @@ import os import json +from tests import get_data_file_path + class TestJsonFromXml(TestCase): def setUp(self) -> None: - self.odm_file = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'data', 'cdash-odm-test.xml') + self.odm_file = get_data_file_path("cdash-odm-test.xml") self.loader = LD.ODMLoader(OL.XMLODMLoader()) def test_open_odm_document(self): @@ -20,8 +22,9 @@ def test_json_from_xml(self): root = self.loader.open_odm_document(self.odm_file) odm = self.loader.create_odmlib(root) odm_json = odm.to_json() - odm_json_file = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'data', 'cdash_odm_test.json') + odm_json_file = get_data_file_path("cdash_odm_test.json", check_exists=False) with open(odm_json_file, "w") as odm_in: odm_in.write(odm_json) odm_dict = json.loads(odm_json) - self.assertEqual(odm_dict["Study"][0]["MetaDataVersion"][0]["ItemDef"][0]["OID"], "ODM.IT.Common.StudyID") + self.assertEqual(odm_dict["Study"][0]["MetaDataVersion"] + [0]["ItemDef"][0]["OID"], "ODM.IT.Common.StudyID") diff --git a/tests/test_metadataversion.py b/tests/test_metadataversion.py index cdbf928..90d7a28 100644 --- a/tests/test_metadataversion.py +++ b/tests/test_metadataversion.py @@ -7,31 +7,37 @@ class TestMetaDataVersion(TestCase): def setUp(self) -> None: attrs = self.set_attributes() self.mdv = ODM.MetaDataVersion(**attrs) - self.odm_test_file = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'data', 'test_mdv_001.xml') + self.odm_test_file = os.path.join(os.path.dirname( + os.path.realpath(__file__)), 'data', 'test_mdv_001.xml') def test_add_include(self): - inc = ODM.Include(StudyOID="STUDY.TRACE-XML-DEMO", MetaDataVersionOID="MDV.TRACE-XML-ODM-01") + inc = ODM.Include(StudyOID="STUDY.TRACE-XML-DEMO", + MetaDataVersionOID="MDV.TRACE-XML-ODM-01") self.mdv.Include = inc self.assertEqual(self.mdv.Include.StudyOID, 'STUDY.TRACE-XML-DEMO') def test_add_protocol(self): self.mdv.Protocol = self.add_protocol() print(self.mdv.Protocol.to_dict()) - self.assertEqual(self.mdv.Protocol.StudyEventRef[1].StudyEventOID, "FOLLOW-UP") - self.assertEqual(self.mdv.Protocol.Description.TranslatedText[0]._content, "Trace-XML Test CDASH File") + self.assertEqual( + self.mdv.Protocol.StudyEventRef[1].StudyEventOID, "FOLLOW-UP") + self.assertEqual( + self.mdv.Protocol.Description.TranslatedText[0]._content, "Trace-XML Test CDASH File") def test_add_studyeventdef(self): self.mdv.Protocol = self.add_protocol() self.mdv.StudyEventDef = self.add_SED() self.assertEqual(len(self.mdv.StudyEventDef[1].FormRef), 3) - self.assertEqual(self.mdv.StudyEventDef[1].FormRef[2].FormOID, 'ODM.F.AE') + self.assertEqual( + self.mdv.StudyEventDef[1].FormRef[2].FormOID, 'ODM.F.AE') def test_add_formdef(self): self.mdv.Protocol = self.add_protocol() self.mdv.StudyEventDef = self.add_SED() self.mdv.FormDef = self.add_FD() self.assertEqual(len(self.mdv.FormDef[1].ItemGroupRef), 2) - self.assertEqual(self.mdv.FormDef[0].ItemGroupRef[2].ItemGroupOID, 'ODM.IG.VS') + self.assertEqual( + self.mdv.FormDef[0].ItemGroupRef[2].ItemGroupOID, 'ODM.IG.VS') def test_add_itemgroupdef(self): self.mdv.Protocol = self.add_protocol() @@ -39,7 +45,8 @@ def test_add_itemgroupdef(self): self.mdv.FormDef = self.add_FD() self.mdv.ItemGroupDef = self.add_IGD() self.assertEqual(len(self.mdv.ItemGroupDef[1].ItemRef), 2) - self.assertEqual(self.mdv.ItemGroupDef[0].ItemRef[2].ItemOID, 'ODM.IT.VS.BP.SYSBP.VSORRES') + self.assertEqual( + self.mdv.ItemGroupDef[0].ItemRef[2].ItemOID, 'ODM.IT.VS.BP.SYSBP.VSORRES') def test_add_itemdef(self): self.mdv.Protocol = self.add_protocol() @@ -48,7 +55,8 @@ def test_add_itemdef(self): self.mdv.ItemGroupDef = self.add_IGD() self.mdv.ItemDef = self.add_ITD() self.assertEqual("CDASH/SDTM", self.mdv.ItemDef[1].Alias[1].Context) - self.assertEqual("Diastolic", self.mdv.ItemDef[1].Question.TranslatedText[0]._content) + self.assertEqual( + "Diastolic", self.mdv.ItemDef[1].Question.TranslatedText[0]._content) self.assertEqual("partialDate", self.mdv.ItemDef[0].DataType) def test_add_codelist(self): @@ -59,7 +67,8 @@ def test_add_codelist(self): self.mdv.ItemDef = self.add_ITD() self.mdv.CodeList = self.add_CL() self.assertEqual("ODM.CL.NY_SUB_Y_N", self.mdv.CodeList[0].OID) - self.assertEqual("Yes", self.mdv.CodeList[0].CodeListItem[1].Decode.TranslatedText[0]._content) + self.assertEqual( + "Yes", self.mdv.CodeList[0].CodeListItem[1].Decode.TranslatedText[0]._content) def test_add_methoddef(self): self.mdv.Protocol = self.add_protocol() @@ -70,7 +79,8 @@ def test_add_methoddef(self): self.mdv.CodeList = self.add_CL() self.mdv.MethodDef = self.add_MD() self.assertEqual("ODM.MT.DOB", self.mdv.MethodDef[0].OID) - self.assertEqual("Concatenation of BRTHYR, BRTHMO, and BRTHDY in ISO 8601 format", self.mdv.MethodDef[0].Description.TranslatedText[0]._content) + self.assertEqual("Concatenation of BRTHYR, BRTHMO, and BRTHDY in ISO 8601 format", + self.mdv.MethodDef[0].Description.TranslatedText[0]._content) def test_add_conditiondef(self): self.mdv.Protocol = self.add_protocol() @@ -82,10 +92,12 @@ def test_add_conditiondef(self): self.mdv.MethodDef = self.add_MD() self.mdv.ConditionDef = self.add_CD() self.assertEqual("ODM.CD.BRTHMO", self.mdv.ConditionDef[0].OID) - self.assertEqual("Skip the BRTHMO field when BRTHYR length NE 4", self.mdv.ConditionDef[0].Description.TranslatedText[0]._content) + self.assertEqual("Skip the BRTHMO field when BRTHYR length NE 4", + self.mdv.ConditionDef[0].Description.TranslatedText[0]._content) def test_add_presentation(self): - p = ODM.Presentation(OID="ODM.PR.CRF.DM", lang="en", _content="stylesheet") + p = ODM.Presentation(OID="ODM.PR.CRF.DM", + lang="en", _content="stylesheet") self.mdv.Presentation = [p] self.assertEqual("ODM.PR.CRF.DM", self.mdv.Presentation[0].OID) @@ -120,18 +132,22 @@ def test_mdv_to_xml(self): self.assertListEqual(children, [e.tag for e in mdv_xml]) def add_CD(self): - tt1 = ODM.TranslatedText(_content="Skip the BRTHMO field when BRTHYR length NE 4", lang="en") + tt1 = ODM.TranslatedText( + _content="Skip the BRTHMO field when BRTHYR length NE 4", lang="en") desc = ODM.Description() desc.TranslatedText = [tt1] - cd = ODM.ConditionDef(OID="ODM.CD.BRTHMO", Name="Skip BRTHMO when no BRTHYR") + cd = ODM.ConditionDef(OID="ODM.CD.BRTHMO", + Name="Skip BRTHMO when no BRTHYR") cd.Description = desc return [cd] def add_MD(self): - tt1 = ODM.TranslatedText(_content="Concatenation of BRTHYR, BRTHMO, and BRTHDY in ISO 8601 format", lang="en") + tt1 = ODM.TranslatedText( + _content="Concatenation of BRTHYR, BRTHMO, and BRTHDY in ISO 8601 format", lang="en") desc = ODM.Description() desc.TranslatedText = [tt1] - md = ODM.MethodDef(OID="ODM.MT.DOB", Name="Create BRTHDTC from date ELEMENTS", Type="Computation") + md = ODM.MethodDef( + OID="ODM.MT.DOB", Name="Create BRTHDTC from date ELEMENTS", Type="Computation") md.Description = desc return [md] @@ -146,11 +162,11 @@ def add_CL(self): dc2.TranslatedText = [tt2] cli2 = ODM.CodeListItem(CodedValue="Y") cli2.Decode = dc2 - cl = ODM.CodeList(OID="ODM.CL.NY_SUB_Y_N", Name="No Yes Response", DataType="text") + cl = ODM.CodeList(OID="ODM.CL.NY_SUB_Y_N", + Name="No Yes Response", DataType="text") cl.CodeListItem = [cli1, cli2] return [cl] - def add_ITD(self): # ItemDef 1 ttd1 = ODM.TranslatedText(_content="Date of measurements", lang="en") @@ -160,12 +176,14 @@ def add_ITD(self): q1 = ODM.Question() q1.TranslatedText = [ttq1] a1 = ODM.Alias(Context="CDASH", Name="VSDAT") - itd1 = ODM.ItemDef(OID="ODM.IT.VS.VSDAT", Name="Date", DataType="partialDate") + itd1 = ODM.ItemDef(OID="ODM.IT.VS.VSDAT", + Name="Date", DataType="partialDate") itd1.Description = desc1 itd1.Question = q1 itd1.Alias = [a1] # ItemDef 2 - ttd2 = ODM.TranslatedText(_content="Result of the vital signs measurement as originally received or collected.", lang="en") + ttd2 = ODM.TranslatedText( + _content="Result of the vital signs measurement as originally received or collected.", lang="en") ttq2 = ODM.TranslatedText(_content="Diastolic", lang="en") desc2 = ODM.Description() desc2.TranslatedText = [ttd2] @@ -173,7 +191,8 @@ def add_ITD(self): q2.TranslatedText = [ttq2] a2a = ODM.Alias(Context="CDASH", Name="BP.DIABP.VSORRES") a2b = ODM.Alias(Context="CDASH/SDTM", Name="VSORRES+VSORRESU") - itd2 = ODM.ItemDef(OID="ODM.IT.VS.BP.VSORRESU", Name="BP Units", DataType="text") + itd2 = ODM.ItemDef(OID="ODM.IT.VS.BP.VSORRESU", + Name="BP Units", DataType="text") itd2.Description = desc2 itd2.Question = q2 itd2.Alias = [a2a, a2b] @@ -181,24 +200,33 @@ def add_ITD(self): def add_IGD(self): itr1 = ODM.ItemRef(ItemOID="ODM.IT.VS.VSDAT", Mandatory="Yes") - itr2 = ODM.ItemRef(ItemOID="ODM.IT.VS.BP.DIABP.VSORRES", Mandatory="Yes") - itr3 = ODM.ItemRef(ItemOID="ODM.IT.VS.BP.SYSBP.VSORRES", Mandatory="Yes") - igd1 = ODM.ItemGroupDef(OID="ODM.IG.VS", Name="Vital Sign Measurement", Repeating="Yes") + itr2 = ODM.ItemRef( + ItemOID="ODM.IT.VS.BP.DIABP.VSORRES", Mandatory="Yes") + itr3 = ODM.ItemRef( + ItemOID="ODM.IT.VS.BP.SYSBP.VSORRES", Mandatory="Yes") + igd1 = ODM.ItemGroupDef( + OID="ODM.IG.VS", Name="Vital Sign Measurement", Repeating="Yes") igd1.ItemRef = [itr1, itr2, itr3] igr4 = ODM.ItemRef(ItemOID="ODM.IT.DM.BRTHYR", Mandatory="Yes") igr5 = ODM.ItemRef(ItemOID="ODM.IT.DM.SEX", Mandatory="Yes") - igd2 = ODM.ItemGroupDef(OID="ODM.IG.DM", Name="Demographics", Repeating="No") + igd2 = ODM.ItemGroupDef( + OID="ODM.IG.DM", Name="Demographics", Repeating="No") igd2.ItemRef = [igr4, igr5] return [igd1, igd2] def add_FD(self): - igr1 = ODM.ItemGroupRef(ItemGroupOID="ODM.IG.Common", Mandatory="Yes", OrderNumber=1) - igr2 = ODM.ItemGroupRef(ItemGroupOID="ODM.IG.VS_GENERAL", Mandatory="Yes", OrderNumber=2) - igr3 = ODM.ItemGroupRef(ItemGroupOID="ODM.IG.VS", Mandatory="Yes", OrderNumber=3) + igr1 = ODM.ItemGroupRef( + ItemGroupOID="ODM.IG.Common", Mandatory="Yes", OrderNumber=1) + igr2 = ODM.ItemGroupRef( + ItemGroupOID="ODM.IG.VS_GENERAL", Mandatory="Yes", OrderNumber=2) + igr3 = ODM.ItemGroupRef(ItemGroupOID="ODM.IG.VS", + Mandatory="Yes", OrderNumber=3) fd1 = ODM.FormDef(OID="ODM.F.VS", Name="Vital Signs", Repeating="No") fd1.ItemGroupRef = [igr1, igr2, igr3] - igr4 = ODM.ItemGroupRef(ItemGroupOID="ODM.IG.Common", Mandatory="Yes", OrderNumber=1) - igr5 = ODM.ItemGroupRef(ItemGroupOID="ODM.IG.DM", Mandatory="Yes", OrderNumber=2) + igr4 = ODM.ItemGroupRef( + ItemGroupOID="ODM.IG.Common", Mandatory="Yes", OrderNumber=1) + igr5 = ODM.ItemGroupRef(ItemGroupOID="ODM.IG.DM", + Mandatory="Yes", OrderNumber=2) fd2 = ODM.FormDef(OID="ODM.F.DM", Name="Demographics", Repeating="No") fd2.ItemGroupRef = [igr4, igr5] return [fd1, fd2] @@ -207,21 +235,27 @@ def add_SED(self): fr1 = ODM.FormRef(FormOID="ODM.F.DM", Mandatory="Yes", OrderNumber=1) fr2 = ODM.FormRef(FormOID="ODM.F.VS", Mandatory="Yes", OrderNumber=2) fr3 = ODM.FormRef(FormOID="ODM.F.AE", Mandatory="Yes", OrderNumber=3) - ser1 = ODM.StudyEventDef(OID="BASELINE", Name="Baseline Visit", Repeating="No", Type="Scheduled") + ser1 = ODM.StudyEventDef( + OID="BASELINE", Name="Baseline Visit", Repeating="No", Type="Scheduled") ser1.FormRef = [fr1, fr2, fr3] - ser2 = ODM.StudyEventDef(OID="FOLLOW-UP", Name="Follow-up Visit", Repeating="Yes", Type="Scheduled") + ser2 = ODM.StudyEventDef( + OID="FOLLOW-UP", Name="Follow-up Visit", Repeating="Yes", Type="Scheduled") ser2.FormRef = [fr1, fr2, fr3] return [ser1, ser2] def add_protocol(self): p = ODM.Protocol() - tt = ODM.TranslatedText(_content="Trace-XML Test CDASH File", lang="en") + tt = ODM.TranslatedText( + _content="Trace-XML Test CDASH File", lang="en") p.Description = ODM.Description() p.Description.TranslatedText = [tt] - ser1 = ODM.StudyEventRef(StudyEventOID="BASELINE", OrderNumber=1, Mandatory="Yes") - ser2 = ODM.StudyEventRef(StudyEventOID="FOLLOW-UP", OrderNumber=2, Mandatory="Yes") + ser1 = ODM.StudyEventRef( + StudyEventOID="BASELINE", OrderNumber=1, Mandatory="Yes") + ser2 = ODM.StudyEventRef( + StudyEventOID="FOLLOW-UP", OrderNumber=2, Mandatory="Yes") p.StudyEventRef = [ser1, ser2] - p.Alias = [ODM.Alias(Context="ClinicalTrials.gov", Name="trace-protocol")] + p.Alias = [ODM.Alias(Context="ClinicalTrials.gov", + Name="trace-protocol")] return p def set_attributes(self): @@ -229,234 +263,234 @@ def set_attributes(self): def expected_dict(self): return { - "OID": "MDV.TRACE-XML-ODM-01", - "Name": "TRACE-XML MDV", - "Description": "Trace-XML Example", - "Protocol": { - "Description": {"TranslatedText": [{ - "_content": "Trace-XML Test CDASH File", - "lang": "en" - }]}, - "StudyEventRef": [ - { - "StudyEventOID": "BASELINE", - "OrderNumber": 1, - "Mandatory": "Yes" + "OID": "MDV.TRACE-XML-ODM-01", + "Name": "TRACE-XML MDV", + "Description": "Trace-XML Example", + "Protocol": { + "Description": {"TranslatedText": [{ + "_content": "Trace-XML Test CDASH File", + "lang": "en" + }]}, + "StudyEventRef": [ + { + "StudyEventOID": "BASELINE", + "OrderNumber": 1, + "Mandatory": "Yes" + }, + { + "StudyEventOID": "FOLLOW-UP", + "OrderNumber": 2, + "Mandatory": "Yes" + } + ], + "Alias": [{ + "Context": "ClinicalTrials.gov", + "Name": "trace-protocol" + }] }, - { - "StudyEventOID": "FOLLOW-UP", - "OrderNumber": 2, - "Mandatory": "Yes" - } - ], - "Alias": [{ - "Context": "ClinicalTrials.gov", - "Name": "trace-protocol" - }] - }, - "StudyEventDef": [ - { - "OID": "BASELINE", - "Name": "Baseline Visit", - "Repeating": "No", - "Type": "Scheduled", - "FormRef": [ - { - "FormOID": "ODM.F.DM", - "Mandatory": "Yes", - "OrderNumber": 1 - }, - { - "FormOID": "ODM.F.VS", - "Mandatory": "Yes", - "OrderNumber": 2 - }, - { - "FormOID": "ODM.F.AE", - "Mandatory": "Yes", - "OrderNumber": 3 - } - ] - }, - { - "OID": "FOLLOW-UP", - "Name": "Follow-up Visit", - "Repeating": "Yes", - "Type": "Scheduled", - "FormRef": [ - { - "FormOID": "ODM.F.DM", - "Mandatory": "Yes", - "OrderNumber": 1 - }, - { - "FormOID": "ODM.F.VS", - "Mandatory": "Yes", - "OrderNumber": 2 - }, - { - "FormOID": "ODM.F.AE", - "Mandatory": "Yes", - "OrderNumber": 3 - } - ] - } - ], - "FormDef": [ - { - "OID": "ODM.F.VS", - "Name": "Vital Signs", - "Repeating": "No", - "ItemGroupRef": [ + "StudyEventDef": [ { - "ItemGroupOID": "ODM.IG.Common", - "Mandatory": "Yes", - "OrderNumber": 1 + "OID": "BASELINE", + "Name": "Baseline Visit", + "Repeating": "No", + "Type": "Scheduled", + "FormRef": [ + { + "FormOID": "ODM.F.DM", + "Mandatory": "Yes", + "OrderNumber": 1 + }, + { + "FormOID": "ODM.F.VS", + "Mandatory": "Yes", + "OrderNumber": 2 + }, + { + "FormOID": "ODM.F.AE", + "Mandatory": "Yes", + "OrderNumber": 3 + } + ] }, { - "ItemGroupOID": "ODM.IG.VS_GENERAL", - "Mandatory": "Yes", - "OrderNumber": 2 - }, - { - "ItemGroupOID": "ODM.IG.VS", - "Mandatory": "Yes", - "OrderNumber": 3 + "OID": "FOLLOW-UP", + "Name": "Follow-up Visit", + "Repeating": "Yes", + "Type": "Scheduled", + "FormRef": [ + { + "FormOID": "ODM.F.DM", + "Mandatory": "Yes", + "OrderNumber": 1 + }, + { + "FormOID": "ODM.F.VS", + "Mandatory": "Yes", + "OrderNumber": 2 + }, + { + "FormOID": "ODM.F.AE", + "Mandatory": "Yes", + "OrderNumber": 3 + } + ] } - ] - }, - { - "OID": "ODM.F.DM", - "Name": "Demographics", - "Repeating": "No", - "ItemGroupRef": [ + ], + "FormDef": [ { - "ItemGroupOID": "ODM.IG.Common", - "Mandatory": "Yes", - "OrderNumber": 1 + "OID": "ODM.F.VS", + "Name": "Vital Signs", + "Repeating": "No", + "ItemGroupRef": [ + { + "ItemGroupOID": "ODM.IG.Common", + "Mandatory": "Yes", + "OrderNumber": 1 + }, + { + "ItemGroupOID": "ODM.IG.VS_GENERAL", + "Mandatory": "Yes", + "OrderNumber": 2 + }, + { + "ItemGroupOID": "ODM.IG.VS", + "Mandatory": "Yes", + "OrderNumber": 3 + } + ] }, { - "ItemGroupOID": "ODM.IG.DM", - "Mandatory": "Yes", - "OrderNumber": 2 + "OID": "ODM.F.DM", + "Name": "Demographics", + "Repeating": "No", + "ItemGroupRef": [ + { + "ItemGroupOID": "ODM.IG.Common", + "Mandatory": "Yes", + "OrderNumber": 1 + }, + { + "ItemGroupOID": "ODM.IG.DM", + "Mandatory": "Yes", + "OrderNumber": 2 + } + ] } - ] - } - ], - "ItemGroupDef": [ - { - "OID": "ODM.IG.VS", - "Name": "Vital Sign Measurement", - "Repeating": "Yes", - "ItemRef": [ - { - "ItemOID": "ODM.IT.VS.VSDAT", - "Mandatory": "Yes" - }, + ], + "ItemGroupDef": [ { - "ItemOID": "ODM.IT.VS.BP.DIABP.VSORRES", - "Mandatory": "Yes" + "OID": "ODM.IG.VS", + "Name": "Vital Sign Measurement", + "Repeating": "Yes", + "ItemRef": [ + { + "ItemOID": "ODM.IT.VS.VSDAT", + "Mandatory": "Yes" + }, + { + "ItemOID": "ODM.IT.VS.BP.DIABP.VSORRES", + "Mandatory": "Yes" + }, + { + "ItemOID": "ODM.IT.VS.BP.SYSBP.VSORRES", + "Mandatory": "Yes" + } + ] }, { - "ItemOID": "ODM.IT.VS.BP.SYSBP.VSORRES", - "Mandatory": "Yes" + "OID": "ODM.IG.DM", + "Name": "Demographics", + "Repeating": "No", + "ItemRef": [ + { + "ItemOID": "ODM.IT.DM.BRTHYR", + "Mandatory": "Yes" + }, + { + "ItemOID": "ODM.IT.DM.SEX", + "Mandatory": "Yes" + } + ] } - ] - }, - { - "OID": "ODM.IG.DM", - "Name": "Demographics", - "Repeating": "No", - "ItemRef": [ + ], + "ItemDef": [ { - "ItemOID": "ODM.IT.DM.BRTHYR", - "Mandatory": "Yes" + "OID": "ODM.IT.VS.VSDAT", + "Name": "Date", + "DataType": "partialDate", + "Description": {"TranslatedText": [{ + "_content": "Date of measurements", + "lang": "en" + }]}, + "Question": {"TranslatedText": [{ + "_content": "Date", + "lang": "en" + }]}, + "Alias": [{ + "Context": "CDASH", + "Name": "VSDAT" + }] }, { - "ItemOID": "ODM.IT.DM.SEX", - "Mandatory": "Yes" + "OID": "ODM.IT.VS.BP.VSORRESU", + "Name": "BP Units", + "DataType": "text", + "Description": {"TranslatedText": [{ + "_content": "Result of the vital signs measurement as originally received or collected.", + "lang": "en" + }]}, + "Question": {"TranslatedText": [{ + "_content": "Diastolic", + "lang": "en" + }]}, + "Alias": [ + { + "Context": "CDASH", + "Name": "BP.DIABP.VSORRES" + }, + { + "Context": "CDASH/SDTM", + "Name": "VSORRES+VSORRESU" + } + ] } - ] - } - ], - "ItemDef": [ - { - "OID": "ODM.IT.VS.VSDAT", - "Name": "Date", - "DataType": "partialDate", - "Description": {"TranslatedText": [{ - "_content": "Date of measurements", - "lang": "en" - }]}, - "Question": {"TranslatedText": [{ - "_content": "Date", - "lang": "en" - }]}, - "Alias": [{ - "Context": "CDASH", - "Name": "VSDAT" - }] - }, - { - "OID": "ODM.IT.VS.BP.VSORRESU", - "Name": "BP Units", - "DataType": "text", - "Description": {"TranslatedText": [{ - "_content": "Result of the vital signs measurement as originally received or collected.", - "lang": "en" - }]}, - "Question": {"TranslatedText": [{ - "_content": "Diastolic", - "lang": "en" - }]}, - "Alias": [ - { - "Context": "CDASH", - "Name": "BP.DIABP.VSORRES" - }, - { - "Context": "CDASH/SDTM", - "Name": "VSORRES+VSORRESU" - } - ] - } - ], - "CodeList": [{ - "OID": "ODM.CL.NY_SUB_Y_N", - "Name": "No Yes Response", - "DataType": "text", - "CodeListItem": [ - { - "CodedValue": "N", - "Decode": {"TranslatedText": [{ - "_content": "No", + ], + "CodeList": [{ + "OID": "ODM.CL.NY_SUB_Y_N", + "Name": "No Yes Response", + "DataType": "text", + "CodeListItem": [ + { + "CodedValue": "N", + "Decode": {"TranslatedText": [{ + "_content": "No", + "lang": "en" + }]} + }, + { + "CodedValue": "Y", + "Decode": {"TranslatedText": [{ + "_content": "Yes", + "lang": "en" + }]} + } + ] + }], + "MethodDef": [{ + "OID": "ODM.MT.DOB", + "Name": "Create BRTHDTC from date ELEMENTS", + "Type": "Computation", + "Description": {"TranslatedText": [{ + "_content": "Concatenation of BRTHYR, BRTHMO, and BRTHDY in ISO 8601 format", "lang": "en" }]} - }, - { - "CodedValue": "Y", - "Decode": {"TranslatedText": [{ - "_content": "Yes", + }], + "ConditionDef": [{ + "OID": "ODM.CD.BRTHMO", + "Name": "Skip BRTHMO when no BRTHYR", + "Description": {"TranslatedText": [{ + "_content": "Skip the BRTHMO field when BRTHYR length NE 4", "lang": "en" }]} - } - ] - }], - "MethodDef": [{ - "OID": "ODM.MT.DOB", - "Name": "Create BRTHDTC from date ELEMENTS", - "Type": "Computation", - "Description": {"TranslatedText": [{ - "_content": "Concatenation of BRTHYR, BRTHMO, and BRTHDY in ISO 8601 format", - "lang": "en" - }]} - }], - "ConditionDef": [{ - "OID": "ODM.CD.BRTHMO", - "Name": "Skip BRTHMO when no BRTHYR", - "Description": {"TranslatedText": [{ - "_content": "Skip the BRTHMO field when BRTHYR length NE 4", - "lang": "en" - }]} - }] -} + }] + } diff --git a/tests/test_methodDef.py b/tests/test_methodDef.py index ddfe643..51941ce 100644 --- a/tests/test_methodDef.py +++ b/tests/test_methodDef.py @@ -9,12 +9,16 @@ def setUp(self) -> None: self.methoddef = ODM.MethodDef(**attrs) def test_add_formal_expression(self): - self.FormalExpression = [ODM.FormalExpression(Context="Python 3.7", _content="print('hello world')")] - self.assertEqual(self.FormalExpression[0]._content, "print('hello world')") + self.FormalExpression = [ODM.FormalExpression( + Context="Python 3.7", _content="print('hello world')")] + self.assertEqual( + self.FormalExpression[0]._content, "print('hello world')") def test_add_description(self): - tt1 = ODM.TranslatedText(_content="Age at Screening Date (Screening Date - Birth date)", lang="en") - tt2 = ODM.TranslatedText(_content="For the complete algorithm see the referenced external document.", lang="fr") + tt1 = ODM.TranslatedText( + _content="Age at Screening Date (Screening Date - Birth date)", lang="en") + tt2 = ODM.TranslatedText( + _content="For the complete algorithm see the referenced external document.", lang="fr") self.methoddef.Description = ODM.Description() self.methoddef.Description.TranslatedText = [tt1, tt2] self.assertEqual(len(self.methoddef.Description.TranslatedText), 2) @@ -28,10 +32,12 @@ def test_add_alias(self): def test_to_json(self): attrs = self.set_attributes() method = ODM.MethodDef(**attrs) - tt1 = ODM.TranslatedText(_content="Age at Screening Date (Screening Date - Birth date)", lang="en") + tt1 = ODM.TranslatedText( + _content="Age at Screening Date (Screening Date - Birth date)", lang="en") method.Description = ODM.Description() method.Description.TranslatedText = [tt1] - fex = ODM.FormalExpression(Context="Python 3.7", _content="print('hello world')") + fex = ODM.FormalExpression( + Context="Python 3.7", _content="print('hello world')") method.FormalExpression = [fex] method_dict = method.to_dict() print(method_dict) @@ -44,10 +50,12 @@ def test_to_dict(self): attrs = self.set_attributes() method = ODM.MethodDef(**attrs) desc = ODM.Description() - tt1 = ODM.TranslatedText(_content="Age at Screening Date (Screening Date - Birth date)", lang="en") + tt1 = ODM.TranslatedText( + _content="Age at Screening Date (Screening Date - Birth date)", lang="en") desc.TranslatedText = [tt1] method.Description = desc - fex = ODM.FormalExpression(Context="Python 3.7", _content="print('hello world')") + fex = ODM.FormalExpression( + Context="Python 3.7", _content="print('hello world')") method.FormalExpression = [fex] method_dict = method.to_dict() self.assertEqual(method_dict["OID"], "ODM.MT.AGE") @@ -57,17 +65,18 @@ def test_to_xml(self): attrs = self.set_attributes() method = ODM.MethodDef(**attrs) desc = ODM.Description() - tt1 = ODM.TranslatedText(_content="Age at Screening Date (Screening Date - Birth date)", lang="en") + tt1 = ODM.TranslatedText( + _content="Age at Screening Date (Screening Date - Birth date)", lang="en") desc.TranslatedText = [tt1] method.Description = desc - fex = ODM.FormalExpression(Context="Python 3.7", _content="print('hello world')") + fex = ODM.FormalExpression( + Context="Python 3.7", _content="print('hello world')") method.FormalExpression = [fex] method_xml = method.to_xml() self.assertEqual(method_xml.attrib["OID"], "ODM.MT.AGE") fex = method_xml.find("FormalExpression") self.assertEqual(fex.attrib["Context"], "Python 3.7") - def set_attributes(self): """ set some MethodDef element attributes using test data @@ -77,5 +86,6 @@ def set_attributes(self): def expected_dict(self): return {'OID': 'ODM.MT.AGE', 'Name': 'Algorithm to derive AGE', 'Type': 'Computation', 'Description': - {'TranslatedText': [{'_content': 'Age at Screening Date (Screening Date - Birth date)', 'lang': 'en'}]}, - 'FormalExpression': [{'Context': 'Python 3.7', '_content': "print('hello world')"}]} + {'TranslatedText': [ + {'_content': 'Age at Screening Date (Screening Date - Birth date)', 'lang': 'en'}]}, + 'FormalExpression': [{'Context': 'Python 3.7', '_content': "print('hello world')"}]} diff --git a/tests/test_methodDef_check.py b/tests/test_methodDef_check.py index 2c47334..6093254 100644 --- a/tests/test_methodDef_check.py +++ b/tests/test_methodDef_check.py @@ -14,14 +14,18 @@ def test_no_description(self): validator = METADATA.MetadataSchema() results = tt.verify_conformance(validator) - self.methoddef.FormalExpression = [ODM.FormalExpression(Context="Python 3.7", _content="print('hello world')")] - self.assertEqual(self.methoddef.FormalExpression[0]._content, "print('hello world')") + self.methoddef.FormalExpression = [ODM.FormalExpression( + Context="Python 3.7", _content="print('hello world')")] + self.assertEqual( + self.methoddef.FormalExpression[0]._content, "print('hello world')") with self.assertRaises(ValueError): results = self.methoddef.verify_conformance(validator) def test_add_description(self): - tt1 = ODM.TranslatedText(_content="Age at Screening Date (Screening Date - Birth date)", lang="en") - tt2 = ODM.TranslatedText(_content="For the complete algorithm see the referenced external document.", lang="fr") + tt1 = ODM.TranslatedText( + _content="Age at Screening Date (Screening Date - Birth date)", lang="en") + tt2 = ODM.TranslatedText( + _content="For the complete algorithm see the referenced external document.", lang="fr") self.methoddef.Description = ODM.Description() self.methoddef.Description.TranslatedText = [tt1, tt2] self.assertEqual(len(self.methoddef.Description.TranslatedText), 2) @@ -35,10 +39,12 @@ def test_add_alias(self): def test_to_json(self): attrs = self.set_attributes() method = ODM.MethodDef(**attrs) - tt1 = ODM.TranslatedText(_content="Age at Screening Date (Screening Date - Birth date)", lang="en") + tt1 = ODM.TranslatedText( + _content="Age at Screening Date (Screening Date - Birth date)", lang="en") method.Description = ODM.Description() method.Description.TranslatedText = [tt1] - fex = ODM.FormalExpression(Context="Python 3.7", _content="print('hello world')") + fex = ODM.FormalExpression( + Context="Python 3.7", _content="print('hello world')") method.FormalExpression = [fex] method_dict = method.to_dict() print(method_dict) @@ -51,10 +57,12 @@ def test_to_dict(self): attrs = self.set_attributes() method = ODM.MethodDef(**attrs) desc = ODM.Description() - tt1 = ODM.TranslatedText(_content="Age at Screening Date (Screening Date - Birth date)", lang="en") + tt1 = ODM.TranslatedText( + _content="Age at Screening Date (Screening Date - Birth date)", lang="en") desc.TranslatedText = [tt1] method.Description = desc - fex = ODM.FormalExpression(Context="Python 3.7", _content="print('hello world')") + fex = ODM.FormalExpression( + Context="Python 3.7", _content="print('hello world')") method.FormalExpression = [fex] method_dict = method.to_dict() self.assertEqual(method_dict["OID"], "ODM.MT.AGE") @@ -64,17 +72,18 @@ def test_to_xml(self): attrs = self.set_attributes() method = ODM.MethodDef(**attrs) desc = ODM.Description() - tt1 = ODM.TranslatedText(_content="Age at Screening Date (Screening Date - Birth date)", lang="en") + tt1 = ODM.TranslatedText( + _content="Age at Screening Date (Screening Date - Birth date)", lang="en") desc.TranslatedText = [tt1] method.Description = desc - fex = ODM.FormalExpression(Context="Python 3.7", _content="print('hello world')") + fex = ODM.FormalExpression( + Context="Python 3.7", _content="print('hello world')") method.FormalExpression = [fex] method_xml = method.to_xml() self.assertEqual(method_xml.attrib["OID"], "ODM.MT.AGE") fex = method_xml.find("FormalExpression") self.assertEqual(fex.attrib["Context"], "Python 3.7") - def set_attributes(self): """ set some MethodDef element attributes using test data @@ -84,5 +93,6 @@ def set_attributes(self): def expected_dict(self): return {'OID': 'ODM.MT.AGE', 'Name': 'Algorithm to derive AGE', 'Type': 'Computation', 'Description': - {'TranslatedText': [{'_content': 'Age at Screening Date (Screening Date - Birth date)', 'lang': 'en'}]}, - 'FormalExpression': [{'Context': 'Python 3.7', '_content': "print('hello world')"}]} + {'TranslatedText': [ + {'_content': 'Age at Screening Date (Screening Date - Birth date)', 'lang': 'en'}]}, + 'FormalExpression': [{'Context': 'Python 3.7', '_content': "print('hello world')"}]} diff --git a/tests/test_methodDef_define.py b/tests/test_methodDef_define.py index 5f8da28..ec99a57 100644 --- a/tests/test_methodDef_define.py +++ b/tests/test_methodDef_define.py @@ -9,12 +9,16 @@ def setUp(self) -> None: self.methoddef = DEFINE.MethodDef(**attrs) def test_add_formal_expression(self): - self.FormalExpression = [DEFINE.FormalExpression(Context="Python 3.7", _content="print('hello world')")] - self.assertEqual(self.FormalExpression[0]._content, "print('hello world')") + self.FormalExpression = [DEFINE.FormalExpression( + Context="Python 3.7", _content="print('hello world')")] + self.assertEqual( + self.FormalExpression[0]._content, "print('hello world')") def test_add_description(self): - tt1 = DEFINE.TranslatedText(_content="Age at Screening Date (Screening Date - Birth date)", lang="en") - tt2 = DEFINE.TranslatedText(_content="For the complete algorithm see the referenced external document.", lang="fr") + tt1 = DEFINE.TranslatedText( + _content="Age at Screening Date (Screening Date - Birth date)", lang="en") + tt2 = DEFINE.TranslatedText( + _content="For the complete algorithm see the referenced external document.", lang="fr") self.methoddef.Description = DEFINE.Description() self.methoddef.Description.TranslatedText = [tt1, tt2] self.assertEqual(len(self.methoddef.Description.TranslatedText), 2) @@ -28,10 +32,12 @@ def test_add_alias(self): def test_to_json(self): attrs = self.set_attributes() method = DEFINE.MethodDef(**attrs) - tt1 = DEFINE.TranslatedText(_content="Age at Screening Date (Screening Date - Birth date)", lang="en") + tt1 = DEFINE.TranslatedText( + _content="Age at Screening Date (Screening Date - Birth date)", lang="en") method.Description = DEFINE.Description() method.Description.TranslatedText = [tt1] - fex = DEFINE.FormalExpression(Context="Python 3.7", _content="print('hello world')") + fex = DEFINE.FormalExpression( + Context="Python 3.7", _content="print('hello world')") method.FormalExpression = [fex] method_dict = method.to_dict() print(method_dict) @@ -44,10 +50,12 @@ def test_to_dict(self): attrs = self.set_attributes() method = DEFINE.MethodDef(**attrs) desc = DEFINE.Description() - tt1 = DEFINE.TranslatedText(_content="Age at Screening Date (Screening Date - Birth date)", lang="en") + tt1 = DEFINE.TranslatedText( + _content="Age at Screening Date (Screening Date - Birth date)", lang="en") desc.TranslatedText = [tt1] method.Description = desc - fex = DEFINE.FormalExpression(Context="Python 3.7", _content="print('hello world')") + fex = DEFINE.FormalExpression( + Context="Python 3.7", _content="print('hello world')") method.FormalExpression = [fex] method_dict = method.to_dict() self.assertEqual(method_dict["OID"], "ODM.MT.AGE") @@ -57,17 +65,18 @@ def test_to_xml(self): attrs = self.set_attributes() method = DEFINE.MethodDef(**attrs) desc = DEFINE.Description() - tt1 = DEFINE.TranslatedText(_content="Age at Screening Date (Screening Date - Birth date)", lang="en") + tt1 = DEFINE.TranslatedText( + _content="Age at Screening Date (Screening Date - Birth date)", lang="en") desc.TranslatedText = [tt1] method.Description = desc - fex = DEFINE.FormalExpression(Context="Python 3.7", _content="print('hello world')") + fex = DEFINE.FormalExpression( + Context="Python 3.7", _content="print('hello world')") method.FormalExpression = [fex] method_xml = method.to_xml() self.assertEqual(method_xml.attrib["OID"], "ODM.MT.AGE") fex = method_xml.find("FormalExpression") self.assertEqual(fex.attrib["Context"], "Python 3.7") - def set_attributes(self): """ set some MethodDef element attributes using test data @@ -77,5 +86,6 @@ def set_attributes(self): def expected_dict(self): return {'OID': 'ODM.MT.AGE', 'Name': 'Algorithm to derive AGE', 'Type': 'Computation', 'Description': - {'TranslatedText': [{'_content': 'Age at Screening Date (Screening Date - Birth date)', 'lang': 'en'}]}, - 'FormalExpression': [{'Context': 'Python 3.7', '_content': "print('hello world')"}]} + {'TranslatedText': [ + {'_content': 'Age at Screening Date (Screening Date - Birth date)', 'lang': 'en'}]}, + 'FormalExpression': [{'Context': 'Python 3.7', '_content': "print('hello world')"}]} diff --git a/tests/test_ns_registry.py b/tests/test_ns_registry.py index 642e865..6641424 100644 --- a/tests/test_ns_registry.py +++ b/tests/test_ns_registry.py @@ -9,39 +9,52 @@ class TestNamespaceRegistry(TestCase): def setUp(self) -> None: - NS.NamespaceRegistry(prefix="odm", uri="http://www.cdisc.org/ns/odm/v1.3", is_default=True, is_reset=True) - NS.NamespaceRegistry(prefix="xs", uri="http://www.w3.org/2001/XMLSchema-instance") - NS.NamespaceRegistry(prefix="xml", uri="http://www.w3.org/XML/1998/namespace") - NS.NamespaceRegistry(prefix="xlink", uri="http://www.w3.org/1999/xlink") + NS.NamespaceRegistry( + prefix="odm", uri="http://www.cdisc.org/ns/odm/v1.3", is_default=True, is_reset=True) + NS.NamespaceRegistry( + prefix="xs", uri="http://www.w3.org/2001/XMLSchema-instance") + NS.NamespaceRegistry( + prefix="xml", uri="http://www.w3.org/XML/1998/namespace") + NS.NamespaceRegistry( + prefix="xlink", uri="http://www.w3.org/1999/xlink") def test_get_ns_prefix(self): - nsr = NS.NamespaceRegistry(prefix="def", uri="http://www.cdisc.org/ns/def/v2.0") - def_prefix = nsr.get_prefix_ns_from_uri("http://www.cdisc.org/ns/def/v2.0") + nsr = NS.NamespaceRegistry( + prefix="def", uri="http://www.cdisc.org/ns/def/v2.0") + def_prefix = nsr.get_prefix_ns_from_uri( + "http://www.cdisc.org/ns/def/v2.0") self.assertEqual("def", def_prefix) def test_get_ns_prefix_missing(self): - nsr = NS.NamespaceRegistry(prefix="def", uri="http://www.cdisc.org/ns/def/v2.0") + nsr = NS.NamespaceRegistry( + prefix="def", uri="http://www.cdisc.org/ns/def/v2.0") with self.assertRaises(ValueError): - def_prefix = nsr.get_prefix_ns_from_uri("http://www.cdisc.org/ns/def/v3.0") + def_prefix = nsr.get_prefix_ns_from_uri( + "http://www.cdisc.org/ns/def/v3.0") def test_get_ns_entry_dict(self): - nsr = NS.NamespaceRegistry(prefix="def", uri="http://www.cdisc.org/ns/def/v2.0") + nsr = NS.NamespaceRegistry( + prefix="def", uri="http://www.cdisc.org/ns/def/v2.0") def_ns_dict = nsr.get_ns_entry_dict("def") expected_dict = {"def": "http://www.cdisc.org/ns/def/v2.0"} self.assertDictEqual(expected_dict, def_ns_dict) def test_get_ns_entry_dict_missing(self): - nsr = NS.NamespaceRegistry(prefix="def", uri="http://www.cdisc.org/ns/def/v2.0") + nsr = NS.NamespaceRegistry( + prefix="def", uri="http://www.cdisc.org/ns/def/v2.0") missing_ns_dict = nsr.get_ns_entry_dict("ct") expected_dict = {} self.assertDictEqual(expected_dict, missing_ns_dict) def test_update_registry(self): - nsr = NS.NamespaceRegistry(prefix="def", uri="http://www.cdisc.org/ns/def/v2.0") + nsr = NS.NamespaceRegistry( + prefix="def", uri="http://www.cdisc.org/ns/def/v2.0") expected_ns = self.get_ns_defaults() - expected_ns.update({'odm': 'http://www.cdisc.org/ns/odm/v1.3', 'def': 'http://www.cdisc.org/ns/def/v2.0'}) + expected_ns.update({'odm': 'http://www.cdisc.org/ns/odm/v1.3', + 'def': 'http://www.cdisc.org/ns/def/v2.0'}) self.assertDictEqual(expected_ns, nsr.namespaces) - nsr = NS.NamespaceRegistry(prefix="nciodm", uri="http://ncicb.nci.nih.gov/xml/odm/EVS/CDISC") + nsr = NS.NamespaceRegistry( + prefix="nciodm", uri="http://ncicb.nci.nih.gov/xml/odm/EVS/CDISC") expected_ns = self.get_ns_defaults() expected_ns.update({'odm': 'http://www.cdisc.org/ns/odm/v1.3', 'def': 'http://www.cdisc.org/ns/def/v2.0', 'nciodm': 'http://ncicb.nci.nih.gov/xml/odm/EVS/CDISC'}) @@ -55,8 +68,10 @@ def test_use_existing_registry(self): def test_remove_registry_entry(self): # tests a singleton so each test builds on the previous one - NS.NamespaceRegistry(prefix="def", uri="http://www.cdisc.org/ns/def/v2.0") - nsr = NS.NamespaceRegistry(prefix="nciodm", uri="http://ncicb.nci.nih.gov/xml/odm/EVS/CDISC") + NS.NamespaceRegistry( + prefix="def", uri="http://www.cdisc.org/ns/def/v2.0") + nsr = NS.NamespaceRegistry( + prefix="nciodm", uri="http://ncicb.nci.nih.gov/xml/odm/EVS/CDISC") expected_ns = self.get_ns_defaults() expected_ns.update({'odm': 'http://www.cdisc.org/ns/odm/v1.3', 'def': 'http://www.cdisc.org/ns/def/v2.0', @@ -69,17 +84,22 @@ def test_remove_registry_entry(self): self.assertDictEqual(expected_ns, nsr.namespaces) def test_default_ns(self): - NS.NamespaceRegistry(prefix="def", uri="http://www.cdisc.org/ns/def/v2.0") - nsr = NS.NamespaceRegistry(prefix="nciodm", uri="http://ncicb.nci.nih.gov/xml/odm/EVS/CDISC") + NS.NamespaceRegistry( + prefix="def", uri="http://www.cdisc.org/ns/def/v2.0") + nsr = NS.NamespaceRegistry( + prefix="nciodm", uri="http://ncicb.nci.nih.gov/xml/odm/EVS/CDISC") expected_ns = self.get_ns_defaults() expected_ns.update({'odm': 'http://www.cdisc.org/ns/odm/v1.3', 'def': 'http://www.cdisc.org/ns/def/v2.0', 'nciodm': 'http://ncicb.nci.nih.gov/xml/odm/EVS/CDISC'}) self.assertDictEqual(expected_ns, nsr.namespaces) - self.assertDictEqual({'odm': 'http://www.cdisc.org/ns/odm/v1.3'}, nsr.default_namespace) + self.assertDictEqual( + {'odm': 'http://www.cdisc.org/ns/odm/v1.3'}, nsr.default_namespace) def test_odm_ns_entries(self): - NS.NamespaceRegistry(prefix="def", uri="http://www.cdisc.org/ns/def/v2.0") - nsr = NS.NamespaceRegistry(prefix="nciodm", uri="http://ncicb.nci.nih.gov/xml/odm/EVS/CDISC") + NS.NamespaceRegistry( + prefix="def", uri="http://www.cdisc.org/ns/def/v2.0") + nsr = NS.NamespaceRegistry( + prefix="nciodm", uri="http://ncicb.nci.nih.gov/xml/odm/EVS/CDISC") expected_ns = self.get_ns_defaults() expected_ns.update({'odm': 'http://www.cdisc.org/ns/odm/v1.3', 'def': 'http://www.cdisc.org/ns/def/v2.0', 'nciodm': 'http://ncicb.nci.nih.gov/xml/odm/EVS/CDISC'}) @@ -103,19 +123,25 @@ def test_prefix_without_url(self): def test_bad_ns_url(self): with self.assertRaises(ValueError): - nsr = NS.NamespaceRegistry(prefix="def", uri="www.cdisc.org/ns/def/v2.0") + nsr = NS.NamespaceRegistry( + prefix="def", uri="www.cdisc.org/ns/def/v2.0") def test_odm_ns_attributes(self): - odm_test_file = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'data', 'test_odm_namespace.xml') - NS.NamespaceRegistry(prefix="def", uri="http://www.cdisc.org/ns/def/v2.0") - NS.NamespaceRegistry(prefix="nciodm", uri="http://ncicb.nci.nih.gov/xml/odm/EVS/CDISC") - NS.NamespaceRegistry(prefix="xsi", uri="http://www.w3.org/2001/XMLSchema-instance") + odm_test_file = os.path.join(os.path.dirname( + os.path.realpath(__file__)), 'data', 'test_odm_namespace.xml') + NS.NamespaceRegistry( + prefix="def", uri="http://www.cdisc.org/ns/def/v2.0") + NS.NamespaceRegistry( + prefix="nciodm", uri="http://ncicb.nci.nih.gov/xml/odm/EVS/CDISC") + NS.NamespaceRegistry( + prefix="xsi", uri="http://www.w3.org/2001/XMLSchema-instance") root = self.add_root() odm_xml = root.to_xml() nsr = NS.NamespaceRegistry() nsr.set_odm_namespace_attributes(odm_xml) tree = ET.ElementTree(odm_xml) - tree.write(odm_test_file, xml_declaration=True, encoding='utf-8', method='xml', short_empty_elements=True) + tree.write(odm_test_file, xml_declaration=True, + encoding='utf-8', method='xml', short_empty_elements=True) self.assertEqual("ODM.MDV.TEST.001", root.FileOID) loader = LD.ODMLoader(DL.XMLDefineLoader()) odm_root = loader.open_odm_document(odm_test_file) @@ -133,9 +159,6 @@ def test_odm_ns_attributes(self): 'xmlns:xsi=http://www.w3.org/2001/XMLSchema-instance'] self.assertListEqual(namespaces, expected_ns_list) - - - def add_root(self): attrs = self.get_root_attributes() root = ODM.ODM(**attrs) diff --git a/tests/test_odm_json_string_parser.py b/tests/test_odm_json_string_parser.py index f54d2c1..6782bac 100644 --- a/tests/test_odm_json_string_parser.py +++ b/tests/test_odm_json_string_parser.py @@ -1,11 +1,13 @@ import unittest import os import odmlib.odm_parser as P +from tests import get_data_file_path class TestOdmParserMetaData(unittest.TestCase): def setUp(self) -> None: - self.odm_file_1 = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'data', 'cdash_odm_test.json') + # this file is generated sometimes + self.odm_file_1 = get_data_file_path("cdash_odm_test.json") with open(self.odm_file_1, "r", encoding="utf-8") as f: self.odm_string = f.read() self.parser = P.ODMJSONStringParser(self.odm_string) diff --git a/tests/test_odm_loader.py b/tests/test_odm_loader.py index 430f728..a51eaac 100644 --- a/tests/test_odm_loader.py +++ b/tests/test_odm_loader.py @@ -3,12 +3,22 @@ import odmlib.loader as LD import os +from tests import get_data_file_path + class TestODMLoader(TestCase): def setUp(self) -> None: - self.odm_file = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'data', 'cdash-odm-test.xml') + self.odm_file = get_data_file_path("cdash-odm-test.xml") self.loader = LD.ODMLoader(OL.XMLODMLoader()) + def tearDown(self) -> None: + odm_json_file = get_data_file_path("cdash_odm_test.json", check_exists=False) + if os.path.exists(odm_json_file): + os.remove(odm_json_file) + odm_xml_file = get_data_file_path("cdash_odm_test_roundtrip.xml", check_exists=False) + if os.path.exists(odm_xml_file): + os.remove(odm_xml_file) + def test_open_odm_document(self): root = self.loader.open_odm_document(self.odm_file) elem_name = root.tag[root.tag.find('}') + 1:] @@ -58,13 +68,13 @@ def test_odm_round_trip(self): root = self.loader.open_odm_document(self.odm_file) odm = self.loader.create_odmlib(root) odm_json = odm.to_json() - odm_json_file = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'data', 'cdash_odm_test.json') + odm_json_file = get_data_file_path("cdash_odm_test.json", check_exists=False) with open(odm_json_file, "w") as odm_in: odm_in.write(odm_json) json_loader = LD.ODMLoader(OL.JSONODMLoader()) odm_dict = json_loader.open_odm_document(odm_json_file) rt_odm = json_loader.create_odmlib(odm_dict, "ODM") - odm_xml_file = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'data', 'cdash_odm_test_roundtrip.xml') + odm_xml_file = get_data_file_path("cdash_odm_test_roundtrip.xml", check_exists=False) rt_odm.write_xml(odm_xml_file) root2 = self.loader.open_odm_document(odm_xml_file) odm2 = self.loader.create_odmlib(root2) diff --git a/tests/test_odm_loader_define.py b/tests/test_odm_loader_define.py index 78064d6..7b00eb7 100644 --- a/tests/test_odm_loader_define.py +++ b/tests/test_odm_loader_define.py @@ -3,11 +3,13 @@ import odmlib.loader as LD import os +from tests import get_data_file_path + class TestODMLoader(TestCase): def setUp(self) -> None: - self.odm_file = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'data', 'define2-0-0-sdtm-test.xml') - self.odm_file_json = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'data', 'sdtm_define2_test.json') + self.odm_file = get_data_file_path("define2-0-0-sdtm-test.xml") + self.odm_file_json = get_data_file_path("sdtm_define2_test.json") self.loader = LD.ODMLoader(OL.XMLDefineLoader()) self.jloader = LD.ODMLoader(OL.JSONDefineLoader()) @@ -89,13 +91,13 @@ def test_odm_round_trip(self): root = self.loader.open_odm_document(self.odm_file) odm = self.loader.create_odmlib(root) odm_json = odm.to_json() - odm_json_file = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'data', 'sdtm_define2_test.json') + odm_json_file = get_data_file_path("sdtm_define2_test.json") with open(odm_json_file, "w") as odm_in: odm_in.write(odm_json) json_loader = LD.ODMLoader(OL.JSONDefineLoader()) odm_dict = json_loader.open_odm_document(odm_json_file) rt_odm = json_loader.create_odmlib(odm_dict, "ODM") - def_xml_file = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'data', 'sdtm_def_test_roundtrip.xml') + def_xml_file = get_data_file_path("sdtm_def_test_roundtrip.xml") rt_odm.write_xml(def_xml_file) root2 = self.loader.open_odm_document(def_xml_file) odm2 = self.loader.create_odmlib(root2) diff --git a/tests/test_odm_loader_define_string.py b/tests/test_odm_loader_define_string.py index e780654..134c649 100644 --- a/tests/test_odm_loader_define_string.py +++ b/tests/test_odm_loader_define_string.py @@ -3,10 +3,12 @@ import odmlib.loader as LD import os +from tests import get_data_file_path + class TestDefineLoaderString(TestCase): def setUp(self) -> None: - self.odm_file = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'data', 'define2-0-0-sdtm-test.xml') + self.odm_file = get_data_file_path("define2-0-0-sdtm-test.xml") with open(self.odm_file, "r", encoding="utf-8") as f: self.odm_string = f.read() self.loader = LD.ODMLoader(OL.XMLDefineLoader()) @@ -43,13 +45,13 @@ def test_odm_round_trip(self): root = self.loader.load_odm_string(self.odm_string) odm = self.loader.create_odmlib(root) odm_json = odm.to_json() - odm_json_file = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'data', 'sdtm_define2_test.json') + odm_json_file = get_data_file_path("sdtm_define2_test.json") with open(odm_json_file, "w") as odm_in: odm_in.write(odm_json) json_loader = LD.ODMLoader(OL.JSONDefineLoader()) odm_dict = json_loader.open_odm_document(odm_json_file) rt_odm = json_loader.create_odmlib(odm_dict, "ODM") - def_xml_file = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'data', 'sdtm_def_test_roundtrip.xml') + def_xml_file = get_data_file_path("sdtm_def_test_roundtrip.xml") rt_odm.write_xml(def_xml_file) root2 = self.loader.open_odm_document(def_xml_file) odm2 = self.loader.create_odmlib(root2) diff --git a/tests/test_odm_loader_string.py b/tests/test_odm_loader_string.py index c8f1706..321ea45 100644 --- a/tests/test_odm_loader_string.py +++ b/tests/test_odm_loader_string.py @@ -3,14 +3,16 @@ import odmlib.loader as LD import os +from tests import get_data_file_path + class TestODMLoaderString(TestCase): def setUp(self) -> None: - self.odm_file = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'data', 'cdash-odm-test.xml') + self.odm_file = get_data_file_path("cdash-odm-test.xml") with open(self.odm_file, "r", encoding="utf-8") as f: self.odm_string = f.read() self.loader = LD.ODMLoader(OL.XMLODMLoader()) - + def test_open_odm_document(self): root = self.loader.load_odm_string(self.odm_string) elem_name = root.tag[root.tag.find('}') + 1:] @@ -45,13 +47,13 @@ def test_odm_round_trip(self): root = self.loader.load_odm_string(self.odm_string) odm = self.loader.create_odmlib(root) odm_json = odm.to_json() - odm_json_file = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'data', 'cdash_odm_test.json') + odm_json_file = get_data_file_path("cdash_odm_test.json", check_exists=False) with open(odm_json_file, "w") as odm_in: odm_in.write(odm_json) json_loader = LD.ODMLoader(OL.JSONODMLoader()) odm_dict = json_loader.open_odm_document(odm_json_file) rt_odm = json_loader.create_odmlib(odm_dict, "ODM") - odm_xml_file = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'data', 'cdash_odm_test_roundtrip.xml') + odm_xml_file = get_data_file_path("cdash_odm_test_roundtrip.xml", check_exists=False) rt_odm.write_xml(odm_xml_file) root2 = self.loader.open_odm_document(odm_xml_file) odm2 = self.loader.create_odmlib(root2) diff --git a/tests/test_odm_parser_metadata.py b/tests/test_odm_parser_metadata.py index 52551f5..0e9d3c2 100644 --- a/tests/test_odm_parser_metadata.py +++ b/tests/test_odm_parser_metadata.py @@ -2,25 +2,29 @@ import os import odmlib.odm_parser as P from xml.etree.ElementTree import Element + +from tests import get_data_file_path ODM_NS = "{http://www.cdisc.org/ns/odm/v1.3}" class TestOdmParserMetaData(unittest.TestCase): def setUp(self) -> None: - self.odm_file_1 = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'data', 'cdash-odm-test.xml') + self.odm_file_1 = get_data_file_path("cdash-odm-test.xml") self.parser = P.ODMParser(self.odm_file_1) self.root = self.parser.parse() self.mdv = self.parser.MetaDataVersion() def test_MetaDataVersion(self): self.assertTrue(isinstance(self.mdv, list)) - self.assertDictEqual(self.mdv[0].attrib, {"Name": "TRACE-XML MDV", "OID": "MDV.TRACE-XML-ODM-01"}) + self.assertDictEqual(self.mdv[0].attrib, { + "Name": "TRACE-XML MDV", "OID": "MDV.TRACE-XML-ODM-01"}) self.assertEqual(81, len([e.tag for e in self.mdv[0]])) def test_Protocol(self): protocol = self.parser.Protocol(parent=self.mdv[0]) self.assertIsInstance(protocol[0]["elem"], Element) - self.assertListEqual([ODM_NS + "StudyEventRef"], [e.tag for e in protocol[0]["elem"]]) + self.assertListEqual([ODM_NS + "StudyEventRef"], + [e.tag for e in protocol[0]["elem"]]) def test_StudyEventRef(self): protocol = self.parser.Protocol(parent=self.mdv[0]) @@ -48,17 +52,16 @@ def test_FormDef(self): def test_ItemGroupDef(self): ig = self.parser.ItemGroupDef(parent=self.mdv[0]) self.assertEqual(len(ig), 7) - self.assertDictEqual({'Name': 'Demographics', 'OID': 'ODM.IG.DM', 'Repeating': 'No'}, ig[1]['elem'].attrib) + self.assertDictEqual( + {'Name': 'Demographics', 'OID': 'ODM.IG.DM', 'Repeating': 'No'}, ig[1]['elem'].attrib) self.assertEqual(ig[3]["OID"], "ODM.IG.VS") def test_ItemRef(self): ig = self.parser.ItemGroupDef(parent=self.mdv[0]) ir = self.parser.ItemRef(parent=ig[0]['elem']) self.assertEqual(len(ir), 4) - self.assertDictEqual({'ItemOID': 'ODM.IT.Common.SubjectID', 'Mandatory': 'Yes'}, ir[2]['elem'].attrib) - - - + self.assertDictEqual( + {'ItemOID': 'ODM.IT.Common.SubjectID', 'Mandatory': 'Yes'}, ir[2]['elem'].attrib) if __name__ == '__main__': diff --git a/tests/test_odm_root.py b/tests/test_odm_root.py index 50efb61..8e82bb2 100644 --- a/tests/test_odm_root.py +++ b/tests/test_odm_root.py @@ -5,15 +5,20 @@ import os import odmlib.odm_loader as OL import odmlib.loader as LD +from tests import get_data_file_path class TestODM(TestCase): def setUp(self) -> None: self.odm = self.add_root() - self.odm_test_file = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'data', 'test_odm_001.xml') - self.odm_writer_test_file = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'data', 'test_odm_002.xml') + self.odm_test_file = get_data_file_path("test_odm_001.xml") + self.odm_writer_test_file = get_data_file_path("test_odm_002.xml", check_exists=False) self.loader = LD.ODMLoader(OL.XMLODMLoader()) + def tearDown(self) -> None: + if os.path.exists(self.odm_writer_test_file): + os.remove(self.odm_writer_test_file) + def test_odm_attributes(self): self.assertEqual("ODM.MDV.TEST.001", self.odm.FileOID) self.assertEqual("Metadata", self.odm.Granularity) @@ -41,7 +46,6 @@ def test_odm_dict(self): study = self.add_study() self.odm.Study = [study] odm_dict = self.odm.to_dict() - print(odm_dict) self.assertDictEqual(self.expected_dict(), odm_dict) def write_odm_file(self, odm_xml): @@ -54,7 +58,8 @@ def set_datetime(self): def add_study(self): study_name = ODM.StudyName(_content="ODM XML Test Study Name") protocol_name = ODM.ProtocolName(_content="ODM XML Test Study") - study_description = ODM.StudyDescription(_content="Testing the generation of an ODM XML file") + study_description = ODM.StudyDescription( + _content="Testing the generation of an ODM XML file") gv = ODM.GlobalVariables() gv.StudyName = study_name gv.StudyDescription = study_description @@ -92,18 +97,22 @@ def add_mdv(self): return mdv def add_CD(self): - tt1 = ODM.TranslatedText(_content="Skip the BRTHMO field when BRTHYR length NE 4", lang="en") + tt1 = ODM.TranslatedText( + _content="Skip the BRTHMO field when BRTHYR length NE 4", lang="en") desc = ODM.Description() desc.TranslatedText = [tt1] - cd = ODM.ConditionDef(OID="ODM.CD.BRTHMO", Name="Skip BRTHMO when no BRTHYR") + cd = ODM.ConditionDef(OID="ODM.CD.BRTHMO", + Name="Skip BRTHMO when no BRTHYR") cd.Description = desc return [cd] def add_MD(self): - tt1 = ODM.TranslatedText(_content="Concatenation of BRTHYR, BRTHMO, and BRTHDY in ISO 8601 format", lang="en") + tt1 = ODM.TranslatedText( + _content="Concatenation of BRTHYR, BRTHMO, and BRTHDY in ISO 8601 format", lang="en") desc = ODM.Description() desc.TranslatedText = [tt1] - md = ODM.MethodDef(OID="ODM.MT.DOB", Name="Create BRTHDTC from date ELEMENTS", Type="Computation") + md = ODM.MethodDef( + OID="ODM.MT.DOB", Name="Create BRTHDTC from date ELEMENTS", Type="Computation") md.Description = desc return [md] @@ -118,11 +127,11 @@ def add_CL(self): dc2.TranslatedText = [tt2] cli2 = ODM.CodeListItem(CodedValue="Y") cli2.Decode = dc2 - cl = ODM.CodeList(OID="ODM.CL.NY_SUB_Y_N", Name="No Yes Response", DataType="text") + cl = ODM.CodeList(OID="ODM.CL.NY_SUB_Y_N", + Name="No Yes Response", DataType="text") cl.CodeListItem = [cli1, cli2] return [cl] - def add_ITD(self): # ItemDef 1 ttd1 = ODM.TranslatedText(_content="Date of measurements", lang="en") @@ -132,12 +141,14 @@ def add_ITD(self): q1 = ODM.Question() q1.TranslatedText = [ttq1] a1 = ODM.Alias(Context="CDASH", Name="VSDAT") - itd1 = ODM.ItemDef(OID="ODM.IT.VS.VSDAT", Name="Date", DataType="partialDate") + itd1 = ODM.ItemDef(OID="ODM.IT.VS.VSDAT", + Name="Date", DataType="partialDate") itd1.Description = desc1 itd1.Question = q1 itd1.Alias = [a1] # ItemDef 2 - ttd2 = ODM.TranslatedText(_content="Result of the vital signs measurement as originally received or collected.", lang="en") + ttd2 = ODM.TranslatedText( + _content="Result of the vital signs measurement as originally received or collected.", lang="en") ttq2 = ODM.TranslatedText(_content="Diastolic", lang="en") desc2 = ODM.Description() desc2.TranslatedText = [ttd2] @@ -145,7 +156,8 @@ def add_ITD(self): q2.TranslatedText = [ttq2] a2a = ODM.Alias(Context="CDASH", Name="BP.DIABP.VSORRES") a2b = ODM.Alias(Context="CDASH/SDTM", Name="VSORRES+VSORRESU") - itd2 = ODM.ItemDef(OID="ODM.IT.VS.BP.VSORRESU", Name="BP Units", DataType="text") + itd2 = ODM.ItemDef(OID="ODM.IT.VS.BP.VSORRESU", + Name="BP Units", DataType="text") itd2.Description = desc2 itd2.Question = q2 itd2.Alias = [a2a, a2b] @@ -153,24 +165,33 @@ def add_ITD(self): def add_IGD(self): itr1 = ODM.ItemRef(ItemOID="ODM.IT.VS.VSDAT", Mandatory="Yes") - itr2 = ODM.ItemRef(ItemOID="ODM.IT.VS.BP.DIABP.VSORRES", Mandatory="Yes") - itr3 = ODM.ItemRef(ItemOID="ODM.IT.VS.BP.SYSBP.VSORRES", Mandatory="Yes") - igd1 = ODM.ItemGroupDef(OID="ODM.IG.VS", Name="Vital Sign Measurement", Repeating="Yes") + itr2 = ODM.ItemRef( + ItemOID="ODM.IT.VS.BP.DIABP.VSORRES", Mandatory="Yes") + itr3 = ODM.ItemRef( + ItemOID="ODM.IT.VS.BP.SYSBP.VSORRES", Mandatory="Yes") + igd1 = ODM.ItemGroupDef( + OID="ODM.IG.VS", Name="Vital Sign Measurement", Repeating="Yes") igd1.ItemRef = [itr1, itr2, itr3] igr4 = ODM.ItemRef(ItemOID="ODM.IT.DM.BRTHYR", Mandatory="Yes") igr5 = ODM.ItemRef(ItemOID="ODM.IT.DM.SEX", Mandatory="Yes") - igd2 = ODM.ItemGroupDef(OID="ODM.IG.DM", Name="Demographics", Repeating="No") + igd2 = ODM.ItemGroupDef( + OID="ODM.IG.DM", Name="Demographics", Repeating="No") igd2.ItemRef = [igr4, igr5] return [igd1, igd2] def add_FD(self): - igr1 = ODM.ItemGroupRef(ItemGroupOID="ODM.IG.Common", Mandatory="Yes", OrderNumber=1) - igr2 = ODM.ItemGroupRef(ItemGroupOID="ODM.IG.VS_GENERAL", Mandatory="Yes", OrderNumber=2) - igr3 = ODM.ItemGroupRef(ItemGroupOID="ODM.IG.VS", Mandatory="Yes", OrderNumber=3) + igr1 = ODM.ItemGroupRef( + ItemGroupOID="ODM.IG.Common", Mandatory="Yes", OrderNumber=1) + igr2 = ODM.ItemGroupRef( + ItemGroupOID="ODM.IG.VS_GENERAL", Mandatory="Yes", OrderNumber=2) + igr3 = ODM.ItemGroupRef(ItemGroupOID="ODM.IG.VS", + Mandatory="Yes", OrderNumber=3) fd1 = ODM.FormDef(OID="ODM.F.VS", Name="Vital Signs", Repeating="No") fd1.ItemGroupRef = [igr1, igr2, igr3] - igr4 = ODM.ItemGroupRef(ItemGroupOID="ODM.IG.Common", Mandatory="Yes", OrderNumber=1) - igr5 = ODM.ItemGroupRef(ItemGroupOID="ODM.IG.DM", Mandatory="Yes", OrderNumber=2) + igr4 = ODM.ItemGroupRef( + ItemGroupOID="ODM.IG.Common", Mandatory="Yes", OrderNumber=1) + igr5 = ODM.ItemGroupRef(ItemGroupOID="ODM.IG.DM", + Mandatory="Yes", OrderNumber=2) fd2 = ODM.FormDef(OID="ODM.F.DM", Name="Demographics", Repeating="No") fd2.ItemGroupRef = [igr4, igr5] return [fd1, fd2] @@ -179,19 +200,24 @@ def add_SED(self): fr1 = ODM.FormRef(FormOID="ODM.F.DM", Mandatory="Yes", OrderNumber=1) fr2 = ODM.FormRef(FormOID="ODM.F.VS", Mandatory="Yes", OrderNumber=2) fr3 = ODM.FormRef(FormOID="ODM.F.AE", Mandatory="Yes", OrderNumber=3) - ser1 = ODM.StudyEventDef(OID="BASELINE", Name="Baseline Visit", Repeating="No", Type="Scheduled") + ser1 = ODM.StudyEventDef( + OID="BASELINE", Name="Baseline Visit", Repeating="No", Type="Scheduled") ser1.FormRef = [fr1, fr2, fr3] - ser2 = ODM.StudyEventDef(OID="FOLLOW-UP", Name="Follow-up Visit", Repeating="Yes", Type="Scheduled") + ser2 = ODM.StudyEventDef( + OID="FOLLOW-UP", Name="Follow-up Visit", Repeating="Yes", Type="Scheduled") ser2.FormRef = [fr1, fr2, fr3] return [ser1, ser2] def add_protocol(self): p = ODM.Protocol() - tt = ODM.TranslatedText(_content="Trace-XML Test CDASH File", lang="en") + tt = ODM.TranslatedText( + _content="Trace-XML Test CDASH File", lang="en") desc = ODM.Description() desc.TranslatedText = [tt] - ser1 = ODM.StudyEventRef(StudyEventOID="BASELINE", OrderNumber=1, Mandatory="Yes") - ser2 = ODM.StudyEventRef(StudyEventOID="FOLLOW-UP", OrderNumber=2, Mandatory="Yes") + ser1 = ODM.StudyEventRef( + StudyEventOID="BASELINE", OrderNumber=1, Mandatory="Yes") + ser2 = ODM.StudyEventRef( + StudyEventOID="FOLLOW-UP", OrderNumber=2, Mandatory="Yes") a = ODM.Alias(Context="ClinicalTrials.gov", Name="trace-protocol") p.Description = desc p.StudyEventRef = [ser1, ser2] @@ -203,254 +229,254 @@ def set_mdv_attributes(self): def expected_dict(self): return { - "FileOID": "ODM.MDV.TEST.001", - "Granularity": "Metadata", - "AsOfDateTime": "2020-07-13T00:13:51.309617+00:00", - "CreationDateTime": "2020-07-13T00:13:51.309617+00:00", - "ODMVersion": "1.3.2", - "FileType": "Snapshot", - "Originator": "RDS", - "SourceSystem": "ODMLib", - "SourceSystemVersion": "0.1", - "schemaLocation": "http://www.cdisc.org/ns/odm/v1.3 odm1-3-2.xsd", - "Study": [{ - "OID": "ODM.STUDY.001", - "GlobalVariables": { - "StudyName": {"_content": "ODM XML Test Study Name"}, - "StudyDescription": {"_content": "Testing the generation of an ODM XML file"}, - "ProtocolName": {"_content": "ODM XML Test Study"} - }, - "MetaDataVersion": [{ - "OID": "MDV.TRACE-XML-ODM-01", - "Name": "TRACE-XML MDV", - "Description": "Trace-XML Example", - "Protocol": { - "Description": {"TranslatedText": [{ - "_content": "Trace-XML Test CDASH File", - "lang": "en" - }]}, - "StudyEventRef": [ - { - "StudyEventOID": "BASELINE", - "OrderNumber": 1, - "Mandatory": "Yes" - }, - { - "StudyEventOID": "FOLLOW-UP", - "OrderNumber": 2, - "Mandatory": "Yes" - } - ], - "Alias": [{ - "Context": "ClinicalTrials.gov", - "Name": "trace-protocol" - }] - }, - "StudyEventDef": [ - { - "OID": "BASELINE", - "Name": "Baseline Visit", - "Repeating": "No", - "Type": "Scheduled", - "FormRef": [ - { - "FormOID": "ODM.F.DM", - "Mandatory": "Yes", - "OrderNumber": 1 - }, - { - "FormOID": "ODM.F.VS", - "Mandatory": "Yes", - "OrderNumber": 2 - }, - { - "FormOID": "ODM.F.AE", - "Mandatory": "Yes", - "OrderNumber": 3 - } - ] + "FileOID": "ODM.MDV.TEST.001", + "Granularity": "Metadata", + "AsOfDateTime": "2020-07-13T00:13:51.309617+00:00", + "CreationDateTime": "2020-07-13T00:13:51.309617+00:00", + "ODMVersion": "1.3.2", + "FileType": "Snapshot", + "Originator": "RDS", + "SourceSystem": "ODMLib", + "SourceSystemVersion": "0.1", + "schemaLocation": "http://www.cdisc.org/ns/odm/v1.3 odm1-3-2.xsd", + "Study": [{ + "OID": "ODM.STUDY.001", + "GlobalVariables": { + "StudyName": {"_content": "ODM XML Test Study Name"}, + "StudyDescription": {"_content": "Testing the generation of an ODM XML file"}, + "ProtocolName": {"_content": "ODM XML Test Study"} }, - { - "OID": "FOLLOW-UP", - "Name": "Follow-up Visit", - "Repeating": "Yes", - "Type": "Scheduled", - "FormRef": [ - { - "FormOID": "ODM.F.DM", - "Mandatory": "Yes", - "OrderNumber": 1 - }, - { - "FormOID": "ODM.F.VS", - "Mandatory": "Yes", - "OrderNumber": 2 - }, - { - "FormOID": "ODM.F.AE", - "Mandatory": "Yes", - "OrderNumber": 3 - } - ] - } - ], - "FormDef": [ - { - "OID": "ODM.F.VS", - "Name": "Vital Signs", - "Repeating": "No", - "ItemGroupRef": [ - { - "ItemGroupOID": "ODM.IG.Common", - "Mandatory": "Yes", - "OrderNumber": 1 - }, - { - "ItemGroupOID": "ODM.IG.VS_GENERAL", - "Mandatory": "Yes", - "OrderNumber": 2 - }, - { - "ItemGroupOID": "ODM.IG.VS", - "Mandatory": "Yes", - "OrderNumber": 3 - } - ] - }, - { - "OID": "ODM.F.DM", - "Name": "Demographics", - "Repeating": "No", - "ItemGroupRef": [ + "MetaDataVersion": [{ + "OID": "MDV.TRACE-XML-ODM-01", + "Name": "TRACE-XML MDV", + "Description": "Trace-XML Example", + "Protocol": { + "Description": {"TranslatedText": [{ + "_content": "Trace-XML Test CDASH File", + "lang": "en" + }]}, + "StudyEventRef": [ + { + "StudyEventOID": "BASELINE", + "OrderNumber": 1, + "Mandatory": "Yes" + }, + { + "StudyEventOID": "FOLLOW-UP", + "OrderNumber": 2, + "Mandatory": "Yes" + } + ], + "Alias": [{ + "Context": "ClinicalTrials.gov", + "Name": "trace-protocol" + }] + }, + "StudyEventDef": [ { - "ItemGroupOID": "ODM.IG.Common", - "Mandatory": "Yes", - "OrderNumber": 1 + "OID": "BASELINE", + "Name": "Baseline Visit", + "Repeating": "No", + "Type": "Scheduled", + "FormRef": [ + { + "FormOID": "ODM.F.DM", + "Mandatory": "Yes", + "OrderNumber": 1 + }, + { + "FormOID": "ODM.F.VS", + "Mandatory": "Yes", + "OrderNumber": 2 + }, + { + "FormOID": "ODM.F.AE", + "Mandatory": "Yes", + "OrderNumber": 3 + } + ] }, { - "ItemGroupOID": "ODM.IG.DM", - "Mandatory": "Yes", - "OrderNumber": 2 + "OID": "FOLLOW-UP", + "Name": "Follow-up Visit", + "Repeating": "Yes", + "Type": "Scheduled", + "FormRef": [ + { + "FormOID": "ODM.F.DM", + "Mandatory": "Yes", + "OrderNumber": 1 + }, + { + "FormOID": "ODM.F.VS", + "Mandatory": "Yes", + "OrderNumber": 2 + }, + { + "FormOID": "ODM.F.AE", + "Mandatory": "Yes", + "OrderNumber": 3 + } + ] } - ] - } - ], - "ItemGroupDef": [ - { - "OID": "ODM.IG.VS", - "Name": "Vital Sign Measurement", - "Repeating": "Yes", - "ItemRef": [ + ], + "FormDef": [ { - "ItemOID": "ODM.IT.VS.VSDAT", - "Mandatory": "Yes" + "OID": "ODM.F.VS", + "Name": "Vital Signs", + "Repeating": "No", + "ItemGroupRef": [ + { + "ItemGroupOID": "ODM.IG.Common", + "Mandatory": "Yes", + "OrderNumber": 1 + }, + { + "ItemGroupOID": "ODM.IG.VS_GENERAL", + "Mandatory": "Yes", + "OrderNumber": 2 + }, + { + "ItemGroupOID": "ODM.IG.VS", + "Mandatory": "Yes", + "OrderNumber": 3 + } + ] }, { - "ItemOID": "ODM.IT.VS.BP.DIABP.VSORRES", - "Mandatory": "Yes" - }, - { - "ItemOID": "ODM.IT.VS.BP.SYSBP.VSORRES", - "Mandatory": "Yes" + "OID": "ODM.F.DM", + "Name": "Demographics", + "Repeating": "No", + "ItemGroupRef": [ + { + "ItemGroupOID": "ODM.IG.Common", + "Mandatory": "Yes", + "OrderNumber": 1 + }, + { + "ItemGroupOID": "ODM.IG.DM", + "Mandatory": "Yes", + "OrderNumber": 2 + } + ] } - ] - }, - { - "OID": "ODM.IG.DM", - "Name": "Demographics", - "Repeating": "No", - "ItemRef": [ + ], + "ItemGroupDef": [ { - "ItemOID": "ODM.IT.DM.BRTHYR", - "Mandatory": "Yes" + "OID": "ODM.IG.VS", + "Name": "Vital Sign Measurement", + "Repeating": "Yes", + "ItemRef": [ + { + "ItemOID": "ODM.IT.VS.VSDAT", + "Mandatory": "Yes" + }, + { + "ItemOID": "ODM.IT.VS.BP.DIABP.VSORRES", + "Mandatory": "Yes" + }, + { + "ItemOID": "ODM.IT.VS.BP.SYSBP.VSORRES", + "Mandatory": "Yes" + } + ] }, { - "ItemOID": "ODM.IT.DM.SEX", - "Mandatory": "Yes" + "OID": "ODM.IG.DM", + "Name": "Demographics", + "Repeating": "No", + "ItemRef": [ + { + "ItemOID": "ODM.IT.DM.BRTHYR", + "Mandatory": "Yes" + }, + { + "ItemOID": "ODM.IT.DM.SEX", + "Mandatory": "Yes" + } + ] } - ] - } - ], - "ItemDef": [ - { - "OID": "ODM.IT.VS.VSDAT", - "Name": "Date", - "DataType": "partialDate", - "Description": {"TranslatedText": [{ - "_content": "Date of measurements", - "lang": "en" - }]}, - "Question": {"TranslatedText": [{ - "_content": "Date", - "lang": "en" - }]}, - "Alias": [{ - "Context": "CDASH", - "Name": "VSDAT" - }] - }, - { - "OID": "ODM.IT.VS.BP.VSORRESU", - "Name": "BP Units", - "DataType": "text", - "Description": {"TranslatedText": [{ - "_content": "Result of the vital signs measurement as originally received or collected.", - "lang": "en" - }]}, - "Question": {"TranslatedText": [{ - "_content": "Diastolic", - "lang": "en" - }]}, - "Alias": [ + ], + "ItemDef": [ { - "Context": "CDASH", - "Name": "BP.DIABP.VSORRES" + "OID": "ODM.IT.VS.VSDAT", + "Name": "Date", + "DataType": "partialDate", + "Description": {"TranslatedText": [{ + "_content": "Date of measurements", + "lang": "en" + }]}, + "Question": {"TranslatedText": [{ + "_content": "Date", + "lang": "en" + }]}, + "Alias": [{ + "Context": "CDASH", + "Name": "VSDAT" + }] }, { - "Context": "CDASH/SDTM", - "Name": "VSORRES+VSORRESU" + "OID": "ODM.IT.VS.BP.VSORRESU", + "Name": "BP Units", + "DataType": "text", + "Description": {"TranslatedText": [{ + "_content": "Result of the vital signs measurement as originally received or collected.", + "lang": "en" + }]}, + "Question": {"TranslatedText": [{ + "_content": "Diastolic", + "lang": "en" + }]}, + "Alias": [ + { + "Context": "CDASH", + "Name": "BP.DIABP.VSORRES" + }, + { + "Context": "CDASH/SDTM", + "Name": "VSORRES+VSORRESU" + } + ] } - ] - } - ], - "CodeList": [{ - "OID": "ODM.CL.NY_SUB_Y_N", - "Name": "No Yes Response", - "DataType": "text", - "CodeListItem": [ - { - "CodedValue": "N", - "Decode": {"TranslatedText": [{ - "_content": "No", + ], + "CodeList": [{ + "OID": "ODM.CL.NY_SUB_Y_N", + "Name": "No Yes Response", + "DataType": "text", + "CodeListItem": [ + { + "CodedValue": "N", + "Decode": {"TranslatedText": [{ + "_content": "No", + "lang": "en" + }]} + }, + { + "CodedValue": "Y", + "Decode": {"TranslatedText": [{ + "_content": "Yes", + "lang": "en" + }]} + } + ] + }], + "ConditionDef": [{ + "OID": "ODM.CD.BRTHMO", + "Name": "Skip BRTHMO when no BRTHYR", + "Description": {"TranslatedText": [{ + "_content": "Skip the BRTHMO field when BRTHYR length NE 4", "lang": "en" }]} - }, - { - "CodedValue": "Y", - "Decode": {"TranslatedText": [{ - "_content": "Yes", + }], + "MethodDef": [{ + "OID": "ODM.MT.DOB", + "Name": "Create BRTHDTC from date ELEMENTS", + "Type": "Computation", + "Description": {"TranslatedText": [{ + "_content": "Concatenation of BRTHYR, BRTHMO, and BRTHDY in ISO 8601 format", "lang": "en" }]} - } - ] - }], - "ConditionDef": [{ - "OID": "ODM.CD.BRTHMO", - "Name": "Skip BRTHMO when no BRTHYR", - "Description": {"TranslatedText": [{ - "_content": "Skip the BRTHMO field when BRTHYR length NE 4", - "lang": "en" - }]} - }], - "MethodDef": [{ - "OID": "ODM.MT.DOB", - "Name": "Create BRTHDTC from date ELEMENTS", - "Type": "Computation", - "Description": {"TranslatedText": [{ - "_content": "Concatenation of BRTHYR, BRTHMO, and BRTHDY in ISO 8601 format", - "lang": "en" - }]} + }] + }] }] - }] - }] -} + } diff --git a/tests/test_odm_string_parser.py b/tests/test_odm_string_parser.py index efe998a..a3bb90b 100644 --- a/tests/test_odm_string_parser.py +++ b/tests/test_odm_string_parser.py @@ -2,12 +2,14 @@ import os import odmlib.odm_parser as P from xml.etree.ElementTree import Element + +from tests import get_data_file_path ODM_NS = "{http://www.cdisc.org/ns/odm/v1.3}" class TestOdmParserMetaData(unittest.TestCase): def setUp(self) -> None: - self.odm_file_1 = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'data', 'cdash-odm-test.xml') + self.odm_file_1 = get_data_file_path("cdash-odm-test.xml") with open(self.odm_file_1, "r", encoding="utf-8") as f: self.odm_string = f.read() self.parser = P.ODMStringParser(self.odm_string) diff --git a/tests/test_oid_index.py b/tests/test_oid_index.py index 7c4af8a..a014135 100644 --- a/tests/test_oid_index.py +++ b/tests/test_oid_index.py @@ -30,7 +30,8 @@ def test_oid_index_find_codelist(self): def test_oid_index_define_igd(self): self.define_file = get_data_file_path('defineV21-SDTM.xml') - self.loader = LD.ODMLoader(DL.XMLDefineLoader(model_package="define_2_1", ns_uri="http://www.cdisc.org/ns/def/v2.1")) + self.loader = LD.ODMLoader(DL.XMLDefineLoader( + model_package="define_2_1", ns_uri="http://www.cdisc.org/ns/def/v2.1")) self.loader.open_odm_document(self.define_file) self.odm = self.loader.root() idx = self.odm.build_oid_index() diff --git a/tests/test_rangeCheck.py b/tests/test_rangeCheck.py index 689286b..76db076 100644 --- a/tests/test_rangeCheck.py +++ b/tests/test_rangeCheck.py @@ -13,30 +13,41 @@ def test_add_check_value(self): self.assertEqual(self.range_check.CheckValue[0]._content, "DIABP") def test_add_formal_expression(self): - self.range_check.FormalExpression = [ODM.FormalExpression(Context="Python 3.7", _content="print('hello world')")] - self.assertEqual(self.range_check.FormalExpression[0]._content, "print('hello world')") + self.range_check.FormalExpression = [ODM.FormalExpression( + Context="Python 3.7", _content="print('hello world')")] + self.assertEqual( + self.range_check.FormalExpression[0]._content, "print('hello world')") def test_add_measurement_unit_ref(self): - self.range_check.MeasurementUnitRef = ODM.MeasurementUnitRef(MeasurementUnitOID="ODM.MU.UNITS") - self.assertEqual(self.range_check.MeasurementUnitRef.MeasurementUnitOID, "ODM.MU.UNITS") + self.range_check.MeasurementUnitRef = ODM.MeasurementUnitRef( + MeasurementUnitOID="ODM.MU.UNITS") + self.assertEqual( + self.range_check.MeasurementUnitRef.MeasurementUnitOID, "ODM.MU.UNITS") def test_add_error_message(self): #self.range_check.ErrorMessage = ODM.ErrorMessage() - self.range_check.ErrorMessage.TranslatedText.append(ODM.TranslatedText(_content="invalid test code", lang="en")) - self.range_check.ErrorMessage.TranslatedText.append(ODM.TranslatedText(_content="code de test invalide", lang="fr")) + self.range_check.ErrorMessage.TranslatedText.append( + ODM.TranslatedText(_content="invalid test code", lang="en")) + self.range_check.ErrorMessage.TranslatedText.append( + ODM.TranslatedText(_content="code de test invalide", lang="fr")) self.assertEqual(len(self.range_check.ErrorMessage.TranslatedText), 2) - self.assertEqual(self.range_check.ErrorMessage.TranslatedText[1]._content, 'code de test invalide') + self.assertEqual( + self.range_check.ErrorMessage.TranslatedText[1]._content, 'code de test invalide') def test_to_json(self): attrs = self._set_attributes() rc = ODM.RangeCheck(**attrs) rc.CheckValue = [ODM.CheckValue(_content="DIABP")] - rc.FormalExpression = [ODM.FormalExpression(Context="Python 3.7", _content="print('hello world')")] - rc.MeasurementUnitRef = ODM.MeasurementUnitRef(MeasurementUnitOID="ODM.MU.UNITS") + rc.FormalExpression = [ODM.FormalExpression( + Context="Python 3.7", _content="print('hello world')")] + rc.MeasurementUnitRef = ODM.MeasurementUnitRef( + MeasurementUnitOID="ODM.MU.UNITS") # commented out when allowed empty wrapper ELEMENTS to be virtually instantiated (may not keep this) #rc.ErrorMessage = ODM.ErrorMessage() - rc.ErrorMessage.TranslatedText.append(ODM.TranslatedText(_content="invalid test code", lang="en")) - rc.ErrorMessage.TranslatedText.append(ODM.TranslatedText(_content="code de test invalide", lang="fr")) + rc.ErrorMessage.TranslatedText.append( + ODM.TranslatedText(_content="invalid test code", lang="en")) + rc.ErrorMessage.TranslatedText.append(ODM.TranslatedText( + _content="code de test invalide", lang="fr")) rc_json = rc.to_json() rc_dict = json.loads(rc_json) print(rc_dict) @@ -46,10 +57,14 @@ def test_to_dict(self): attrs = self._set_attributes() rc = ODM.RangeCheck(**attrs) rc.CheckValue = [ODM.CheckValue(_content="DIABP")] - rc.FormalExpression = [ODM.FormalExpression(Context="Python 3.7", _content="print('hello world')")] - rc.MeasurementUnitRef = ODM.MeasurementUnitRef(MeasurementUnitOID="ODM.MU.UNITS") - rc.ErrorMessage.TranslatedText.append(ODM.TranslatedText(_content="invalid test code", lang="en")) - rc.ErrorMessage.TranslatedText.append(ODM.TranslatedText(_content="code de test invalide", lang="fr")) + rc.FormalExpression = [ODM.FormalExpression( + Context="Python 3.7", _content="print('hello world')")] + rc.MeasurementUnitRef = ODM.MeasurementUnitRef( + MeasurementUnitOID="ODM.MU.UNITS") + rc.ErrorMessage.TranslatedText.append( + ODM.TranslatedText(_content="invalid test code", lang="en")) + rc.ErrorMessage.TranslatedText.append(ODM.TranslatedText( + _content="code de test invalide", lang="fr")) rc_dict = rc.to_dict() print(rc_dict) rc_dict = rc.to_dict() @@ -59,11 +74,15 @@ def test_to_xml(self): attrs = self._set_attributes() rc = ODM.RangeCheck(**attrs) rc.CheckValue = [ODM.CheckValue(_content="DIABP")] - rc.FormalExpression = [ODM.FormalExpression(Context="Python 3.7", _content="print('hello world')")] - rc.MeasurementUnitRef = ODM.MeasurementUnitRef(MeasurementUnitOID="ODM.MU.UNITS") + rc.FormalExpression = [ODM.FormalExpression( + Context="Python 3.7", _content="print('hello world')")] + rc.MeasurementUnitRef = ODM.MeasurementUnitRef( + MeasurementUnitOID="ODM.MU.UNITS") #rc.ErrorMessage = ODM.ErrorMessage() - rc.ErrorMessage.TranslatedText.append(ODM.TranslatedText(_content="invalid test code", lang="en")) - rc.ErrorMessage.TranslatedText.append(ODM.TranslatedText(_content="code de test invalide", lang="fr")) + rc.ErrorMessage.TranslatedText.append( + ODM.TranslatedText(_content="invalid test code", lang="en")) + rc.ErrorMessage.TranslatedText.append(ODM.TranslatedText( + _content="code de test invalide", lang="fr")) rc_xml = rc.to_xml() self.assertEqual(rc_xml.attrib["SoftHard"], "Soft") @@ -76,8 +95,9 @@ def _set_attributes(self): def _json_range_check_dict(self): return {'Comparator': 'EQ', 'SoftHard': 'Soft', 'CheckValue': [{'_content': 'DIABP'}], 'FormalExpression': - [{'Context': 'Python 3.7', '_content': "print('hello world')"}], - 'MeasurementUnitRef': {'MeasurementUnitOID': 'ODM.MU.UNITS'}, - 'ErrorMessage': {'TranslatedText': [{'lang': 'en', '_content': 'invalid test code'}, - {'lang': 'fr', '_content': 'code de test invalide'}]} + [{'Context': 'Python 3.7', + '_content': "print('hello world')"}], + 'MeasurementUnitRef': {'MeasurementUnitOID': 'ODM.MU.UNITS'}, + 'ErrorMessage': {'TranslatedText': [{'lang': 'en', '_content': 'invalid test code'}, + {'lang': 'fr', '_content': 'code de test invalide'}]} } diff --git a/tests/test_read_dataset.py b/tests/test_read_dataset.py index 7c2920e..faffffe 100644 --- a/tests/test_read_dataset.py +++ b/tests/test_read_dataset.py @@ -9,17 +9,23 @@ class TestReadDataset(unittest.TestCase): def setUp(self) -> None: self.odm_file = get_data_file_path('ae_test.xml') - self.loader = OL.XMLODMLoader(model_package="dataset_1_0_1", ns_uri="http://www.cdisc.org/ns/Dataset-XML/v1.0") - NS.NamespaceRegistry(prefix="odm", uri="http://www.cdisc.org/ns/odm/v1.3", is_default=True) - self.ns = NS.NamespaceRegistry(prefix="data", uri="http://www.cdisc.org/ns/Dataset-XML/v1.0") + self.loader = OL.XMLODMLoader( + model_package="dataset_1_0_1", ns_uri="http://www.cdisc.org/ns/Dataset-XML/v1.0") + NS.NamespaceRegistry( + prefix="odm", uri="http://www.cdisc.org/ns/odm/v1.3", is_default=True) + self.ns = NS.NamespaceRegistry( + prefix="data", uri="http://www.cdisc.org/ns/Dataset-XML/v1.0") def test_open_odm_document(self): self.loader.create_document(self.odm_file, self.ns) self.odm = self.loader.load_odm() self.assertEqual(self.odm.FileOID, "ODM.DATASET.001") - self.assertEqual(self.odm.ClinicalData.ItemGroupData[0].ItemGroupOID, "IG.AE") - self.assertEqual(self.odm.ClinicalData.ItemGroupData[0].ItemData[2].Value, "CDISC01.100008") - self.assertEqual(self.odm.ClinicalData.ItemGroupData[1].ItemData[4].Value, "ANXIETY") + self.assertEqual( + self.odm.ClinicalData.ItemGroupData[0].ItemGroupOID, "IG.AE") + self.assertEqual( + self.odm.ClinicalData.ItemGroupData[0].ItemData[2].Value, "CDISC01.100008") + self.assertEqual( + self.odm.ClinicalData.ItemGroupData[1].ItemData[4].Value, "ANXIETY") def test_dataset_iterator(self): self.loader.create_document(self.odm_file, self.ns) @@ -30,7 +36,6 @@ def test_dataset_iterator(self): values.append(item.Value) self.assertListEqual(values, test_values) - def _get_igd_values(self): return ["CDISC01", "AE", "CDISC01.100008", "2", "ANXIETY", "AGITATION", "Anxiety", "MODERATE", "N", "DOSE NOT CHANGED", "POSSIBLY RELATED"] diff --git a/tests/test_referenceData.py b/tests/test_referenceData.py index cd48e82..3119bf8 100644 --- a/tests/test_referenceData.py +++ b/tests/test_referenceData.py @@ -2,23 +2,31 @@ import odmlib.odm_1_3_2.model as ODM import odmlib.odm_parser as ODM_PARSER import xml.etree.ElementTree as ET -import os import datetime +from tests import get_data_file_path + class TestReferenceData(TestCase): def setUp(self) -> None: - self.odm_test_file = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'data','test_referece_data_01.xml') + self.odm_test_file = get_data_file_path("test_referece_data_01.xml") def test_reference_data_to_xml(self): rd = [] - rd.append(ODM.ReferenceData(StudyOID="P2006-101", MetaDataVersionOID="101.01")) - rd[0].ItemGroupData.append(ODM.ItemGroupData(ItemGroupOID="IG.REFSAMP", ItemGroupRepeatKey="1", TransactionType="Insert")) - rd[0].ItemGroupData[0].ItemData.append(ODM.ItemData(ItemOID="IT.REF1", Value="1")) - rd[0].ItemGroupData[0].ItemData.append(ODM.ItemData(ItemOID="IT.REF2", Value="Sample reference data line 1")) - rd[0].ItemGroupData.append(ODM.ItemGroupData(ItemGroupOID="IG.REFSAMP", ItemGroupRepeatKey="2", TransactionType="Insert")) - rd[0].ItemGroupData[1].ItemData.append(ODM.ItemData(ItemOID="IT.REF1", Value="2")) - rd[0].ItemGroupData[1].ItemData.append(ODM.ItemData(ItemOID="IT.REF2", Value="Sample reference data line 2")) + rd.append(ODM.ReferenceData( + StudyOID="P2006-101", MetaDataVersionOID="101.01")) + rd[0].ItemGroupData.append(ODM.ItemGroupData( + ItemGroupOID="IG.REFSAMP", ItemGroupRepeatKey="1", TransactionType="Insert")) + rd[0].ItemGroupData[0].ItemData.append( + ODM.ItemData(ItemOID="IT.REF1", Value="1")) + rd[0].ItemGroupData[0].ItemData.append(ODM.ItemData( + ItemOID="IT.REF2", Value="Sample reference data line 1")) + rd[0].ItemGroupData.append(ODM.ItemGroupData( + ItemGroupOID="IG.REFSAMP", ItemGroupRepeatKey="2", TransactionType="Insert")) + rd[0].ItemGroupData[1].ItemData.append( + ODM.ItemData(ItemOID="IT.REF1", Value="2")) + rd[0].ItemGroupData[1].ItemData.append(ODM.ItemData( + ItemOID="IT.REF2", Value="Sample reference data line 2")) root = self.create_odm_document(rd) odm_xml = root.to_xml() self.assertEqual(odm_xml.attrib["FileOID"], "ODM.TEST.RD.001") @@ -33,7 +41,8 @@ def test_clinical_data_from_xml(self): self.assertEqual(item_data[0]["Value"], "2") def test_audit_records(self): - ar = ODM.AuditRecord(EditPoint="Monitoring", UsedImputationMethod="Yes", ID="2") + ar = ODM.AuditRecord(EditPoint="Monitoring", + UsedImputationMethod="Yes", ID="2") ar.UserRef = ODM.UserRef(UserOID="USER.001") ar.LocationRef = ODM.LocationRef(LocationOID="SITE.102") ar.DateTimeStamp = ODM.DateTimeStamp(_content="2020-12-01T10:28:16") @@ -42,10 +51,13 @@ def test_audit_records(self): ars = ODM.AuditRecords() ars.AuditRecord.append(ar) rd = [] - rd.append(ODM.ReferenceData(StudyOID="P2006-101", MetaDataVersionOID="101.01")) + rd.append(ODM.ReferenceData( + StudyOID="P2006-101", MetaDataVersionOID="101.01")) rd[0].AuditRecords.append(ars) self.assertEqual(rd[0].AuditRecords[0].AuditRecord[0].ID, "2") - self.assertEqual(rd[0].AuditRecords[0].AuditRecord[0].LocationRef.LocationOID, "SITE.102") + self.assertEqual( + rd[0].AuditRecords[0].AuditRecord[0].LocationRef.LocationOID, + "SITE.102") def test_signatures(self): sig = ODM.Signature(ID="SIG.001.USER.002") @@ -56,24 +68,34 @@ def test_signatures(self): sigs = ODM.Signatures() sigs.Signature.append(sig) rd = [] - rd.append(ODM.ReferenceData(StudyOID="P2006-101", MetaDataVersionOID="101.01")) + rd.append(ODM.ReferenceData( + StudyOID="P2006-101", MetaDataVersionOID="101.01")) rd[0].Signatures.append(sigs) - self.assertEqual(rd[0].Signatures[0].Signature[0].ID, "SIG.001.USER.002") - self.assertEqual(rd[0].Signatures[0].Signature[0].SignatureRef.SignatureOID, "SIG.002") + self.assertEqual( + rd[0].Signatures[0].Signature[0].ID, "SIG.001.USER.002") + self.assertEqual( + rd[0].Signatures[0].Signature[0].SignatureRef.SignatureOID, + "SIG.002") def test_annotations(self): - annotation = ODM.Annotation(SeqNum="1", TransactionType="Insert", ID="ANN999") - annotation.Comment = ODM.Comment(SponsorOrSite="Site", _content="Transferred from EHR") + annotation = ODM.Annotation( + SeqNum="1", TransactionType="Insert", ID="ANN999") + annotation.Comment = ODM.Comment( + SponsorOrSite="Site", _content="Transferred from EHR") annotation.Flag.append(ODM.Flag()) - annotation.Flag[0].FlagValue = ODM.FlagValue(CodeListOID="CL.FLAGVALUE", _content="eSource") - annotation.Flag[0].FlagType = ODM.FlagType(CodeListOID="CL.FLAGTYPE", _content="eDT") + annotation.Flag[0].FlagValue = ODM.FlagValue( + CodeListOID="CL.FLAGVALUE", _content="eSource") + annotation.Flag[0].FlagType = ODM.FlagType( + CodeListOID="CL.FLAGTYPE", _content="eDT") anns = ODM.Annotations() anns.Annotation.append(annotation) rd = [] - rd.append(ODM.ReferenceData(StudyOID="P2006-101", MetaDataVersionOID="101.01")) + rd.append(ODM.ReferenceData( + StudyOID="P2006-101", MetaDataVersionOID="101.01")) rd[0].Annotations.append(anns) self.assertEqual(rd[0].Annotations[0].Annotation[0].ID, "ANN999") - self.assertEqual(rd[0].Annotations[0].Annotation[0].Flag[0].FlagValue.CodeListOID, "CL.FLAGVALUE") + self.assertEqual( + rd[0].Annotations[0].Annotation[0].Flag[0].FlagValue.CodeListOID, "CL.FLAGVALUE") def create_odm_document(self, rd): """ @@ -91,8 +113,11 @@ def create_root(self): create the ODM root element object with test data :return: ODM root element object """ - root = {"FileOID": "ODM.TEST.RD.001", "Granularity": "Metadata", "AsOfDateTime": self.set_datetime(), - "CreationDateTime": self.set_datetime(), "ODMVersion": "1.3.2", "Originator": "ODMLIB", "SourceSystem": "ODMLIB", + root = {"FileOID": "ODM.TEST.RD.001", "Granularity": "Metadata", + "AsOfDateTime": self.set_datetime(), + "CreationDateTime": self.set_datetime(), + "ODMVersion": "1.3.2", "Originator": "ODMLIB", + "SourceSystem": "ODMLIB", "SourceSystemVersion": "0.1", "FileType": "Snapshot"} root = ODM.ODM(**root) return root diff --git a/tests/test_study.py b/tests/test_study.py index a63f376..6b1ecdb 100644 --- a/tests/test_study.py +++ b/tests/test_study.py @@ -11,28 +11,36 @@ def test_required_attributes(self): def test_full_study(self): study_name = ODM.StudyName(_content="The ODM study name") - study_desc = ODM.StudyDescription(_content="The description of the ODM study") + study_desc = ODM.StudyDescription( + _content="The description of the ODM study") protocol_name = ODM.ProtocolName(_content="The ODM protocol name") self.study.GlobalVariables = ODM.GlobalVariables() self.study.GlobalVariables.StudyName = study_name self.study.GlobalVariables.StudyDescription = study_desc self.study.GlobalVariables.ProtocolName = protocol_name - self.assertEqual(self.study.GlobalVariables.StudyName._content, "The ODM study name") + self.assertEqual( + self.study.GlobalVariables.StudyName._content, "The ODM study name") def test_study_dict(self): self.study.GlobalVariables = ODM.GlobalVariables() - self.study.GlobalVariables.StudyName = ODM.StudyName(_content="The ODM study name") - self.study.GlobalVariables.StudyDescription = ODM.StudyDescription(_content="The description of the ODM study") - self.study.GlobalVariables.ProtocolName = ODM.ProtocolName(_content="The ODM protocol name") + self.study.GlobalVariables.StudyName = ODM.StudyName( + _content="The ODM study name") + self.study.GlobalVariables.StudyDescription = ODM.StudyDescription( + _content="The description of the ODM study") + self.study.GlobalVariables.ProtocolName = ODM.ProtocolName( + _content="The ODM protocol name") study_dict = self.study.to_dict() print(study_dict) self.assertDictEqual(self.expected_dict(), study_dict) def test_study_to_xml(self): self.study.GlobalVariables = ODM.GlobalVariables() - self.study.GlobalVariables.StudyName = ODM.StudyName(_content="The ODM study name") - self.study.GlobalVariables.StudyDescription = ODM.StudyDescription(_content="The description of the ODM study") - self.study.GlobalVariables.ProtocolName = ODM.ProtocolName(_content="The ODM protocol name") + self.study.GlobalVariables.StudyName = ODM.StudyName( + _content="The ODM study name") + self.study.GlobalVariables.StudyDescription = ODM.StudyDescription( + _content="The description of the ODM study") + self.study.GlobalVariables.ProtocolName = ODM.ProtocolName( + _content="The ODM protocol name") study_xml = self.study.to_xml() gv = study_xml.find("GlobalVariables") pn = gv.find("ProtocolName") diff --git a/tests/test_studyEventDef.py b/tests/test_studyEventDef.py index e12896f..84875f9 100644 --- a/tests/test_studyEventDef.py +++ b/tests/test_studyEventDef.py @@ -9,12 +9,15 @@ def setUp(self) -> None: self.sed = ODM.StudyEventDef(**attrs) def test_add_description(self): - tt1 = ODM.TranslatedText(_content="this is the first test description", lang="en") - tt2 = ODM.TranslatedText(_content="this is the second test description", lang="en") + tt1 = ODM.TranslatedText( + _content="this is the first test description", lang="en") + tt2 = ODM.TranslatedText( + _content="this is the second test description", lang="en") self.sed.Description = ODM.Description() self.sed.Description.TranslatedText = [tt1, tt2] self.assertEqual(len(self.sed.Description.TranslatedText), 2) - self.assertEqual(self.sed.Description.TranslatedText[1]._content, 'this is the second test description') + self.assertEqual( + self.sed.Description.TranslatedText[1]._content, 'this is the second test description') def test_add_bad_attribute(self): attrs = self.set_attributes() @@ -40,7 +43,8 @@ def test_add_alias(self): def test_to_json(self): attrs = self.set_attributes() sed = ODM.StudyEventDef(**attrs) - tt1 = ODM.TranslatedText(_content="this is the first test description", lang="en") + tt1 = ODM.TranslatedText( + _content="this is the first test description", lang="en") desc = ODM.Description() desc.TranslatedText = [tt1] sed.Description = desc @@ -59,7 +63,8 @@ def test_to_json(self): def test_to_dict(self): attrs = self.set_attributes() sed = ODM.StudyEventDef(**attrs) - tt1 = ODM.TranslatedText(_content="this is the first test description", lang="en") + tt1 = ODM.TranslatedText( + _content="this is the first test description", lang="en") desc = ODM.Description() desc.TranslatedText = [tt1] sed.Description = desc @@ -76,7 +81,8 @@ def test_to_dict(self): def test_to_xml(self): attrs = self.set_attributes() sed = ODM.StudyEventDef(**attrs) - tt1 = ODM.TranslatedText(_content="this is the first test description", lang="en") + tt1 = ODM.TranslatedText( + _content="this is the first test description", lang="en") desc = ODM.Description() desc.TranslatedText = [tt1] sed.Description = desc @@ -90,13 +96,15 @@ def test_to_xml(self): self.assertEqual(sed_xml.attrib["OID"], "ODM.SE.BASELINE") fr = sed_xml.findall("FormRef") self.assertEqual(len(fr), 3) - self.assertEqual(fr[0].attrib, {"FormOID": "ODM.F.VS", "Mandatory": "Yes", "OrderNumber": "1"}) + self.assertEqual( + fr[0].attrib, {"FormOID": "ODM.F.VS", "Mandatory": "Yes", "OrderNumber": "1"}) def test_studyeventdef_slice(self): """ test the ability to reference a specific or slice of FormRefs """ attrs = self.set_attributes() sed = ODM.StudyEventDef(**attrs) - tt1 = ODM.TranslatedText(_content="this is the first test description", lang="en") + tt1 = ODM.TranslatedText( + _content="this is the first test description", lang="en") desc = ODM.Description() desc.TranslatedText = [tt1] sed.Description = desc @@ -123,7 +131,8 @@ def set_attributes(): def expected_dict(): return {'OID': 'ODM.SE.BASELINE', 'Name': 'Baseline Visit', 'Repeating': 'No', 'Type': 'Scheduled', 'Category': 'Pre-treatment', 'FormRef': [{'FormOID': 'ODM.F.VS', 'Mandatory': 'Yes', 'OrderNumber': 1}, - {'FormOID': 'ODM.F.DM', 'Mandatory': 'Yes', 'OrderNumber': 2}, - {'FormOID': 'ODM.F.MH', 'Mandatory': 'Yes', 'OrderNumber': 3}], + {'FormOID': 'ODM.F.DM', + 'Mandatory': 'Yes', 'OrderNumber': 2}, + {'FormOID': 'ODM.F.MH', 'Mandatory': 'Yes', 'OrderNumber': 3}], 'Description': {'TranslatedText': [{'lang': 'en', '_content': 'this is the first test description'}]}, 'Alias': [{'Context': 'SDTMIG', 'Name': 'VS'}]} diff --git a/tests/test_typed.py b/tests/test_typed.py index 5802d2c..4789297 100644 --- a/tests/test_typed.py +++ b/tests/test_typed.py @@ -68,7 +68,7 @@ def test_float_type(self): def test_positive_type(self): # testing Length as a positive integer attrs = {"OID": "ODM.IT.AE.AEYN", "Name": "Any AEs?", "DataType": "text", "Length": 1, "SASFieldName": "AEYN", - "SDSVarName": "AEYN", "Origin": "CRF", "Comment": "Data management field"} + "SDSVarName": "AEYN", "Origin": "CRF", "Comment": "Data management field"} item = ODM.ItemDef(**attrs) self.assertEqual(item.Length, 1) # testing Length = 0 as a positive integer @@ -87,7 +87,7 @@ def test_positive_type(self): def test_non_negative_type(self): # testing SignificantDigits as a non-negative integer attrs = {"OID": "ODM.IT.AE.AEYN", "Name": "Any AEs?", "DataType": "text", "Length": 1, "SASFieldName": "AEYN", - "SDSVarName": "AEYN", "Origin": "CRF", "Comment": "Data management field", "SignificantDigits": 1} + "SDSVarName": "AEYN", "Origin": "CRF", "Comment": "Data management field", "SignificantDigits": 1} item = ODM.ItemDef(**attrs) self.assertEqual(item.SignificantDigits, 1) # testing SignificantDigits = 0 as a non-negative integer @@ -106,20 +106,20 @@ def test_non_negative_type(self): def test_add_undefined_content(self): # use conformance rules to check for unknown objects being added after creation attrs = {"OID": "ODM.IT.AE.AEYN", "Name": "Any AEs?", "DataType": "text", "Length": 1, "SASFieldName": "AEYN", - "SDSVarName": "AEYN", "Origin": "CRF", "Comment": "Data management field", "SignificantDigits": 1} + "SDSVarName": "AEYN", "Origin": "CRF", "Comment": "Data management field", "SignificantDigits": 1} item = ODM.ItemDef(**attrs) clr = ODM.CodeListRef(CodeListOID="CL.TEST") with self.assertRaises(TypeError): item.NewCodeList = clr # cannot add unknown attributes or elements during creation, but can afterwards; catch with conformance checks attrs = {"OID": "ODM.IT.AE.AEYN", "Name": "Any AEs?", "DataType": "text", "Length": 1, "SASFieldName": "AEYN", - "SDSVarName": "AEYN", "Origin": "CRF", "Comment": "Data management field", "SignificantDigits": 1} + "SDSVarName": "AEYN", "Origin": "CRF", "Comment": "Data management field", "SignificantDigits": 1} item = ODM.ItemDef(**attrs) with self.assertRaises(TypeError): item.InSignificantDigits = 1 # can add all objects during creation and they are validated attrs = {"OID": "ODM.IT.AE.AEYN", "Name": "Any AEs?", "DataType": "text", "Length": 1, "SASFieldName": "AEYN", - "SDSVarName": "AEYN", "Origin": "CRF", "Comment": "Data management field", "CodeListRef": clr} + "SDSVarName": "AEYN", "Origin": "CRF", "Comment": "Data management field", "CodeListRef": clr} item = ODM.ItemDef(**attrs) self.assertEqual(item.CodeListRef, clr) attrs["CodeListReference"] = clr @@ -129,18 +129,18 @@ def test_add_undefined_content(self): self.assertEqual(item.CodeListRef, clr) # test adding the wrong type of object to a list attrs = {"OID": "ODM.IT.AE.AEYN", "Name": "Any AEs?", "DataType": "text", "Length": 1, "SASFieldName": "AEYN", - "SDSVarName": "AEYN", "Origin": "CRF", "Comment": "Data management field", "Alias": [clr]} + "SDSVarName": "AEYN", "Origin": "CRF", "Comment": "Data management field", "Alias": [clr]} with self.assertRaises(TypeError): item = ODM.ItemDef(**attrs) # test adding the wrong type of object to a list alias = ODM.Alias(Context="CDASH", Name="AEYN") attrs = {"OID": "ODM.IT.AE.AEYN", "Name": "Any AEs?", "DataType": "text", "Length": 1, "SASFieldName": "AEYN", - "SDSVarName": "AEYN", "Origin": "CRF", "Comment": "Data management field", "Alias": alias} + "SDSVarName": "AEYN", "Origin": "CRF", "Comment": "Data management field", "Alias": alias} with self.assertRaises(TypeError): item = ODM.ItemDef(**attrs) # assign wrong type of object to an element attrs = {"OID": "ODM.IT.AE.AEYN", "Name": "Any AEs?", "DataType": "text", "Length": 1, "SASFieldName": "AEYN", - "SDSVarName": "AEYN", "Origin": "CRF", "Comment": "Data management field", "CodeListRef": alias} + "SDSVarName": "AEYN", "Origin": "CRF", "Comment": "Data management field", "CodeListRef": alias} with self.assertRaises(TypeError): item = ODM.ItemDef(**attrs) @@ -180,7 +180,7 @@ def test_sasformat_type(self): def test_sasname_type(self): # valid test attrs = {"OID": "ODM.IT.AE.AEYN", "Name": "Any AEs?", "DataType": "text", "Length": 1, "SASFieldName": "AEYN", - "SDSVarName": "AEYN", "Origin": "CRF", "Comment": "Data management field", "SignificantDigits": 1} + "SDSVarName": "AEYN", "Origin": "CRF", "Comment": "Data management field", "SignificantDigits": 1} item = ODM.ItemDef(**attrs) self.assertEqual(item.SASFieldName, "AEYN") # start with an underscore @@ -230,7 +230,7 @@ def set_datetime(self): def test_string_type(self): attrs = {"OID": "ODM.IT.AE.AEYN", "Name": "Any AEs?", "DataType": "text", "Length": 1, "SASFieldName": "AEYN", - "SDSVarName": "AEYN", "Origin": "CRF", "Comment": "Data management field", "SignificantDigits": 1} + "SDSVarName": "AEYN", "Origin": "CRF", "Comment": "Data management field", "SignificantDigits": 1} item = ODM.ItemDef(**attrs) self.assertEqual(item.Origin, "CRF") # integer @@ -256,64 +256,82 @@ def test_regex_type(self): regex = TextTest(Name="AETERM", Label="help", Range="6-7") def test_url_type(self): - url = TextTest(Name="AETERM", Label="help", Range="4-9", Link="https://cdisc.org") + url = TextTest(Name="AETERM", Label="help", + Range="4-9", Link="https://cdisc.org") self.assertEqual(url.Link, "https://cdisc.org") # not a match with self.assertRaises(ValueError): - url = TextTest(Name="AETERM", Label="help", Range="4-9", Link="cdisc.org") + url = TextTest(Name="AETERM", Label="help", + Range="4-9", Link="cdisc.org") def test_filename_type(self): - archive = ODM.ArchiveLayout(OID="AL.AECRF", PdfFileName="ae_annotated_crf.pdf") + archive = ODM.ArchiveLayout( + OID="AL.AECRF", PdfFileName="ae_annotated_crf.pdf") self.assertEqual(archive.PdfFileName, "ae_annotated_crf.pdf") # with path with self.assertRaises(ValueError): - archive = ODM.ArchiveLayout(OID="AL.AECRF", PdfFileName="c:\\users\\shume\\ae_annotated_crf.pdf") + archive = ODM.ArchiveLayout( + OID="AL.AECRF", PdfFileName="c:\\users\\shume\\ae_annotated_crf.pdf") # with space - archive = ODM.ArchiveLayout(OID="AL.AECRF", PdfFileName="ae_annotated crf.pdf") + archive = ODM.ArchiveLayout( + OID="AL.AECRF", PdfFileName="ae_annotated crf.pdf") self.assertEqual(archive.PdfFileName, "ae_annotated crf.pdf") # with invalid character with self.assertRaises(ValueError): - archive = ODM.ArchiveLayout(OID="AL.AECRF", PdfFileName="ae_annotated>crf.pdf") + archive = ODM.ArchiveLayout( + OID="AL.AECRF", PdfFileName="ae_annotated>crf.pdf") def test_incomplete_datetime_type(self): # valid incomplete datetime 2004-12-01T01:01:01Z and 2004---15T-:05:- - idatetime = TextTest(Name="AETERM", Label="help", Range="4-9", TestDateTime="2004-12-01T01:01:01Z") + idatetime = TextTest(Name="AETERM", Label="help", + Range="4-9", TestDateTime="2004-12-01T01:01:01Z") self.assertEqual(idatetime.TestDateTime, "2004-12-01T01:01:01Z") - idatetime = TextTest(Name="AETERM", Label="help", Range="4-9", TestDateTime="2004---15T-:05:-") + idatetime = TextTest(Name="AETERM", Label="help", + Range="4-9", TestDateTime="2004---15T-:05:-") self.assertEqual(idatetime.TestDateTime, "2004---15T-:05:-") # invalid incomplete datetime with self.assertRaises(ValueError): - idatetime = TextTest(Name="AETERM", Label="help", Range="4-7", TestDateTime="2004---15T-:05") + idatetime = TextTest(Name="AETERM", Label="help", + Range="4-7", TestDateTime="2004---15T-:05") def test_incomplete_date_type(self): # valid incomplete date 2004-12-20 and 2004---15 and ------ - idate = TextTest(Name="AETERM", Label="help", Range="4-9", TestDate="2004-12-20") + idate = TextTest(Name="AETERM", Label="help", + Range="4-9", TestDate="2004-12-20") self.assertEqual(idate.TestDate, "2004-12-20") - idate = TextTest(Name="AETERM", Label="help", Range="4-9", TestDate="2004---15") + idate = TextTest(Name="AETERM", Label="help", + Range="4-9", TestDate="2004---15") self.assertEqual(idate.TestDate, "2004---15") - idate = TextTest(Name="AETERM", Label="help", Range="4-7", TestDate="-----") + idate = TextTest(Name="AETERM", Label="help", + Range="4-7", TestDate="-----") self.assertEqual(idate.TestDate, "-----") # invalid incomplete date with self.assertRaises(ValueError): - idate = TextTest(Name="AETERM", Label="help", Range="4-7", TestDate="----32") + idate = TextTest(Name="AETERM", Label="help", + Range="4-7", TestDate="----32") def test_incomplete_time_type(self): # valid incomplete time 2004-12-01T01:01:01Z and 2004---15T-:05:- - itime = TextTest(Name="AETERM", Label="help", Range="4-9", TestTime="01:01:01Z") + itime = TextTest(Name="AETERM", Label="help", + Range="4-9", TestTime="01:01:01Z") self.assertEqual(itime.TestTime, "01:01:01Z") - itime = TextTest(Name="AETERM", Label="help", Range="4-9", TestTime="-:05:-") + itime = TextTest(Name="AETERM", Label="help", + Range="4-9", TestTime="-:05:-") self.assertEqual(itime.TestTime, "-:05:-") # invalid incomplete datetime with self.assertRaises(ValueError): - itime = TextTest(Name="AETERM", Label="help", Range="4-7", TestTime="-:05") + itime = TextTest(Name="AETERM", Label="help", + Range="4-7", TestTime="-:05") def test_partial_datetime_type(self): # valid partial datetime - pdatetime = PartialDateTest(Name="AETERM", TestDateTime="2004-12-01T01:01:01Z") + pdatetime = PartialDateTest( + Name="AETERM", TestDateTime="2004-12-01T01:01:01Z") self.assertEqual(pdatetime.TestDateTime, "2004-12-01T01:01:01Z") pdatetime = PartialDateTest(Name="AETERM", TestDateTime="2004-12") self.assertEqual(pdatetime.TestDateTime, "2004-12") - pdatetime = PartialDateTest(Name="AETERM", TestDateTime="2004-12-05T12") + pdatetime = PartialDateTest( + Name="AETERM", TestDateTime="2004-12-05T12") self.assertEqual(pdatetime.TestDateTime, "2004-12-05T12") # invalid partial datetime with self.assertRaises(ValueError): diff --git a/tests/test_value_list_define.py b/tests/test_value_list_define.py index 26ff56c..9949c2e 100644 --- a/tests/test_value_list_define.py +++ b/tests/test_value_list_define.py @@ -6,24 +6,31 @@ class TestValueListDef(TestCase): def test_value_list_def(self): vld = DEFINE.ValueListDef(OID="VL.DA.DAORRES") - vld.ItemRef.append(DEFINE.ItemRef(ItemOID="IT.DA.DAORRES.DISPAMT", OrderNumber="1", Mandatory="Yes")) - vld.ItemRef[0].WhereClauseRef.append(DEFINE.WhereClauseRef(WhereClauseOID="WC.DA.DATESTCD.DISPAMT")) - vld.ItemRef.append(DEFINE.ItemRef(ItemOID="IT.DA.DAORRES.RETAMT", OrderNumber="2", Mandatory="No")) - vld.ItemRef[1].WhereClauseRef.append(DEFINE.WhereClauseRef(WhereClauseOID="WC.DA.DATESTCD.RETAMT")) - self.assertEqual("WC.DA.DATESTCD.RETAMT", vld.ItemRef[1].WhereClauseRef[0].WhereClauseOID) + vld.ItemRef.append(DEFINE.ItemRef( + ItemOID="IT.DA.DAORRES.DISPAMT", OrderNumber="1", Mandatory="Yes")) + vld.ItemRef[0].WhereClauseRef.append( + DEFINE.WhereClauseRef(WhereClauseOID="WC.DA.DATESTCD.DISPAMT")) + vld.ItemRef.append(DEFINE.ItemRef( + ItemOID="IT.DA.DAORRES.RETAMT", OrderNumber="2", Mandatory="No")) + vld.ItemRef[1].WhereClauseRef.append( + DEFINE.WhereClauseRef(WhereClauseOID="WC.DA.DATESTCD.RETAMT")) + self.assertEqual("WC.DA.DATESTCD.RETAMT", + vld.ItemRef[1].WhereClauseRef[0].WhereClauseOID) vld_dict = vld.to_dict() print(vld_dict) expected_dict = {'OID': 'VL.DA.DAORRES', 'ItemRef': - [{'ItemOID': 'IT.DA.DAORRES.DISPAMT', 'OrderNumber': 1, 'Mandatory': 'Yes', 'WhereClauseRef': - [{'WhereClauseOID': 'WC.DA.DATESTCD.DISPAMT'}]}, - {'ItemOID': 'IT.DA.DAORRES.RETAMT', 'OrderNumber': 2, 'Mandatory': 'No', 'WhereClauseRef': - [{'WhereClauseOID': 'WC.DA.DATESTCD.RETAMT'}]}]} + [{'ItemOID': 'IT.DA.DAORRES.DISPAMT', 'OrderNumber': 1, 'Mandatory': 'Yes', 'WhereClauseRef': + [{'WhereClauseOID': 'WC.DA.DATESTCD.DISPAMT'}]}, + {'ItemOID': 'IT.DA.DAORRES.RETAMT', 'OrderNumber': 2, 'Mandatory': 'No', 'WhereClauseRef': + [{'WhereClauseOID': 'WC.DA.DATESTCD.RETAMT'}]}]} self.assertDictEqual(vld_dict, expected_dict) def test_where_clause_def(self): wcd = DEFINE.WhereClauseDef(OID="WC.DA.DATESTCD.DISPAMT") - wcd.RangeCheck.append(DEFINE.RangeCheck(SoftHard="Soft", ItemOID="IT.DA.DATESTCD", Comparator="EQ")) - wcd.RangeCheck[0].CheckValue.append(DEFINE.CheckValue(_content="DISPAMT")) + wcd.RangeCheck.append(DEFINE.RangeCheck( + SoftHard="Soft", ItemOID="IT.DA.DATESTCD", Comparator="EQ")) + wcd.RangeCheck[0].CheckValue.append( + DEFINE.CheckValue(_content="DISPAMT")) self.assertEqual("IT.DA.DATESTCD", wcd.RangeCheck[0].ItemOID) wcd_xml = wcd.to_xml() print(ET.tostring(wcd_xml, encoding='utf8', method='xml')) @@ -36,10 +43,11 @@ def test_leaf(self): leaf_dict = leaf.to_dict() print(leaf_dict) expected_dict = {'ID': 'LF.blankcrf', 'href': 'blankcrf.pdf', 'title': - {'_content': 'Annotated Case Report Form'}} + {'_content': 'Annotated Case Report Form'}} self.assertDictEqual(leaf_dict, expected_dict) def test_comment_def(self): comment = DEFINE.CommentDef(OID="COM.VS.VISITNUM") - comment.Description.TranslatedText.append(DEFINE.TranslatedText(_content="Assigned from the TV domain based on the VISIT", lang="en")) + comment.Description.TranslatedText.append(DEFINE.TranslatedText( + _content="Assigned from the TV domain based on the VISIT", lang="en")) self.assertEqual("COM.VS.VISITNUM", comment.OID) From 4b7c63c6ec5e2effb6509a53a4380b9b2a027ebf Mon Sep 17 00:00:00 2001 From: Geoff Low Date: Wed, 20 Jul 2022 16:00:17 +0100 Subject: [PATCH 14/26] Avoid mutable args --- tests/test_oid_ref.py | 104 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 104 insertions(+) create mode 100644 tests/test_oid_ref.py diff --git a/tests/test_oid_ref.py b/tests/test_oid_ref.py new file mode 100644 index 0000000..699c7ac --- /dev/null +++ b/tests/test_oid_ref.py @@ -0,0 +1,104 @@ + + +from re import S +from unittest import TestCase + + + +class TestOIDRefODM(TestCase): + + def test_default_skipelem(self): + from odmlib.odm_1_3_2.rules.oid_ref import OIDRef + oidref = OIDRef() + self.assertEqual(oidref.skip_elem, ["ODM"]) + + + def test_default_skipelem_plus(self): + from odmlib.odm_1_3_2.rules.oid_ref import OIDRef + oidref = OIDRef(skip_elems=["MetaDataVersion"]) + self.assertEqual(sorted(oidref.skip_elem), sorted(["ODM", "MetaDataVersion"])) + + def test_default_skipattr(self): + from odmlib.odm_1_3_2.rules.oid_ref import OIDRef + oidref = OIDRef() + self.assertEqual(oidref.skip_attr, ["FileOID", "PriorFileOID"]) + + + def test_default_skipattr_plus(self): + from odmlib.odm_1_3_2.rules.oid_ref import OIDRef + oidref = OIDRef(skip_attrs=["LastUpdated"]) + self.assertEqual(sorted(oidref.skip_attr), sorted(["LastUpdated", "FileOID", "PriorFileOID"])) + + +class TestOIDRefDefine2(TestCase): + + DEFAULT_ATTRS = [ + "FileOID", + "PriorFileOID", + "StudyOID", + "MetaDataVersionOID", + "ItemGroupOID", + ] + DEFAULT_ELEMS = [ + "ODM", + "Study", + "MetaDataVersion", + "ItemGroupDef", + ] + def test_default_skipelem(self): + from odmlib.define_2_0.rules.oid_ref import OIDRef + oidref = OIDRef() + self.assertEqual(oidref.skip_elem, self.DEFAULT_ELEMS) + + + def test_default_skipelem_plus(self): + from odmlib.define_2_0.rules.oid_ref import OIDRef + oidref = OIDRef(skip_elems=["ItemRef"]) + self.assertEqual(sorted(oidref.skip_elem), sorted(self.DEFAULT_ELEMS + ["ItemRef"])) + + def test_default_skipattr(self): + from odmlib.define_2_0.rules.oid_ref import OIDRef + oidref = OIDRef() + self.assertEqual(oidref.skip_attr, self.DEFAULT_ATTRS) + + def test_default_skipattr_plus(self): + from odmlib.define_2_0.rules.oid_ref import OIDRef + oidref = OIDRef(skip_attrs=["LastUpdated"]) + self.assertEqual(sorted(oidref.skip_attr), sorted(self.DEFAULT_ATTRS + ["LastUpdated"])) + +class TestOIDRefDefine21(TestCase): + + DEFAULT_ATTRS = [ + "FileOID", + "PriorFileOID", + "StudyOID", + "MetaDataVersionOID", + "ItemGroupOID", + ] + DEFAULT_ELEMS = [ + "ODM", + "Study", + "MetaDataVersion", + "ItemGroupDef", + ] + def test_default_skipelem(self): + from odmlib.define_2_1.rules.oid_ref import OIDRef + oidref = OIDRef() + self.assertEqual(oidref.skip_elem, self.DEFAULT_ELEMS) + + + def test_default_skipelem_plus(self): + from odmlib.define_2_1.rules.oid_ref import OIDRef + oidref = OIDRef(skip_elems=["ItemRef"]) + self.assertEqual(sorted(oidref.skip_elem), sorted(self.DEFAULT_ELEMS + ["ItemRef"])) + + def test_default_skipattr(self): + from odmlib.define_2_1.rules.oid_ref import OIDRef + oidref = OIDRef() + self.assertEqual(oidref.skip_attr, self.DEFAULT_ATTRS) + + def test_default_skipattr_plus(self): + from odmlib.define_2_1.rules.oid_ref import OIDRef + oidref = OIDRef(skip_attrs=["LastUpdated"]) + self.assertEqual(sorted(oidref.skip_attr), sorted(self.DEFAULT_ATTRS + ["LastUpdated"])) + From f734e4f85718fba6d82c0641fef220119e724d63 Mon Sep 17 00:00:00 2001 From: Geoff Low Date: Fri, 22 Jul 2022 15:21:52 +0100 Subject: [PATCH 15/26] Added TOX --- tox.ini | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/tox.ini b/tox.ini index b2f844b..e68ef47 100644 --- a/tox.ini +++ b/tox.ini @@ -1,10 +1,10 @@ # content of: tox.ini , put in same dir as setup.py [tox] -envlist = py37, py38, py39, py310,flake8 +envlist = py37, py38, py39, py310, flake8 [flake8] extend-exclude = N812 -exclude = .git,__pycache__,build,dist +exclude = .git, __pycache__, build, dist max-complexity = 10 max-line-length = 120 @@ -14,12 +14,19 @@ skip_install = true deps = flake8 flake8-bugbear - flake8-docstrings>=1.3.1 - flake8-typing-imports>=1.1 - pep8-naming + flakeheaven commands = flake8 odmlib/ setup.py +[tool.flakeheaven] +# make output nice +format = "grouped" +# 80 chars aren't enough in 21 century +max_line_length = 90 +# show line of source code in output +show_source = true + + # Note `python setup.py test` is deprecated [testenv] deps = -rrequirements-dev.txt From 5493e7669d5515eb4dfb587cd4b299e26de827a4 Mon Sep 17 00:00:00 2001 From: Geoff Low Date: Fri, 22 Jul 2022 15:22:20 +0100 Subject: [PATCH 16/26] Move the version decl into the module --- odmlib/__init__.py | 2 +- setup.py | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/odmlib/__init__.py b/odmlib/__init__.py index 48fef32..1276d02 100644 --- a/odmlib/__init__.py +++ b/odmlib/__init__.py @@ -1 +1 @@ -__version__ = "0.1.2" \ No newline at end of file +__version__ = "0.1.5" diff --git a/setup.py b/setup.py index fa16418..1d6854a 100644 --- a/setup.py +++ b/setup.py @@ -1,12 +1,15 @@ +import importlib from setuptools import setup import pathlib HERE = pathlib.Path(__file__).parent README = (HERE / "README.md").read_text() +module = importlib.import_module("odmlib") + setup( name='odmlib', - version='0.1.4', + version=module.__version__, packages=['odmlib', 'tests', 'odmlib.odm_1_3_2', 'odmlib.odm_1_3_2.rules', 'odmlib.define_2_0', 'odmlib.define_2_0.rules', 'odmlib.define_2_1', From ffcefbc85c30aab6ddfff70f695e8dcbf9d3618d Mon Sep 17 00:00:00 2001 From: Geoff Low Date: Fri, 22 Jul 2022 15:30:32 +0100 Subject: [PATCH 17/26] Fixtures --- tests/data/ae_test.json | 2 +- tests/data/ae_test.xml | 2 +- tests/data/defineV21-SDTM-test.json | 2 +- tests/data/defineV21-SDTM-test.xml | 2 +- tests/data/test_clinical_data_01.xml | 2 +- tests/data/test_referece_data_01.xml | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/data/ae_test.json b/tests/data/ae_test.json index 73aae30..050e372 100644 --- a/tests/data/ae_test.json +++ b/tests/data/ae_test.json @@ -1 +1 @@ -{"FileOID": "ODM.DATASET.001", "AsOfDateTime": "2022-04-06T10:11:06.012099+00:00", "DatasetXMLVersion": "1.0.0", "CreationDateTime": "2022-04-06T10:11:06.012099+00:00", "ODMVersion": "1.3.2", "FileType": "Snapshot", "Originator": "swhume", "SourceSystem": "odmlib", "SourceSystemVersion": "0.1", "ClinicalData": {"StudyOID": "cdisc.odmlib.001", "MetaDataVersionOID": "MDV.001", "ItemGroupData": [{"ItemGroupOID": "IG.AE", "ItemGroupDataSeq": 1, "ItemData": [{"ItemOID": "IT.STUDYID", "Value": "CDISC01"}, {"ItemOID": "IT.AE.DOMAIN", "Value": "AE"}, {"ItemOID": "IT.USUBJID", "Value": "CDISC01.100008"}, {"ItemOID": "IT.AE.AESEQ", "Value": "1"}, {"ItemOID": "IT.AE.AETERM", "Value": "AGITATED"}, {"ItemOID": "IT.AE.AEMODIFY", "Value": "AGITATION"}, {"ItemOID": "IT.AE.AEDECOD", "Value": "Agitation"}, {"ItemOID": "IT.AE.AESEV", "Value": "MILD"}, {"ItemOID": "IT.AE.AESER", "Value": "N"}, {"ItemOID": "IT.AE.AEACN", "Value": "DOSE NOT CHANGED"}, {"ItemOID": "IT.AE.AEREL", "Value": "POSSIBLY RELATED"}]}, {"ItemGroupOID": "IG.AE", "ItemGroupDataSeq": 2, "ItemData": [{"ItemOID": "IT.STUDYID", "Value": "CDISC01"}, {"ItemOID": "IT.AE.DOMAIN", "Value": "AE"}, {"ItemOID": "IT.USUBJID", "Value": "CDISC01.100008"}, {"ItemOID": "IT.AE.AESEQ", "Value": "2"}, {"ItemOID": "IT.AE.AETERM", "Value": "ANXIETY"}, {"ItemOID": "IT.AE.AEMODIFY", "Value": "AGITATION"}, {"ItemOID": "IT.AE.AEDECOD", "Value": "Anxiety"}, {"ItemOID": "IT.AE.AESEV", "Value": "MODERATE"}, {"ItemOID": "IT.AE.AESER", "Value": "N"}, {"ItemOID": "IT.AE.AEACN", "Value": "DOSE NOT CHANGED"}, {"ItemOID": "IT.AE.AEREL", "Value": "POSSIBLY RELATED"}]}]}} \ No newline at end of file +{"FileOID": "ODM.DATASET.001", "AsOfDateTime": "2022-07-22T14:21:26.865401+00:00", "DatasetXMLVersion": "1.0.0", "CreationDateTime": "2022-07-22T14:21:26.865401+00:00", "ODMVersion": "1.3.2", "FileType": "Snapshot", "Originator": "swhume", "SourceSystem": "odmlib", "SourceSystemVersion": "0.1", "ClinicalData": {"StudyOID": "cdisc.odmlib.001", "MetaDataVersionOID": "MDV.001", "ItemGroupData": [{"ItemGroupOID": "IG.AE", "ItemGroupDataSeq": 1, "ItemData": [{"ItemOID": "IT.STUDYID", "Value": "CDISC01"}, {"ItemOID": "IT.AE.DOMAIN", "Value": "AE"}, {"ItemOID": "IT.USUBJID", "Value": "CDISC01.100008"}, {"ItemOID": "IT.AE.AESEQ", "Value": "1"}, {"ItemOID": "IT.AE.AETERM", "Value": "AGITATED"}, {"ItemOID": "IT.AE.AEMODIFY", "Value": "AGITATION"}, {"ItemOID": "IT.AE.AEDECOD", "Value": "Agitation"}, {"ItemOID": "IT.AE.AESEV", "Value": "MILD"}, {"ItemOID": "IT.AE.AESER", "Value": "N"}, {"ItemOID": "IT.AE.AEACN", "Value": "DOSE NOT CHANGED"}, {"ItemOID": "IT.AE.AEREL", "Value": "POSSIBLY RELATED"}]}, {"ItemGroupOID": "IG.AE", "ItemGroupDataSeq": 2, "ItemData": [{"ItemOID": "IT.STUDYID", "Value": "CDISC01"}, {"ItemOID": "IT.AE.DOMAIN", "Value": "AE"}, {"ItemOID": "IT.USUBJID", "Value": "CDISC01.100008"}, {"ItemOID": "IT.AE.AESEQ", "Value": "2"}, {"ItemOID": "IT.AE.AETERM", "Value": "ANXIETY"}, {"ItemOID": "IT.AE.AEMODIFY", "Value": "AGITATION"}, {"ItemOID": "IT.AE.AEDECOD", "Value": "Anxiety"}, {"ItemOID": "IT.AE.AESEV", "Value": "MODERATE"}, {"ItemOID": "IT.AE.AESER", "Value": "N"}, {"ItemOID": "IT.AE.AEACN", "Value": "DOSE NOT CHANGED"}, {"ItemOID": "IT.AE.AEREL", "Value": "POSSIBLY RELATED"}]}]}} \ No newline at end of file diff --git a/tests/data/ae_test.xml b/tests/data/ae_test.xml index 400ed14..bbf0cce 100644 --- a/tests/data/ae_test.xml +++ b/tests/data/ae_test.xml @@ -1,2 +1,2 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/tests/data/defineV21-SDTM-test.json b/tests/data/defineV21-SDTM-test.json index 7c4375a..4d152a5 100644 --- a/tests/data/defineV21-SDTM-test.json +++ b/tests/data/defineV21-SDTM-test.json @@ -1 +1 @@ -{"FileOID": "DEFINE.TEST.IGD.001", "FileType": "Snapshot", "AsOfDateTime": "2022-04-06T10:11:06.421435+00:00", "CreationDateTime": "2022-04-06T10:11:06.421445+00:00", "ODMVersion": "1.3.2", "Originator": "CDISC 360", "SourceSystem": "WS2", "SourceSystemVersion": "0.1", "Context": "Other", "Study": {"OID": "ST.TEST.IGD.001", "GlobalVariables": {"StudyName": {"_content": "TEST ODM ItemGroupDef"}, "StudyDescription": {"_content": "ItemGroupDef 001"}, "ProtocolName": {"_content": "ODM ItemGroupDef"}}, "MetaDataVersion": {"OID": "MDV.TEST.IGD.001", "Name": "ItemGroupDefTest001", "Description": "ItemGroupDef Test 001", "DefineVersion": "2.1.0", "ItemGroupDef": [{"OID": "IG.VS", "Name": "VS", "Repeating": "Yes", "Domain": "VS", "SASDatasetName": "VS", "IsReferenceData": "No", "Purpose": "Tabulation", "ArchiveLocationID": "LF.VS", "Structure": "One record per vital sign measurement per visit per subject", "StandardOID": "STD.1", "IsNonStandard": "Yes", "HasNoData": "Yes", "Description": {"TranslatedText": [{"_content": "this is the first test description", "lang": "en"}]}, "ItemRef": [{"ItemOID": "IT.STUDYID", "Mandatory": "Yes", "OrderNumber": 1, "KeySequence": 1}, {"ItemOID": "IT.TA.DOMAIN", "Mandatory": "Yes", "OrderNumber": 2}, {"ItemOID": "IT.TA.ARMCD", "Mandatory": "Yes", "OrderNumber": 3, "KeySequence": 2}, {"ItemOID": "IT.TA.ARM", "Mandatory": "Yes", "OrderNumber": 4}, {"ItemOID": "IT.TA.TAETORD", "Mandatory": "Yes", "OrderNumber": 5, "KeySequence": 3}, {"ItemOID": "IT.TA.ETCD", "Mandatory": "Yes", "OrderNumber": 6}, {"ItemOID": "IT.TA.ELEMENT", "Mandatory": "No", "OrderNumber": 7}, {"ItemOID": "IT.TA.TABRANCH", "Mandatory": "No", "OrderNumber": 8}, {"ItemOID": "IT.TA.TATRANS", "Mandatory": "No", "OrderNumber": 9}, {"ItemOID": "IT.TA.EPOCH", "Mandatory": "No", "OrderNumber": 10}]}]}}} \ No newline at end of file +{"FileOID": "DEFINE.TEST.IGD.001", "FileType": "Snapshot", "AsOfDateTime": "2022-07-22T14:21:27.351123+00:00", "CreationDateTime": "2022-07-22T14:21:27.351134+00:00", "ODMVersion": "1.3.2", "Originator": "CDISC 360", "SourceSystem": "WS2", "SourceSystemVersion": "0.1", "Context": "Other", "Study": {"OID": "ST.TEST.IGD.001", "GlobalVariables": {"StudyName": {"_content": "TEST ODM ItemGroupDef"}, "StudyDescription": {"_content": "ItemGroupDef 001"}, "ProtocolName": {"_content": "ODM ItemGroupDef"}}, "MetaDataVersion": {"OID": "MDV.TEST.IGD.001", "Name": "ItemGroupDefTest001", "Description": "ItemGroupDef Test 001", "DefineVersion": "2.1.0", "ItemGroupDef": [{"OID": "IG.VS", "Name": "VS", "Repeating": "Yes", "Domain": "VS", "SASDatasetName": "VS", "IsReferenceData": "No", "Purpose": "Tabulation", "ArchiveLocationID": "LF.VS", "Structure": "One record per vital sign measurement per visit per subject", "StandardOID": "STD.1", "IsNonStandard": "Yes", "HasNoData": "Yes", "Description": {"TranslatedText": [{"_content": "this is the first test description", "lang": "en"}]}, "ItemRef": [{"ItemOID": "IT.STUDYID", "Mandatory": "Yes", "OrderNumber": 1, "KeySequence": 1}, {"ItemOID": "IT.TA.DOMAIN", "Mandatory": "Yes", "OrderNumber": 2}, {"ItemOID": "IT.TA.ARMCD", "Mandatory": "Yes", "OrderNumber": 3, "KeySequence": 2}, {"ItemOID": "IT.TA.ARM", "Mandatory": "Yes", "OrderNumber": 4}, {"ItemOID": "IT.TA.TAETORD", "Mandatory": "Yes", "OrderNumber": 5, "KeySequence": 3}, {"ItemOID": "IT.TA.ETCD", "Mandatory": "Yes", "OrderNumber": 6}, {"ItemOID": "IT.TA.ELEMENT", "Mandatory": "No", "OrderNumber": 7}, {"ItemOID": "IT.TA.TABRANCH", "Mandatory": "No", "OrderNumber": 8}, {"ItemOID": "IT.TA.TATRANS", "Mandatory": "No", "OrderNumber": 9}, {"ItemOID": "IT.TA.EPOCH", "Mandatory": "No", "OrderNumber": 10}]}]}}} \ No newline at end of file diff --git a/tests/data/defineV21-SDTM-test.xml b/tests/data/defineV21-SDTM-test.xml index 2d8684a..61f19e6 100644 --- a/tests/data/defineV21-SDTM-test.xml +++ b/tests/data/defineV21-SDTM-test.xml @@ -1,2 +1,2 @@ -TEST ODM ItemGroupDefItemGroupDef 001ODM ItemGroupDefthis is the first test description \ No newline at end of file +TEST ODM ItemGroupDefItemGroupDef 001ODM ItemGroupDefthis is the first test description \ No newline at end of file diff --git a/tests/data/test_clinical_data_01.xml b/tests/data/test_clinical_data_01.xml index 2890f65..db17929 100644 --- a/tests/data/test_clinical_data_01.xml +++ b/tests/data/test_clinical_data_01.xml @@ -1,2 +1,2 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/tests/data/test_referece_data_01.xml b/tests/data/test_referece_data_01.xml index ff7a831..5e30e0e 100644 --- a/tests/data/test_referece_data_01.xml +++ b/tests/data/test_referece_data_01.xml @@ -1,2 +1,2 @@ - \ No newline at end of file + \ No newline at end of file From eece9c343e15c92c8cb031b31e8d0a30a049d2bc Mon Sep 17 00:00:00 2001 From: Geoff Low Date: Fri, 22 Jul 2022 15:30:58 +0100 Subject: [PATCH 18/26] Reformatting --- odmlib/odm_2_0/model.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/odmlib/odm_2_0/model.py b/odmlib/odm_2_0/model.py index 279d98e..264d3e6 100644 --- a/odmlib/odm_2_0/model.py +++ b/odmlib/odm_2_0/model.py @@ -223,8 +223,8 @@ class CodeListRef(OE.ODMElement): class ItemDef(OE.ODMElement): - """ - represents ODM v2.0 ItemDef and can serialize as JSON or XML + """ + represents ODM v2.0 ItemDef and can serialize as JSON or XML - ordering of properties matters """ OID = T.OID(required=True) From f7c1bdf1339fc760d199a83b70299eebc63d0203 Mon Sep 17 00:00:00 2001 From: Geoff Low Date: Fri, 22 Jul 2022 15:31:23 +0100 Subject: [PATCH 19/26] Avoid mutable args --- odmlib/define_2_0/rules/oid_ref.py | 10 +++++----- odmlib/define_2_1/rules/oid_ref.py | 10 +++++----- odmlib/odm_1_3_2/rules/oid_ref.py | 7 ++++--- 3 files changed, 14 insertions(+), 13 deletions(-) diff --git a/odmlib/define_2_0/rules/oid_ref.py b/odmlib/define_2_0/rules/oid_ref.py index e7ff069..4449160 100644 --- a/odmlib/define_2_0/rules/oid_ref.py +++ b/odmlib/define_2_0/rules/oid_ref.py @@ -1,5 +1,5 @@ class OIDRef: - def __init__(self, skip_attrs=[], skip_elems=[]): + def __init__(self, skip_attrs=None, skip_elems=None): self.oid = {} self.oid_ref = {} self._init_oid_ref() @@ -7,19 +7,19 @@ def __init__(self, skip_attrs=[], skip_elems=[]): self._init_ref_def() self.def_ref = {} self._init_def_ref() - self.skip_attr = [ + self.skip_attr = (skip_attrs if skip_attrs else []) + [ "FileOID", "PriorFileOID", "StudyOID", "MetaDataVersionOID", "ItemGroupOID", - ] + skip_attrs - self.skip_elem = [ + ] + self.skip_elem = (skip_elems if skip_elems else []) + [ "ODM", "Study", "MetaDataVersion", "ItemGroupDef", - ] + skip_elems + ] self.is_verified = False def add_oid(self, oid, element): diff --git a/odmlib/define_2_1/rules/oid_ref.py b/odmlib/define_2_1/rules/oid_ref.py index 942617d..125cadb 100644 --- a/odmlib/define_2_1/rules/oid_ref.py +++ b/odmlib/define_2_1/rules/oid_ref.py @@ -1,5 +1,5 @@ class OIDRef: - def __init__(self, skip_attrs=[], skip_elems=[]): + def __init__(self, skip_attrs=None, skip_elems=None): self.oid = {} self.oid_ref = {} self._init_oid_ref() @@ -7,19 +7,19 @@ def __init__(self, skip_attrs=[], skip_elems=[]): self._init_ref_def() self.def_ref = {} self._init_def_ref() - self.skip_attr = [ + self.skip_attr = (skip_attrs if skip_attrs else []) + [ "FileOID", "PriorFileOID", "StudyOID", "MetaDataVersionOID", "ItemGroupOID", - ] + skip_attrs - self.skip_elem = [ + ] + self.skip_elem = (skip_elems if skip_elems else []) + [ "ODM", "Study", "MetaDataVersion", "ItemGroupDef", - ] + skip_elems + ] self.is_verified = False def add_oid(self, oid, element): diff --git a/odmlib/odm_1_3_2/rules/oid_ref.py b/odmlib/odm_1_3_2/rules/oid_ref.py index e4cfbc8..1591621 100644 --- a/odmlib/odm_1_3_2/rules/oid_ref.py +++ b/odmlib/odm_1_3_2/rules/oid_ref.py @@ -1,5 +1,5 @@ class OIDRef: - def __init__(self, skip_attrs=[], skip_elems=[]): + def __init__(self, skip_attrs=None, skip_elems=None): self.oid = {} self.oid_ref = {} self._init_oid_ref() @@ -7,8 +7,9 @@ def __init__(self, skip_attrs=[], skip_elems=[]): self._init_ref_def() self.def_ref = {} self._init_def_ref() - self.skip_attr = ["FileOID", "PriorFileOID"] + skip_attrs - self.skip_elem = ["ODM"] + skip_elems + self.skip_attr = (skip_attrs if skip_attrs else []) + ["FileOID", "PriorFileOID"] + self.skip_elem = (skip_elems if skip_elems else []) + ["ODM"] + self.is_verified = False self.is_verified = False def add_oid(self, oid, element): From 0f8299b9dee69349d596d01b7b781a636044545f Mon Sep 17 00:00:00 2001 From: Geoff Low Date: Fri, 22 Jul 2022 15:31:43 +0100 Subject: [PATCH 20/26] remove duplication --- odmlib/odm_1_3_2/rules/metadata_schema.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/odmlib/odm_1_3_2/rules/metadata_schema.py b/odmlib/odm_1_3_2/rules/metadata_schema.py index 7c14446..4c31cf0 100644 --- a/odmlib/odm_1_3_2/rules/metadata_schema.py +++ b/odmlib/odm_1_3_2/rules/metadata_schema.py @@ -683,11 +683,6 @@ def _set_metadata_registry(): "CreationDateTime": {"type": "string", "required": True}, "PriorFileOID": {"type": "string"}, "AsOfDateTime": {"type": "string"}, - "FileType": { - "type": "string", - "required": True, - "allowed": ["Snapshot", "Transactional"], - }, "ODMVersion": {"type": "string", "allowed": ["1.3.2", "1.3"]}, "Originator": {"type": "string"}, "SourceSystem": {"type": "string"}, From 18e5ea3355697aa77f0d09793b454580642f9f81 Mon Sep 17 00:00:00 2001 From: Geoff Low Date: Fri, 22 Jul 2022 15:32:24 +0100 Subject: [PATCH 21/26] Clash of class and import --- odmlib/dataset_1_0_1/model.py | 1 - 1 file changed, 1 deletion(-) diff --git a/odmlib/dataset_1_0_1/model.py b/odmlib/dataset_1_0_1/model.py index dfb35a0..67c2516 100644 --- a/odmlib/dataset_1_0_1/model.py +++ b/odmlib/dataset_1_0_1/model.py @@ -1,4 +1,3 @@ -import odmlib.odm_1_3_2.model as ODM import odmlib.odm_element as OE import odmlib.typed as T import odmlib.ns_registry as NS From 251ee846a01c21a8725e3c12e03067368b7b685d Mon Sep 17 00:00:00 2001 From: Geoff Low Date: Fri, 22 Jul 2022 15:32:51 +0100 Subject: [PATCH 22/26] Unused import, flake8 fixes --- odmlib/odm_loader.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/odmlib/odm_loader.py b/odmlib/odm_loader.py index 40a5998..805f8b6 100644 --- a/odmlib/odm_loader.py +++ b/odmlib/odm_loader.py @@ -5,7 +5,7 @@ import odmlib.ns_registry as NS import json import importlib -import xml.etree.ElementTree as ET + ODM_PREFIX = "odm:" ODM_NS = {"odm": "http://www.cdisc.org/ns/odm/v1.3"} @@ -28,12 +28,12 @@ def load_document(self, odm_dict, key): for k, v in odm_obj_items: if type(v).__name__ == "ODMObject": if k in odm_dict: - odm_child_obj = self.load_document(odm_dict[k], k) + odm_child_obj = self.load_document(odm_dict[k], k) # noqa: F841 exec("odm_obj." + k + " = odm_child_obj") elif type(v).__name__ == "ODMListObject": if k in odm_dict: for val in odm_dict[k]: - odm_child_obj = self.load_document(val, k) + odm_child_obj = self.load_document(val, k) # noqa: F841 eval("odm_obj." + k + ".append(odm_child_obj)") return odm_obj @@ -108,12 +108,12 @@ def load_document(self, elem, *args): namespace = self.nsr.get_ns_entry_dict(v.namespace) e = elem.find(v.namespace + ":" + k, namespace) if e is not None: - odm_child_obj = self.load_document(e) + odm_child_obj = self.load_document(e) # noqa: F841 exec("odm_obj." + k + " = odm_child_obj") elif type(v).__name__ == "ODMListObject": namespace = self.nsr.get_ns_entry_dict(v.namespace) for e in elem.findall(v.namespace + ":" + k, namespace): - odm_child_obj = self.load_document(e) + odm_child_obj = self.load_document(e) # noqa: F841 eval("odm_obj." + k + ".append(odm_child_obj)") return odm_obj From 5f2aaeab9526c853181a53aa5430658ae8ed64bb Mon Sep 17 00:00:00 2001 From: Geoff Low Date: Fri, 22 Jul 2022 15:33:14 +0100 Subject: [PATCH 23/26] flake8 fixes --- odmlib/odm_element.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/odmlib/odm_element.py b/odmlib/odm_element.py index 3ad5b80..0f79144 100644 --- a/odmlib/odm_element.py +++ b/odmlib/odm_element.py @@ -81,7 +81,7 @@ def __init__(self, **kwargs): if name not in self.__class__.__dict__.keys(): # strip out non-default elementtree namespaces from the XML to work with just the name e.g. xml:lang if "}" in name: - name = name[name.find("}") + 1 :] + name = name[name.find("}") + 1:] else: raise TypeError( f"Unknown keyword argument {name} in {self.__class__.__name__}" @@ -158,7 +158,7 @@ def to_xml(self, parent_elem=None, top_elem=None): # add text to element if it exists if "_content" in self.__dict__: odm_elem.text = self.__dict__["_content"] - for name, obj in self.__dict__.items(): + for _name, obj in self.__dict__.items(): # process each element in a list of ELEMENTS if isinstance(obj, list) and obj: for o in obj: @@ -338,10 +338,10 @@ def verify_order(self): elem_list = [elem for elem in self._elems if elem in obj_list] if obj_list != elem_list: raise ValueError( - f"The order of elements in {self.__class__.__name__} "\ - f"should be {', '.join([key for key in self._elems.keys()])}" + f"The order of elements in {self.__class__.__name__} " + f"should be {', '.join([key for key in self._elems.keys()])}" ) - for attr, obj in odm_content.items(): + for _attr, obj in odm_content.items(): if isinstance(obj, ODMElement): obj.verify_order() elif isinstance(obj, list): @@ -351,7 +351,7 @@ def verify_order(self): def reorder_object(self): ordered_obj = OrderedDict() - for model_elem_name, model_elem_obj in self._elems.items(): + for model_elem_name, _model_elem_obj in self._elems.items(): if model_elem_name in self.__dict__: obj = self.__dict__.pop(model_elem_name) ordered_obj[model_elem_name] = obj From cc9846bd5ca6c6d23bf5eee7c168f3b1ecda01e2 Mon Sep 17 00:00:00 2001 From: Geoff Low Date: Fri, 22 Jul 2022 15:33:33 +0100 Subject: [PATCH 24/26] flake8 fixes --- odmlib/define_loader.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/odmlib/define_loader.py b/odmlib/define_loader.py index 055bdce..9722e95 100644 --- a/odmlib/define_loader.py +++ b/odmlib/define_loader.py @@ -34,12 +34,12 @@ def load_document(self, elem, *args): namespace = self.nsr.get_ns_entry_dict(v.namespace) e = elem.find(v.namespace + ":" + k, namespace) if e is not None: - odm_child_obj = self.load_document(e) + odm_child_obj = self.load_document(e) # noqa: F841 exec("odm_obj." + k + " = odm_child_obj") elif type(v).__name__ == "ODMListObject": namespace = self.nsr.get_ns_entry_dict(v.namespace) for e in elem.findall(v.namespace + ":" + k, namespace): - odm_child_obj = self.load_document(e) + odm_child_obj = self.load_document(e) # noqa: F841 eval("odm_obj." + k + ".append(odm_child_obj)") return odm_obj @@ -98,12 +98,12 @@ def load_document(self, odm_dict, key): for k, v in odm_obj_items: if type(v).__name__ == "ODMObject": if k in odm_dict: - odm_child_obj = self.load_document(odm_dict[k], k) + odm_child_obj = self.load_document(odm_dict[k], k) # noqa: F841 exec("odm_obj." + k + " = odm_child_obj") elif type(v).__name__ == "ODMListObject": if k in odm_dict: for val in odm_dict[k]: - odm_child_obj = self.load_document(val, k) + odm_child_obj = self.load_document(val, k) # noqa: F841 eval("odm_obj." + k + ".append(odm_child_obj)") return odm_obj From 469a0cfdaa71602e580b880ad3ecb2e3ece28c0f Mon Sep 17 00:00:00 2001 From: Geoff Low Date: Fri, 22 Jul 2022 15:33:49 +0100 Subject: [PATCH 25/26] no mutable args --- odmlib/descriptor.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/odmlib/descriptor.py b/odmlib/descriptor.py index 356df71..88f4c82 100644 --- a/odmlib/descriptor.py +++ b/odmlib/descriptor.py @@ -4,13 +4,14 @@ def __init__( name=None, required=False, element_class=None, - valid_values=[], + valid_values=None, namespace="odm", ): self.name = name self.required = required self.element_class = element_class - self.valid_values = valid_values + self.valid_values = valid_values if valid_values else [] + self.namespace = namespace self.namespace = namespace def __get__(self, instance, cls): From c709ce6b9ac205b64f819ed72e56b94076b5754a Mon Sep 17 00:00:00 2001 From: Geoff Low Date: Fri, 22 Jul 2022 15:42:08 +0100 Subject: [PATCH 26/26] Added flake8 configuration --- .flake8 | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 .flake8 diff --git a/.flake8 b/.flake8 new file mode 100644 index 0000000..531d150 --- /dev/null +++ b/.flake8 @@ -0,0 +1,2 @@ +[flake8] +exclude = .git,__pycache__, docs/source/conf.py, old, build, dist, tests