Skip to content

Commit 667ab48

Browse files
authored
Merge pull request #74 from cdisc-org/main
Update sdtm-define.xml branch from main
2 parents 53ca4c8 + 01f63f3 commit 667ab48

25 files changed

Lines changed: 23631 additions & 505 deletions

src/generators/define/__init__.py

Whitespace-only changes.

src/generators/define/annotatedCRF.py

Lines changed: 8 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44

55
class AnnotatedCRF(define_object.DefineObject):
6-
""" create a Define-XML v2.1 leaf element template """
6+
""" create Define-XML v2.1 AnnotatedCRF and leaf element objects """
77
def __init__(self):
88
super().__init__()
99

@@ -15,15 +15,13 @@ def create_define_objects(self, template, define_objects, lang, acrf):
1515
:param lang: xml:lang setting for TranslatedText
1616
:param acrf: part of the common interface but not used by this class
1717
"""
18-
self.logger.info("in annotatedCRF...")
1918
self.lang = lang
20-
define_objects["AnnotatedCRF"] = []
21-
define_objects["leaf"] = []
2219
for doc in template:
2320
a_crf = self._create_acrf_object(doc)
2421
define_objects["AnnotatedCRF"].append(a_crf)
2522
leaf = self._create_leaf_object(doc)
26-
define_objects["leaf"].append(leaf)
23+
if self.find_object(define_objects["leaf"], leaf.ID) is None:
24+
define_objects["leaf"].append(leaf)
2725

2826
@staticmethod
2927
def _create_acrf_object(doc):
@@ -33,7 +31,7 @@ def _create_acrf_object(doc):
3331
:return: a leaf odmlib template
3432
"""
3533
acrf = DEFINE.AnnotatedCRF()
36-
doc_ref = DEFINE.DocumentRef(leafID=doc["leafID"])
34+
doc_ref = DEFINE.DocumentRef(leafID=doc.get("leafID", "LF.acrf"))
3735
acrf.DocumentRef = doc_ref
3836
return acrf
3937

@@ -45,9 +43,7 @@ def _create_leaf_object(doc):
4543
:return: a leaf odmlib template
4644
"""
4745
href = doc.get("href", "acrf.pdf")
48-
id = "LF.acrf"
49-
# TODO how generate the ID correctly?
50-
lf = DEFINE.leaf(ID=id, href=href)
51-
title = DEFINE.title(_content=doc["title"])
52-
lf.title = title
53-
return lf
46+
leaf_id = doc.get("leafID", "LF.acrf")
47+
lf = DEFINE.leaf(ID=leaf_id, href=href)
48+
lf.title = DEFINE.title(_content=doc.get("title", "Annotated CRF"))
49+
return lf

src/generators/define/codeLists.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,13 @@ def create_define_objects(
2626
:param acrf: part of the common interface but not used by this class
2727
"""
2828
self.lang = lang
29-
define_objects["CodeList"] = []
3029
for cl in template:
3130
# TODO template missing the NCI c-codes for codelists and terms
31+
cl_oid = self.require_key(cl, "OID", "CodeList")
32+
# Dedup CodeLists by OID so two datasets that reference the same codelist
33+
# don't land duplicate definitions in the output.
34+
if self.find_object(define_objects["CodeList"], cl_oid) is not None:
35+
continue
3236
cl_defn = self._create_codelist_object(cl)
3337
coding = cl.get("coding", [])
3438
cl_c_code = coding[0].get("code") if coding else None
@@ -58,9 +62,7 @@ def _add_codelist_to_objects(cl_c_code, cl, objects):
5862
if cl_c_code:
5963
alias = DEFINE.Alias(Context="nci:ExtCodeID", Name=cl_c_code)
6064
cl.Alias.append(alias)
61-
# add the code list to the list of code list define_objects
62-
if cl:
63-
objects["CodeList"].append(cl)
65+
objects["CodeList"].append(cl)
6466

6567
def _create_codelist_object(self, obj):
6668
oid = self.require_key(obj, "OID", "CodeList")

src/generators/define/comments.py

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
import define_object
33

44

5-
""" Note: Comments have not yet been implemented in the template """
65
class Comments(define_object.DefineObject):
76
""" create a Define-XML v2.1 CommentDef element template """
87
def __init__(self):
@@ -17,13 +16,14 @@ def create_define_objects(self, template, define_objects, lang, acrf):
1716
:param define_objects: dictionary of odmlib define_objects updated by this method
1817
:param lang: xml:lang setting for TranslatedText
1918
"""
20-
self.logger.info("in Comments...")
2119
self.lang = lang
22-
define_objects["CommentDef"] = []
2320
for comment in template:
24-
com_oid = self.generate_oid(["COM", comment.Name])
25-
comment = self._create_commentdef_object(com_oid, comment)
26-
define_objects["CommentDef"].append(comment)
21+
name = self.require_key(comment, "name", "CommentDef")
22+
com_oid = comment.get("OID") or self.generate_oid(["COM", name])
23+
if self.find_object(define_objects["CommentDef"], com_oid) is not None:
24+
continue
25+
com_def = self._create_commentdef_object(com_oid, comment)
26+
define_objects["CommentDef"].append(com_def)
2727

2828
def _create_commentdef_object(self, com_oid, comment):
2929
"""
@@ -32,11 +32,12 @@ def _create_commentdef_object(self, com_oid, comment):
3232
:param comment: comment dictionary from the DDS JSON
3333
:return: a CommentDef odmlib template
3434
"""
35-
com = DEFINE.CommentDef(OID=com_oid, CommentType="FreeText")
36-
tt = DEFINE.TranslatedText(_content=comment["Description"], lang=self.lang)
35+
com = DEFINE.CommentDef(OID=com_oid)
36+
description = self.require_key(comment, "description", f"CommentDef {com_oid}")
37+
tt = DEFINE.TranslatedText(_content=description, lang=self.lang)
3738
com.Description = DEFINE.Description()
3839
com.Description.TranslatedText.append(tt)
39-
if comment.get("Document"):
40+
if comment.get("document"):
4041
self._add_document(comment, com)
4142
return com
4243

@@ -47,8 +48,8 @@ def _add_document(comment, com):
4748
:param comment: comment dictionary from the DDS JSON
4849
:param com: define comment template
4950
"""
50-
dr = DEFINE.DocumentRef(leafID=comment["Document"])
51-
if comment.get("Pages"):
52-
pdf = DEFINE.PDFPageRef(PageRefs=comment["Pages"], Type="NamedDestination")
51+
dr = DEFINE.DocumentRef(leafID=comment["document"])
52+
if comment.get("pages"):
53+
pdf = DEFINE.PDFPageRef(PageRefs=comment["pages"], Type="NamedDestination")
5354
dr.PDFPageRef.append(pdf)
5455
com.DocumentRef.append(dr)

src/generators/define/conceptProperties.py

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,26 +2,27 @@
22

33

44
class ConceptProperties(define_object.DefineObject):
5-
""" create a Define-XML v2.1 ExternalCodeList element template """
5+
"""Create Define-XML v2.1 CodeList elements for concept properties."""
66
def __init__(self):
77
super().__init__()
88

99
def create_define_objects(self, template, objects, lang, acrf):
1010
"""
1111
parse the define-template and create odmlib define_objects to return in the define_objects dictionary
12-
:param template: DDS template dictionary section
13-
:param objects: dictionary of odmlib define_objects updated by this method
14-
:param lang: xml:lang setting for TranslatedText
15-
:param acrf: part of the common interface but not used by this class
1612
"""
1713
self.lang = lang
1814
for concept in template:
19-
cl_oid = self.generate_oid(["CL", concept["Short Name"]])
15+
short_name = concept.get("shortName") or concept.get("Short Name")
16+
if not short_name:
17+
raise ValueError("Required field 'shortName' missing in ConceptProperties")
18+
cl_oid = self.generate_oid(["CL", short_name])
19+
if self.find_object(objects["CodeList"], cl_oid) is not None:
20+
continue
2021
cl = self.create_external_codelist(
2122
cl_oid=cl_oid,
22-
name=concept["Name"],
23-
data_type=concept["Data Type"],
24-
dictionary=concept["Dictionary"],
25-
version=concept.get("Version")
23+
name=concept.get("name") or concept.get("Name"),
24+
data_type=concept.get("dataType") or concept.get("Data Type"),
25+
dictionary=concept.get("dictionary") or concept.get("Dictionary"),
26+
version=concept.get("version") or concept.get("Version"),
2627
)
2728
objects["CodeList"].append(cl)

src/generators/define/concepts.py

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,26 +2,27 @@
22

33

44
class Concepts(define_object.DefineObject):
5-
""" create a Define-XML v2.1 ExternalCodeList element template """
5+
"""Create Define-XML v2.1 CodeList elements for biomedical concepts."""
66
def __init__(self):
77
super().__init__()
88

99
def create_define_objects(self, template, objects, lang, acrf):
1010
"""
1111
parse the define-template and create odmlib define_objects to return in the define_objects dictionary
12-
:param template: define-template dictionary section
13-
:param objects: dictionary of odmlib define_objects updated by this method
14-
:param lang: xml:lang setting for TranslatedText
15-
:param acrf: part of the common interface but not used by this class
1612
"""
1713
self.lang = lang
1814
for concept in template:
19-
cl_oid = self.generate_oid(["CL", concept["Short Name"]])
15+
short_name = concept.get("shortName") or concept.get("Short Name")
16+
if not short_name:
17+
raise ValueError("Required field 'shortName' missing in Concepts")
18+
cl_oid = self.generate_oid(["CL", short_name])
19+
if self.find_object(objects["CodeList"], cl_oid) is not None:
20+
continue
2021
cl = self.create_external_codelist(
2122
cl_oid=cl_oid,
22-
name=concept["Name"],
23-
data_type=concept["Data Type"],
24-
dictionary=concept["Dictionary"],
25-
version=concept.get("Version")
23+
name=concept.get("name") or concept.get("Name"),
24+
data_type=concept.get("dataType") or concept.get("Data Type"),
25+
dictionary=concept.get("dictionary") or concept.get("Dictionary"),
26+
version=concept.get("version") or concept.get("Version"),
2627
)
2728
objects["CodeList"].append(cl)
Lines changed: 8 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
from odmlib.define_2_1 import model as DEFINE
21
import define_object
32

3+
44
class Conditions(define_object.DefineObject):
5-
""" create a Define-XML v2.1 WhereClauseDef element objects """
5+
""" cache DDS conditions for use by WhereClauses to build RangeCheck elements """
66
def __init__(self):
77
super().__init__()
88

@@ -16,38 +16,22 @@ def create_define_objects(self, template, define_objects, lang, acrf):
1616
"""
1717
self.lang = lang
1818
conditions = []
19-
# store the conditions in define_objects for use when generating WhereClauseDef
2019
for condition in template:
2120
rc = self._create_condition(condition)
2221
conditions.append({rc["OID"]: rc})
23-
# store in define_objects with underscore prefix to indicate internal use
2422
define_objects["_conditions"] = conditions
2523

2624
@staticmethod
2725
def _create_condition(condition):
2826
condition_obj = {"OID": condition["OID"]}
2927
range_checks = []
3028
for rc in condition["rangeChecks"]:
31-
rc_attr = {"SoftHard": "Soft", "ItemOID": rc["item"], "Comparator": rc["comparator"]}
32-
check_values = []
33-
for value in rc["checkValues"]:
34-
check_values.append(value)
35-
rc_attr["CheckValue"] = check_values
29+
rc_attr = {
30+
"SoftHard": rc.get("softHard", "Soft"),
31+
"ItemOID": rc["item"],
32+
"Comparator": rc["comparator"],
33+
"CheckValue": list(rc.get("checkValues", [])),
34+
}
3635
range_checks.append(rc_attr)
3736
condition_obj["RangeCheck"] = range_checks
3837
return condition_obj
39-
40-
def _create_rangecheck(self, wc, dataset):
41-
"""
42-
use the values from the conditions section of the DDS JSON to create a RangeCheck odmlib template
43-
:param wc: WhereClause dictionary from the DDS JSON
44-
:param dataset: dataset name
45-
:return: a RangeCheck odmlib template
46-
"""
47-
item_oid = self.generate_oid(["IT", dataset, wc["Variable"]])
48-
rc_attr = {"SoftHard": "Soft", "ItemOID": item_oid, "Comparator": wc["Comparator"]}
49-
rc = DEFINE.RangeCheck(**rc_attr)
50-
for value in wc["Values"]:
51-
cv = DEFINE.CheckValue(_content=value)
52-
rc.CheckValue.append(cv)
53-
return rc

0 commit comments

Comments
 (0)