<?xml version="1.0" encoding="UTF-8"?>
<sdsrules last-update="2020-08-08T09:00:00" originator="FDA" standard="SDTM" version="2019">

<!-- 2020-07-28: adaption for support of define.xml 2.1 -->
<!-- TODO: extend for "HasNoData" datasets for case define.xml 2.1  -->

<title>FDA SDTM validation rules v.1.3 October 2018</title>

<!-- Comment: as FDA does not publish whether it is a warning or error or info, the severity is taken from the P21 implementation, 
or, when the P21 doesn't make sense, we change it using common sense -->

<sdsrule id="SD1097" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>Treatment Emergent info for Adverse Event must be present</ruledescription>
<ruledetaileddescription>A treatment-emergent flag should be included 
	in SUPPAE according to SDTM IG v3.1.2 #8.4.3</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>AE</domain>
<domain>SUPPAE</domain>
<synonym>SD1321</synonym>
<rulexquery><![CDATA[
(: Rule SD1097 - No Treatment Emergent info for Adverse Event: A treatment-emergent flag should be included in SUPPAE according to SDTM IG v3.1.2 #8.4.3 :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external;
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: look for a SUPPAE dataset and the AE dataset itself :)
let $suppaedataset := $definedoc//odm:ItemGroupDef[@Name='SUPPAE']
let $aedataset := $definedoc//odm:ItemGroupDef[@Name='AE']
(: get the OID of the QVAL SUPPAE variabke :)
let $suppaeqnamoid := (
    for $a in $definedoc//odm:ItemDef[@Name='QVAL']/@OID
        where $a = $definedoc//odm:ItemGroupDef[@Name='SUPPAE']/odm:ItemRef/@ItemOID
        return $a
)
(: now that we have the OID of the QVAL, check whether there is a ValueList attached :)
let $qvalvaluelistoid := (
	if ($defineversion = '2.1') then $definedoc//odm:ItemDef[@OID=$suppaeqnamoid]/def21:ValueListRef/@ValueListOID
	else $definedoc//odm:ItemDef[@OID=$suppaeqnamoid]/def:ValueListRef/@ValueListOID
)
(: and look for a "AETRTEM" in it :)
let $aetrtemoid := (
	if($defineversion = '2.1') then 
		 for $a in $definedoc//odm:ItemDef[@Name='AETRTEM']/@OID
			where $a = $definedoc//def21:ValueListDef[@OID=$qvalvaluelistoid]/odm:ItemRef/@ItemOID
			return $a
	else 
		for $a in $definedoc//odm:ItemDef[@Name='AETRTEM']/@OID
			where $a = $definedoc//def:ValueListDef[@OID=$qvalvaluelistoid]/odm:ItemRef/@ItemOID
			return $a
)
(: and check whether the corresponding ItemDef exists :)
let $aetrtemdef := $definedoc//odm:ItemDef[@OID=$aetrtemoid]
(: when absent, report an error :)
where not($aetrtemdef)
return <warning rule="SD1097" dataset="SUPPAE" rulelastupdate="2020-08-08">No Treatment Emergent info for Adverse Event - SUPPAE QVAL does not have an entry AETRTEM in ValueList</warning>
]]></rulexquery>
</sdsrule>


<sdsrule id="SD1037" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>Value for --TOX must be provided, when --TOXGR is populated</ruledescription>
<ruledetaileddescription>A value for a Toxicity (--TOX) variable should be provided, when a Toxicity Grade (--TOXGR) variable value is populated and greater than 0</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>AE</domain>
<domain>MH</domain>
<domain>CE</domain>
<domain>EG</domain>
<domain>LB</domain>
<domain>PC</domain>
<domain>PP</domain>
<rulexquery><![CDATA[	
(: Rule SD1037 - Missing value for --TOX, when --TOXGR is populated:
A value for a Toxicity (--TOX) variable should be provided, when a Toxicity Grade (--TOXGR) variable value is populated and greater than 0
:)
(: TODO: better testing, we did not have a good dataset for this :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(:  iterate over the AE, MH, CE, EG, LB, PC, PP datasets :)
for $dataset in $definedoc/odm:ItemGroupDef[starts-with(@Name,'AE') or @Domain='AE' or starts-with(@Name,'MH') or @Domain='MH' or starts-with(@Name,'CE') or @Domain='CE' or starts-with(@Name,'EG') or @Domain='EG' or starts-with(@Name,'LB') or @Domain='LB' or starts-with(@Name,'PC') or @Domain='PC' or starts-with(@Name,'PP') or @Domain='PP']
    let $name := $dataset/@Name
    let $datasetname := (
		if($defineversion = '2.1') then $dataset/def21:leaf/@xlink:href
		else $dataset/def:leaf/@xlink:href
	)
    let $datasetlocation := concat($base,$datasetname)
    (: Get the OIDs of the TOX and TOXGR variables :)
    let $toxoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'TOX')]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
    )
    let $toxname := $definedoc//odm:ItemDef[@OID=$toxoid]/@Name
    let $toxgroid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'TOXGR')]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
    )
    let $toxgrname := $definedoc//odm:ItemDef[@OID=$toxgroid]/@Name
    (: get all records for which TOXGR is populated :)
    for $record in doc($datasetlocation)//odm:ItemGroupData[odm:ItemData[@ItemOID=$toxgroid]]
        let $recnum := $record/@data:ItemGroupDataSeq
        (: and the value of TOX :)
        let $toxgrvalue := $record/odm:ItemData[@ItemOID=$toxgroid]/@Value
        (: and check whether TOX is populated, when TOXGR>0 :)
        let $toxvalue := $record/odm:ItemData[@ItemOID=$toxoid]/@Value
        where number($toxgrvalue) and not($toxvalue)
        return <warning rule="SD1037" dataset="{data($name)}" variable="{data($toxname)}" rulelastupdate="2020-08-08" recordnumber="{$recnum}">Missing value for {data($toxname)}, when {data($toxgrname)} is populated with a value >0, value={data($toxgrvalue)}</warning>	
]]></rulexquery>
</sdsrule>

<sdsrule id="SD0008" last-update="2020-08-08" originator="FDA" standard="SDTM" requiresMedDRA="Yes">
<ruledescription>Value for -DECOD must come from the MedDRA dictionary</ruledescription>
<ruledetaileddescription>Value for the Dictionary-Derived Term (-DECOD) variable must be populated using a Preferred Term of the MedDRA dictionary of a version specified in the define.xml (Case-insensitive)</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>AE</domain>
<domain>MH</domain>
<domain>CE</domain>
<synonym context="CDISC">CG0379</synonym>
<rulexquery><![CDATA[	
(: Rule SD0008: Value for -DECOD not found in MedDRA dictionary - 
Value for the Dictionary-Derived Term (-DECOD) variable must be populated using a Preferred Term of the MedDRA dictionary of a version specified in the define.xml (Case-insensitive) :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
declare namespace xs="http://www.w3.org/2001/XMLSchema";
declare namespace functx = "http://www.functx.com";
(: function to find out whether a value is in a sequence (array) :)
declare function functx:is-value-in-sequence
  ( $value as xs:anyAtomicType? ,
    $seq as xs:anyAtomicType* )  as xs:boolean {
   $value = $seq
} ;
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
declare variable $meddrabase external; 
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
(: let $meddrabase := 'file:///e:/Validation_Rules_XQuery/meddra_17_1_english/MedAscii/' :)
let $meddrafile := 'pt.asc' 
let $meddralocation := concat($meddrabase,$meddrafile)
(: read the meddra file line by line :)
let $lines := unparsed-text-lines($meddralocation) 
(: get the preferred term, which is the second fied - field separator is the $ character :)
let $ptterms := (
	for $line in $lines
	return upper-case(tokenize($line,'\$')[2])
)  
(: $ptterms now contains all the MedDRA PT terms in UPPERCASE :)
(: iterate over all AE, MH and CE dataset definitions :)
let $definedoc := doc(concat($base,$define))
for $itemgroup in $definedoc//odm:ItemGroupDef[@Name='AE' or @Name='MH' or @Name='CE' or @Domain='AE' or @Domain='MH' or @Domain='CE']
    let $name := $itemgroup/@Name
    (: get the OID of the --DECOD variable :)
    let $decodoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'DECOD')]/@OID 
        where $a = $itemgroup/odm:ItemRef/@ItemOID
        return $a
    )
    let $decodname := $definedoc//odm:ItemDef[@OID=$decodoid]/@Name
    (: get the dataset location :)
    let $datasetlocation := (
		if($defineversion = '2.1') then $itemgroup/def21:leaf/@xlink:href
		else $itemgroup/def:leaf/@xlink:href
	)
    let $datasetdoc := doc(concat($base,$datasetlocation))
    (: iterate over all the records in the dataset :)
    for $record in $datasetdoc//odm:ItemGroupData
        let $recnum := $record/@data:ItemGroupDataSeq
        (: get the value of --DECOD :)
        let $decodvalue := $record/odm:ItemData[@ItemOID=$decodoid]/@Value
        (: give an error when the DECOD value is not one of the PT terms of MedDRA - case-insensitive :)
        where $decodvalue and not(functx:is-value-in-sequence(upper-case($decodvalue),$ptterms)) 
        return <error rule="SD0008" dataset="{data($name)}" variable="{data($decodname)}" recordnumber="{data($recnum)}" rulelastupdate="2020-08-08">Value '{data($decodvalue)}' for {data($decodname)} was not found in the MedDRA dictionary</error>
]]></rulexquery>
</sdsrule>

<sdsrule id="SD1114" last-update="2020-08-08" originator="FDA" requiresMedDRA="Yes" standard="SDTM">
<ruledescription>Value for --BODSYS must come from the MedDRA dictionary</ruledescription>
<ruledetaileddescription>Value for Body System or Organ Class (--BODSYS) variable must be populated using a System Organ Class of the MedDRA dictionary of a version specified in the define.xml (case-insensitive)</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>AE</domain>
<domain>MH</domain>
<domain>CE</domain>
<synonym context="CDISC">CG0385</synonym>
<rulexquery><![CDATA[	
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
declare namespace xs="http://www.w3.org/2001/XMLSchema";
declare namespace functx = "http://www.functx.com";
(: function to find out whether a value is in a sequence (array) :)
declare function functx:is-value-in-sequence
  ( $value as xs:anyAtomicType? ,
    $seq as xs:anyAtomicType* )  as xs:boolean {
   $value = $seq
} ;
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
declare variable $meddrabase external; 
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
(: let $meddrabase := 'file:///e:/Validation_Rules_XQuery/meddra_17_1_english/MedAscii/' :)
let $meddrafile := 'soc.asc' (:   :)
let $meddralocation := concat($meddrabase,$meddrafile)
(: read the meddra file line by line :)
let $lines := unparsed-text-lines($meddralocation) 
(: get the SOC term, which is the second fied - field separator is the $ character :)
let $socterms := (
	for $line in $lines
	return upper-case(tokenize($line,'\$')[2])
)  
(: $socterms know contains all the MedDRA SOC terms in UPPERCASE :)
(: iterate over all AE, MH and CE dataset definitions :)
let $definedoc := doc(concat($base,$define))
for $itemgroup in $definedoc//odm:ItemGroupDef[@Name='AE' or @Name='MH' or @Name='CE' or @Domain='AE' or @Domain='MH' or @Domain='CE']
    let $name := $itemgroup/@Name
    (: get the OID of the --BODSYS variable :)
    let $bodsysoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'BODSYS')]/@OID 
        where $a = $itemgroup/odm:ItemRef/@ItemOID
        return $a
    )
    let $bodsysname := $definedoc//odm:ItemDef[@OID=$bodsysoid]/@Name
    (: get the dataset location :)
	let $datasetlocation := (
		if($defineversion = '2.1') then $itemgroup/def21:leaf/@xlink:href
		else $itemgroup/def:leaf/@xlink:href
	)
    let $datasetdoc := doc(concat($base,$datasetlocation))
    (: iterate over all the records in the dataset :)
    for $record in $datasetdoc//odm:ItemGroupData
        let $recnum := $record/@data:ItemGroupDataSeq
        (: get the value of --BODSYS :)
        let $bodsysvalue := $record/odm:ItemData[@ItemOID=$bodsysoid]/@Value
        (: give an error when the BODSYS value is not one of the SOC terms of MedDRA - case-insensitive :)
        where $bodsysvalue and not(functx:is-value-in-sequence(upper-case($bodsysvalue),$socterms)) 
        return <error rule="SD1114" dataset="{data($name)}" variable="{data($bodsysname)}" recordnumber="{data($recnum)}" rulelastupdate="2020-08-08">Value '{data($bodsysvalue)}' for {data($bodsysname)} was not found in the MedDRA dictionary</error>
]]></rulexquery>
</sdsrule>

<sdsrule id="SD2007" last-update="2020-08-08" originator="FDA" requiresMedDRA="Yes" standard="SDTM">
<ruledescription>Value for --PTCD must come from the MedDRA dictionary</ruledescription>
<ruledetaileddescription>Value for Preferred Term Code (--PTCD) variable must be populated using a Preferred Term Code of the MedDRA dictionary of a version specified in the define.xml</ruledetaileddescription>
<!-- Remark: not for SDTM-IG 3.1.2 -->
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>AE</domain>
<domain>MH</domain>
<domain>CE</domain>
<rulexquery><![CDATA[	
(: Attention: this is about the preferred term CODE, not the name - e.g. 10000002  :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
declare namespace xs="http://www.w3.org/2001/XMLSchema";
declare namespace functx = "http://www.functx.com";
(: function to find out whether a value is in a sequence (array) :)
declare function functx:is-value-in-sequence
  ( $value as xs:anyAtomicType? ,
    $seq as xs:anyAtomicType* )  as xs:boolean {
   $value = $seq
} ;
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
declare variable $meddrabase external; 
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
(: let $meddrabase := 'file:///e:/Validation_Rules_XQuery/meddra_17_1_english/MedAscii/' :)
let $meddrafile := 'pt.asc' 
let $meddralocation := concat($meddrabase,$meddrafile)
(: read the meddra file line by line :)
let $lines := unparsed-text-lines($meddralocation) 
(: get the preferred term codes, which is the first fied - field separator is the $ character :)
let $ptcodes := (
	for $line in $lines
	return tokenize($line,'\$')[1]
)  
(: $ptcodes know contains all the MedDRA PT codes :)
(: iterate over all AE, MH and CE dataset definitions :)
let $definedoc := doc(concat($base,$define))
for $itemgroup in $definedoc//odm:ItemGroupDef[@Name='AE' or @Name='MH' or @Name='CE' or @Domain='AE' or @Domain='MH' or @Domain='CE']
    let $name := $itemgroup/@Name
    (: get the OID of the --PTCD variable :)
    let $ptcdoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'PTCD')]/@OID 
        where $a = $itemgroup/odm:ItemRef/@ItemOID
        return $a
    )
    let $ptcdname := $definedoc//odm:ItemDef[@OID=$ptcdoid]/@Name
    (: get the dataset location :)
	let $datasetlocation := (
		if($defineversion = '2.1') then $itemgroup/def21:leaf/@xlink:href
		else $itemgroup/def:leaf/@xlink:href
	)
    let $datasetdoc := doc(concat($base,$datasetlocation))
    (: iterate over all the records in the dataset :)
    for $record in $datasetdoc//odm:ItemGroupData
        let $recnum := $record/@data:ItemGroupDataSeq
        (: get the value of --PTCD :)
        let $ptcdvalue := $record/odm:ItemData[@ItemOID=$ptcdoid]/@Value
        (: give an error when the PTCD value is not one of the PT codes of MedDRA :)
        where $ptcdvalue and not(functx:is-value-in-sequence($ptcdvalue,$ptcodes)) 
        return <error rule="SD2007" dataset="{data($name)}" variable="{data($ptcdname)}" recordnumber="{data($recnum)}" rulelastupdate="2020-08-08">Value '{data($ptcdvalue)}' for {data($ptcdname)} was not found in the MedDRA dictionary</error>
]]></rulexquery>
</sdsrule>


<sdsrule id="SD2008" last-update="2020-08-08" originator="FDA" requiresMedDRA="Yes" standard="SDTM">
<ruledescription>Value for --LLT must come from the MedDRA dictionary</ruledescription>
<ruledetaileddescription>Value for Lowest Level Term (--LLT) variable must be populated using a Lowest Level Term of the MedDRA dictionary of a version specified in the define.xml (case-insensitive)</ruledetaileddescription>
<!-- Remark: not for SDTM-IG 3.1.2 -->
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>AE</domain>
<domain>MH</domain>
<domain>CE</domain>
<synonym context="CDISC">CG0377</synonym>
<rulexquery><![CDATA[	
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
declare namespace xs="http://www.w3.org/2001/XMLSchema";
declare namespace functx = "http://www.functx.com";
(: function to find out whether a value is in a sequence (array) :)
declare function functx:is-value-in-sequence
  ( $value as xs:anyAtomicType? ,
    $seq as xs:anyAtomicType* )  as xs:boolean {
   $value = $seq
} ;
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
declare variable $meddrabase external; 
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
(: let $meddrabase := 'file:///e:/Validation_Rules_XQuery/meddra_17_1_english/MedAscii/' :)
let $meddrafile := 'llt.asc' (:   :)
let $meddralocation := concat($meddrabase,$meddrafile)
(: read the meddra file line by line :)
let $lines := unparsed-text-lines($meddralocation) 
(: get the LLT term, which is the second fied - field separator is the $ character :)
let $lltterms := (
	for $line in $lines
	return upper-case(tokenize($line,'\$')[2])
)  
(: $lltterms know contains all the MedDRA LLT terms in UPPERCASE :)
(: iterate over all AE, MH and CE dataset definitions :)
let $definedoc := doc(concat($base,$define))
for $itemgroup in $definedoc//odm:ItemGroupDef[@Name='AE' or @Name='MH' or @Name='CE' or @Domain='AE' or @Domain='MH' or @Domain='CE']
    let $name := $itemgroup/@Name
    (: get the OID of the --LLT variable :)
    let $lltoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'LLT') and string-length(@Name)=5]/@OID 
        where $a = $itemgroup/odm:ItemRef/@ItemOID
        return $a
    )
    let $lltname := $definedoc//odm:ItemDef[@OID=$lltoid]/@Name
    (: get the dataset location :)
	let $datasetlocation := (
		if($defineversion = '2.1') then $itemgroup/def21:leaf/@xlink:href
		else $itemgroup/def:leaf/@xlink:href
	)
    let $datasetdoc := doc(concat($base,$datasetlocation))
    (: iterate over all the records in the dataset :)
    for $record in $datasetdoc//odm:ItemGroupData
        let $recnum := $record/@data:ItemGroupDataSeq
        (: get the value of --LLC :)
        let $lltvalue := $record/odm:ItemData[@ItemOID=$lltoid]/@Value
        (: give an error when the LLT value is not one of the LLT terms of MedDRA - case-insensitive :)
        where $lltvalue and not(functx:is-value-in-sequence(upper-case($lltvalue),$lltterms)) 
        return <error rule="SD2008" dataset="{data($name)}" variable="{data($lltname)}" recordnumber="{data($recnum)}" rulelastupdate="2020-08-08">Value '{data($lltvalue)}' for {data($lltname)} was not found in the MedDRA dictionary</error>			
]]></rulexquery>
</sdsrule>

<sdsrule id="SD2010" last-update="2020-08-08" originator="FDA" requiresMedDRA="Yes" standard="SDTM">
<ruledescription>Value for --HLT must come from the MedDRA dictionary</ruledescription>
<ruledetaileddescription>Value for High Level Term (--HLT) variable must be populated using a High Level Term of the MedDRA dictionary of a version specified in the define.xml (case-insensitive)</ruledetaileddescription>
<!-- Remark: not for SDTM-IG 3.1.2 -->
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>AE</domain>
<domain>MH</domain>
<domain>CE</domain>
<synonym context="CDISC">CG0381</synonym>
<rulexquery><![CDATA[	
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
declare namespace xs="http://www.w3.org/2001/XMLSchema";
declare namespace functx = "http://www.functx.com";
(: function to find out whether a value is in a sequence (array) :)
declare function functx:is-value-in-sequence
  ( $value as xs:anyAtomicType? ,
    $seq as xs:anyAtomicType* )  as xs:boolean {
   $value = $seq
} ;
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
declare variable $meddrabase external; 
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
(: let $meddrabase := 'file:///e:/Validation_Rules_XQuery/meddra_17_1_english/MedAscii/' :)
let $meddrafile := 'hlt.asc' (:   :)
let $meddralocation := concat($meddrabase,$meddrafile)
(: read the meddra file line by line :)
let $lines := unparsed-text-lines($meddralocation) 
(: get the HLT term, which is the second fied - field separator is the $ character :)
let $hltterms := (
	for $line in $lines
	return upper-case(tokenize($line,'\$')[2])
)  
(: $hltterms know contains all the MedDRA HLT terms in UPPERCASE :)
(: iterate over all AE, MH and CE dataset definitions :)
let $definedoc := doc(concat($base,$define))
for $itemgroup in $definedoc//odm:ItemGroupDef[@Name='AE' or @Name='MH' or @Name='CE' or @Domain='AE' or @Domain='MH' or @Domain='CE']
    let $name := $itemgroup/@Name
    (: get the OID of the --HLT variable :)
    let $hltoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'HLT') and string-length(@Name)=5]/@OID 
        where $a = $itemgroup/odm:ItemRef/@ItemOID
        return $a
    )
    let $hltname := $definedoc//odm:ItemDef[@OID=$hltoid]/@Name
    (: get the dataset location :)
   	let $datasetlocation := (
		if($defineversion = '2.1') then $itemgroup/def21:leaf/@xlink:href
		else $itemgroup/def:leaf/@xlink:href
	)
    let $datasetdoc := doc(concat($base,$datasetlocation))
    (: iterate over all the records in the dataset :)
    for $record in $datasetdoc//odm:ItemGroupData
        let $recnum := $record/@data:ItemGroupDataSeq
        (: get the value of --HLT :)
        let $hltvalue := $record/odm:ItemData[@ItemOID=$hltoid]/@Value
        (: give an error when the HLT value is not one of the HLT terms of MedDRA - case-insensitive :)
        where $hltvalue and not(functx:is-value-in-sequence(upper-case($hltvalue),$hltterms)) 
        return <error rule="SD2010" dataset="{data($name)}" variable="{data($hltname)}" recordnumber="{data($recnum)}" rulelastupdate="2020-08-08">Value '{data($hltvalue)}' for {data($hltname)} was not found in the MedDRA dictionary</error>			
]]></rulexquery>
</sdsrule>

<sdsrule id="SD2011" last-update="2020-08-08" originator="FDA" requiresMedDRA="Yes" standard="SDTM">
<ruledescription>Value for --HLTCD must come from the MedDRA dictionary</ruledescription>
<ruledetaileddescription>Value for High Level Term Code (--HLTCD) variable must be populated using a High Level Term Code of the MedDRA dictionary of a version specified in the define.xml.</ruledetaileddescription>
<!-- Remark: not for SDTM-IG 3.1.2 -->
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>AE</domain>
<domain>MH</domain>
<domain>CE</domain>
<synonym context="CDISC">CG0382</synonym>
<rulexquery><![CDATA[	
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
declare namespace xs="http://www.w3.org/2001/XMLSchema";
declare namespace functx = "http://www.functx.com";
(: function to find out whether a value is in a sequence (array) :)
declare function functx:is-value-in-sequence
  ( $value as xs:anyAtomicType? ,
    $seq as xs:anyAtomicType* )  as xs:boolean {
   $value = $seq
} ;
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
declare variable $meddrabase external; 
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
(: let $meddrabase := 'file:///e:/Validation_Rules_XQuery/meddra_17_1_english/MedAscii/' :)
let $meddrafile := 'hlt.asc' (:   :)
let $meddralocation := concat($meddrabase,$meddrafile)
(: read the meddra file line by line :)
let $lines := unparsed-text-lines($meddralocation) 
(: get the HLT CODE, which is the first fied - field separator is the $ character :)
let $hltcodes := (
	for $line in $lines
	return upper-case(tokenize($line,'\$')[1])
)  
(: $hltcodes know contains all the MedDRA HLT codes :)
(: iterate over all AE, MH and CE dataset definitions :)
let $definedoc := doc(concat($base,$define))
for $itemgroup in $definedoc//odm:ItemGroupDef[@Name='AE' or @Name='MH' or @Name='CE' or @Domain='AE' or @Domain='MH' or @Domain='CE']
    let $name := $itemgroup/@Name
    (: get the OID of the --HLTCD variable :)
    let $hltcdoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'HLTCD')]/@OID 
        where $a = $itemgroup/odm:ItemRef/@ItemOID
        return $a
    )
    let $hltcdname := $definedoc//odm:ItemDef[@OID=$hltcdoid]/@Name
    (: get the dataset location :)
	let $datasetlocation := (
		if($defineversion = '2.1') then $itemgroup/def21:leaf/@xlink:href
		else $itemgroup/def:leaf/@xlink:href
	)
    let $datasetdoc := doc(concat($base,$datasetlocation))
    (: iterate over all the records in the dataset :)
    for $record in $datasetdoc//odm:ItemGroupData
        let $recnum := $record/@data:ItemGroupDataSeq
        (: get the value of --HLTCD :)
        let $hltcdvalue := $record/odm:ItemData[@ItemOID=$hltcdoid]/@Value
        (: give an error when the HLTCD value is not one of the HLTCD codes of MedDRA :)
        where $hltcdvalue and not(functx:is-value-in-sequence($hltcdvalue,$hltcodes)) 
        return <error rule="SD2011" dataset="{data($name)}" variable="{data($hltcdname)}" recordnumber="{data($recnum)}" rulelastupdate="2020-08-08">Value '{data($hltcdvalue)}' for {data($hltcdname)} was not found in the MedDRA dictionary</error>			
]]></rulexquery>
</sdsrule>

<!-- 2019-09-15 evening: SD2012 "Value for -HLGT not found in MedDRA dictionary" - also see SD2012C -->
<sdsrule id="SD2012" last-update="2020-08-08" originator="FDA" requiresMedDRA="Yes" standard="SDTM">
<ruledescription>Value for --HLGT must be in MedDRA dictionary</ruledescription>
<ruledetaileddescription>Value for High Level Group Term (--HLGT) variable must be populated using a High Level Group Term of the MedDRA dictionary of a version specified in the define.xml (Case-insensitive)</ruledetaileddescription>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>AE</domain>
<domain>MH</domain>
<domain>CE</domain>
<rulexquery><![CDATA[	
(: Rule SD2012: Value for --HLGT not found in MedDRA dictionary :) 
(: Value for High Level Group Term (--HLGT) variable must be populated using a High Level Group Term of the MedDRA dictionary of a version specified in the define.xml (Case-insensitive) :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
declare namespace xs="http://www.w3.org/2001/XMLSchema";
declare namespace functx = "http://www.functx.com";
(: function to find out whether a value is in a sequence (array) :)
declare function functx:is-value-in-sequence
  ( $value as xs:anyAtomicType? ,
    $seq as xs:anyAtomicType* )  as xs:boolean {
   $value = $seq
} ;
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
declare variable $meddrabase external; 
(:let $base := '/db/fda_submissions/cdisc01/'
let $define := 'define2-0-0-example-sdtm.xml' 
let $meddrabase := 'file:///e:/Validation_Rules_XQuery/meddra_17_1_english/MedAscii/' :)
let $meddrafile := 'hlgt.asc' 
let $meddralocation := concat($meddrabase,$meddrafile)
(: read the meddra file line by line :)
let $lines := unparsed-text-lines($meddralocation) 
(: get the HLGT terms, which is the second fied - field separator is the $ character :)
let $hlgtterms := (
	for $line in $lines
	return tokenize($line,'\$')[2]
)  
(: $hltgterms know contains all the MedDRA HLT terms in ORIGINAL CASE :)
(: we also keep a list of the uppercase ones :)
let $hlgttermsuppercase := (
	for $hlgtterm in $hlgtterms
	return upper-case($hlgtterm)
) 
(: iterate over all AE, MH and CE dataset definitions :)
let $definedoc := doc(concat($base,$define))
for $itemgroup in $definedoc//odm:ItemGroupDef[@Name='AE' or @Name='MH' or @Name='CE' or @Domain='AE' or @Domain='MH' or @Domain='CE']
    let $name := $itemgroup/@Name
    (: get the OID of the --HLGT variable :)
    let $hlgtoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'HLGT') and string-length(@Name)=6]/@OID 
        where $a = $itemgroup/odm:ItemRef/@ItemOID
        return $a
    )
    let $hlgtname := $definedoc//odm:ItemDef[@OID=$hlgtoid]/@Name
    (: get the dataset location :)
	let $datasetlocation := (
		if($defineversion = '2.1') then $itemgroup/def21:leaf/@xlink:href
		else $itemgroup/def:leaf/@xlink:href
	)
    let $datasetdoc := doc(concat($base,$datasetlocation))
    (: iterate over all the records in the dataset :)
    for $record in $datasetdoc//odm:ItemGroupData
        let $recnum := $record/@data:ItemGroupDataSeq
        (: get the value of --HLGT :)
        let $hlgtvalue := $record/odm:ItemData[@ItemOID=$hlgtoid]/@Value
        (: give an error when the HLGT value is not found (case-insenstive) :)
        where $hlgtvalue and not(functx:is-value-in-sequence(upper-case($hlgtvalue),$hlgttermsuppercase))
        return <error rule="SD2012" dataset="{data($name)}" variable="{data($hlgtname)}" recordnumber="{data($recnum)}" rulelastupdate="2020-08-08">Value '{data($hlgtvalue)}' for {data($hlgtname)} is not found in the MedDRA dictionary (case-insensitive)</error>		
]]></rulexquery>
</sdsrule>


<sdsrule id="SD2013" last-update="2020-08-08" originator="FDA" requiresMedDRA="Yes" standard="SDTM">
<ruledescription>Value for --HLGTCD must come from the MedDRA dictionary</ruledescription>
<ruledetaileddescription>Value for High Level Group Term Code (--HLGTCD) variable must be populated using a High Level Group Term Code of the MedDRA dictionary of a version specified in the define.xml</ruledetaileddescription>
<!-- Remark: not for SDTM-IG 3.1.2 -->
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>AE</domain>
<domain>MH</domain>
<domain>CE</domain>
<synonym context="CDISC">CG0384</synonym>
<rulexquery><![CDATA[	
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
declare namespace xs="http://www.w3.org/2001/XMLSchema";
declare namespace functx = "http://www.functx.com";
(: function to find out whether a value is in a sequence (array) :)
declare function functx:is-value-in-sequence
  ( $value as xs:anyAtomicType? ,
    $seq as xs:anyAtomicType* )  as xs:boolean {
   $value = $seq
} ;
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
declare variable $meddrabase external; 
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
(: let $meddrabase := 'file:///e:/Validation_Rules_XQuery/meddra_17_1_english/MedAscii/' :)
let $meddrafile := 'hlgt.asc' (:   :)
let $meddralocation := concat($meddrabase,$meddrafile)
(: read the meddra file line by line :)
let $lines := unparsed-text-lines($meddralocation) 
(: get the HLGT CODE, which is the first fied - field separator is the $ character :)
let $hlgtcodes := (
	for $line in $lines
	return upper-case(tokenize($line,'\$')[1])
)  
(: $hlgtcodes know contains all the MedDRA HLGT codes :)
(: iterate over all AE, MH and CE dataset definitions :)
let $definedoc := doc(concat($base,$define))
for $itemgroup in $definedoc//odm:ItemGroupDef[@Name='AE' or @Name='MH' or @Name='CE' or @Domain='AE' or @Domain='MH' or @Domain='CE']
    let $name := $itemgroup/@Name
    (: get the OID of the --HLGTCD variable :)
    let $hlgtcdoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'HLGTCD')]/@OID 
        where $a = $itemgroup/odm:ItemRef/@ItemOID
        return $a
    )
    let $hlgtcdname := $definedoc//odm:ItemDef[@OID=$hlgtcdoid]/@Name
    (: get the dataset location :)
	let $datasetlocation := (
		if($defineversion = '2.1') then $itemgroup/def21:leaf/@xlink:href
		else $itemgroup/def:leaf/@xlink:href
	)
    let $datasetdoc := doc(concat($base,$datasetlocation))
    (: iterate over all the records in the dataset :)
    for $record in $datasetdoc//odm:ItemGroupData
        let $recnum := $record/@data:ItemGroupDataSeq
        (: get the value of --HLGTCD :)
        let $hlgtcdvalue := $record/odm:ItemData[@ItemOID=$hlgtcdoid]/@Value
        (: give an error when the HLGTCD value is not one of the HLGTCD codes of MedDRA :)
        where $hlgtcdvalue and not(functx:is-value-in-sequence($hlgtcdvalue,$hlgtcodes)) 
        return <error rule="SD2013" dataset="{data($name)}" variable="{data($hlgtcdname)}" recordnumber="{data($recnum)}" rulelastupdate="2020-08-08">Value '{data($hlgtcdvalue)}' for {data($hlgtcdname)} was not found in the MedDRA dictionary</error>			
]]></rulexquery>
</sdsrule>

<sdsrule id="SD2014" last-update="2020-08-08" originator="FDA" requiresMedDRA="Yes" standard="SDTM">
<ruledescription>Value for --BDSYCD must be from the MedDRA dictionary</ruledescription>
<ruledetaileddescription>Value for Body System or Organ Class Code (--BDSYCD) variable should be populated using a System Organ Class Code of the MedDRA dictionary of a version specified in the define.xml</ruledetaileddescription>
<!-- Remark: not for SDTM-IG 3.1.2 -->
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>AE</domain>
<domain>MH</domain>
<domain>CE</domain>
<synonym context="CDISC">CG0386</synonym>
<rulexquery><![CDATA[	
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
declare namespace xs="http://www.w3.org/2001/XMLSchema";
declare namespace functx = "http://www.functx.com";
(: function to find out whether a value is in a sequence (array) :)
declare function functx:is-value-in-sequence
  ( $value as xs:anyAtomicType? ,
    $seq as xs:anyAtomicType* )  as xs:boolean {
   $value = $seq
} ;
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
declare variable $meddrabase external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
(: let $meddrabase := 'file:///e:/Validation_Rules_XQuery/meddra_17_1_english/MedAscii/' :)
let $meddrafile := 'soc.asc' (:   :)
let $meddralocation := concat($meddrabase,$meddrafile)
(: read the meddra file line by line :)
let $lines := unparsed-text-lines($meddralocation) 
(: get the SOC code, which is the first fied - field separator is the $ character :)
let $soccodes := (
	for $line in $lines
	return upper-case(tokenize($line,'\$')[1])
)  
(: $soccodes know contains all the MedDRA SOC codes :)
(: iterate over all AE, MH and CE dataset definitions :)
let $definedoc := doc(concat($base,$define))
for $itemgroup in $definedoc//odm:ItemGroupDef[@Name='AE' or @Name='MH' or @Name='CE' or @Domain='AE' or @Domain='MH' or @Domain='CE']
    let $name := $itemgroup/@Name
    (: get the OID of the --BDSYCD variable :)
    let $bdsycdoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'BDSYCD')]/@OID 
        where $a = $itemgroup/odm:ItemRef/@ItemOID
        return $a
    )
    let $bdsycdname := $definedoc//odm:ItemDef[@OID=$bdsycdoid]/@Name
    (: get the dataset location :)
    let $datasetlocation := (
		if($defineversion = '2.1') then $itemgroup/def21:leaf/@xlink:href
		else $itemgroup/def:leaf/@xlink:href
	)
    let $datasetdoc := doc(concat($base,$datasetlocation))
    (: iterate over all the records in the dataset :)
    for $record in $datasetdoc//odm:ItemGroupData
        let $recnum := $record/@data:ItemGroupDataSeq
        (: get the value of --BDSYCD :)
        let $bdsycdvalue := $record/odm:ItemData[@ItemOID=$bdsycdoid]/@Value
        (: give an error when the BDSYCD value is not one of the SOC codes of MedDRA :)
        where $bdsycdvalue and not(functx:is-value-in-sequence($bdsycdvalue,$soccodes)) 
        return <warning rule="SD2014" dataset="{data($name)}" variable="{data($bdsycdvalue)}" recordnumber="{data($recnum)}" rulelastupdate="2020-08-08">Value '{data($bdsycdvalue)}' for {data($bdsycdname)} was not found in the MedDRA dictionary</warning>
]]></rulexquery>
</sdsrule>

<sdsrule id="SD2015" last-update="2020-08-08" originator="FDA" requiresMedDRA="Yes" standard="SDTM">
<ruledescription>Value for --SOC must be from the MedDRA dictionary - </ruledescription>
<ruledetaileddescription>Value for System Organ Class (--SOC) variable must be populated using a System Organ Class of the MedDRA dictionary of a version specified in the define.xml (case-insensitive)</ruledetaileddescription>
<!-- Remark: not for SDTM-IG 3.1.2 -->
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>AE</domain>
<domain>MH</domain>
<domain>CE</domain>
<synonym context="CDISC">CG0436</synonym>
<rulexquery><![CDATA[	
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
declare namespace xs="http://www.w3.org/2001/XMLSchema";
declare namespace functx = "http://www.functx.com";
(: function to find out whether a value is in a sequence (array) :)
declare function functx:is-value-in-sequence
  ( $value as xs:anyAtomicType? ,
    $seq as xs:anyAtomicType* )  as xs:boolean {
   $value = $seq
} ;
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
declare variable $meddrabase external; 
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
(: let $meddrabase := 'file:///e:/Validation_Rules_XQuery/meddra_17_1_english/MedAscii/' :)
let $meddrafile := 'soc.asc' (: SOC file  :)
let $meddralocation := concat($meddrabase,$meddrafile)
(: read the meddra file line by line :)
let $lines := unparsed-text-lines($meddralocation) 
(: get the SOC term, which is the second fied - field separator is the $ character :)
let $socterms := (
	for $line in $lines
	return upper-case(tokenize($line,'\$')[2])
)  
(: $socterms know contains all the MedDRA SOC terms - UPPERCASE :)
(: iterate over all AE, MH and CE dataset definitions :)
let $definedoc := doc(concat($base,$define))
for $itemgroup in $definedoc//odm:ItemGroupDef[@Name='AE' or @Name='MH' or @Name='CE' or @Domain='AE' or @Domain='MH' or @Domain='CE']
    let $name := $itemgroup/@Name
    (: get the OID of the --SOC variable :)
    let $socoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'SOC') and string-length(@Name)=5]/@OID 
        where $a = $itemgroup/odm:ItemRef/@ItemOID
        return $a
    )
    let $socname := $definedoc//odm:ItemDef[@OID=$socoid]/@Name
    (: get the dataset location :)
    let $datasetlocation := (
		if($defineversion = '2.1') then $itemgroup/def21:leaf/@xlink:href
		else $itemgroup/def:leaf/@xlink:href
	)
    let $datasetdoc := doc(concat($base,$datasetlocation))
    (: iterate over all the records in the dataset :)
    for $record in $datasetdoc//odm:ItemGroupData
        let $recnum := $record/@data:ItemGroupDataSeq
        (: get the value of --SOC :)
        let $socvalue := $record/odm:ItemData[@ItemOID=$socoid]/@Value
        (: give an error when the SOC value is not one of the SOC terms of MedDRA :)
        where $socvalue and not(functx:is-value-in-sequence(upper-case($socvalue),$socterms)) 
        return <error rule="SD2015" dataset="{data($name)}" variable="{data($socname)}" recordnumber="{data($recnum)}" rulelastupdate="2020-08-08">Value '{data($socvalue)}' for {data($socname)} was not found in the MedDRA dictionary</error>
]]></rulexquery>
</sdsrule>

<!-- Rule SD2006: "MedDRA coding info should be populated using variables in the Events General Observation Class, but not in SUPPQUAL domains"
will not be implemented as it is nonsense. How can we know that a code is intended to be a MedDRA code? Maybe over the ValueList?
-->

<sdsrule id="SD1110" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>DS dataset must be included</ruledescription>
<ruledetaileddescription>Disposition (DS) dataset should be included in every submission</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>DS</domain>
<rulexquery><![CDATA[	
(: Rule SD1110 - Missing DS dataset :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
declare namespace request="http://exist-db.org/xquery/request";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external;
declare variable $defineversion external;
(: let $base := 'MSG_draft_define_2-1/' :)
(: let $define := 'define_2_1.xml' :)
(: let $defineversion := '2.1' :)
let $definedoc := doc(concat($base,$define))
(: get the location of the DS dataset :)
let $dsdatasetname := (
	if($defineversion and $defineversion='2.1') then
		$definedoc//odm:ItemGroupDef[@Name='DS']/def21:leaf/@xlink:href
    else $definedoc//odm:ItemGroupDef[@Name='DS']/def:leaf/@xlink:href
)
let $dsdatasetlocation := concat($base,$dsdatasetname)
(: we now have the location of the dataset, check whether it is available there :)
where (not(doc-available($dsdatasetlocation)))
return <warning rule="SD1110" dataset="DS" rulelastupdate="2020-08-08">Document {data($dsdatasetname)} could not be found in collection/folder {data($base)}</warning>		

]]></rulexquery>
</sdsrule>

<sdsrule id="SD1106" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>AE dataset must be included</ruledescription>
<ruledetaileddescription>Adverse Events (AE) dataset should be included in every submission</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>AE</domain>
<rulexquery><![CDATA[	
(: Rule SD1106 - Missing AE dataset :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
declare namespace request="http://exist-db.org/xquery/request";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: get the location of the AE dataset :)
let $aedatasetname := ( 
	if($defineversion and $defineversion='2.1') then
		$definedoc//odm:ItemGroupDef[@Name='AE']/def21:leaf/@xlink:href
	else $definedoc//odm:ItemGroupDef[@Name='AE']/def:leaf/@xlink:href
)
let $aedatasetlocation := concat($base,$aedatasetname)
(: we now have the location of the dataset, check whether it is available there :)
where (not(doc-available($aedatasetlocation)))
return <warning rule="SD1106" dataset="AE" rulelastupdate="2020-08-08">Document {data($aedatasetname)} could not be found in collection {data($base)}</warning>		
]]></rulexquery>
</sdsrule>


<sdsrule id="SD1107" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>LB dataset must be included</ruledescription>
<ruledetaileddescription>Lab Test Results (LB) dataset should be included in every submission</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>LB</domain>
<rulexquery><![CDATA[	
(: Rule SD1107 - Missing LB dataset :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
declare namespace request="http://exist-db.org/xquery/request";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external;
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: get the location of the LB dataset :)
let $lbdatasetname := (
	if($defineversion = '2.1') then $definedoc//odm:ItemGroupDef[@Name='LB']/def21:leaf/@xlink:href
	else $definedoc//odm:ItemGroupDef[@Name='LB']/def:leaf/@xlink:href
)
let $lbdatasetlocation := concat($base,$lbdatasetname)
(: we now have the location of the dataset, check whether it is available there :)
where (not(doc-available($lbdatasetlocation)))
return <error rule="SD1107" dataset="LB" rulelastupdate="2020-08-08">Document {data($lbdatasetname)} could not be found in collection {data($base)}</error>		
]]></rulexquery>
</sdsrule>

<sdsrule id="SD1108" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>VS dataset must be included</ruledescription>
<ruledetaileddescription>Vital Signs (VS) dataset should be included in every submission</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>VS</domain>
<rulexquery><![CDATA[	
(: Rule SD1108 - Missing VS dataset :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
declare namespace request="http://exist-db.org/xquery/request";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external;
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: get the location of the VS dataset :)
let $vsdatasetname := (
	if($defineversion = '2.1') then $definedoc//odm:ItemGroupDef[@Name='VS']/def21:leaf/@xlink:href
	else $definedoc//odm:ItemGroupDef[@Name='VS']/def:leaf/@xlink:href
)
let $vsdatasetlocation := concat($base,$vsdatasetname)
(: we now have the location of the dataset, check whether it is available there :)
where (not(doc-available($vsdatasetlocation)))
return <error rule="SD1108" dataset="VS" rulelastupdate="2020-08-08">Document {data($vsdatasetname)} could not be found in collection {data($base)}</error>		
]]></rulexquery>
</sdsrule>

<sdsrule id="SD1111" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>SE dataset must be included</ruledescription>
<ruledetaileddescription>Subject Elements (SE) dataset should be included in every submission</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>SE</domain>
<rulexquery><![CDATA[	
(: Rule SD1111 - Missing SE dataset :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
declare namespace request="http://exist-db.org/xquery/request";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external;
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: get the location of the SE dataset :)
let $sedatasetname := (
	if($defineversion = '2.1') then $definedoc//odm:ItemGroupDef[@Name='SE']/def21:leaf/@xlink:href
	else $definedoc//odm:ItemGroupDef[@Name='SE']/def:leaf/@xlink:href
)
let $sedatasetlocation := concat($base,$sedatasetname)
(: we now have the location of the dataset, check whether it is available there :)
where (not(doc-available($sedatasetlocation)))
return <warning rule="SD1111" dataset="SE" rulelastupdate="2020-08-08">Document {data($sedatasetname)} could not be found in collection {data($base)}</warning>	
]]></rulexquery>
</sdsrule>

<sdsrule id="SD1129" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>AGE or AGETXT variable must be present</ruledescription>
<ruledetaileddescription>At least one of Age (AGE) or Age Text (AGETXT) variables should be inculded into Demographics (DM) domain</ruledetaileddescription>
<!-- REMARK: not for SDTM-IG 3.1.2 -->
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>DM</domain>
<rulexquery><![CDATA[		
(: Rule SD1129 - Neither AGE nor AGETXT variables are present
At least one of Age (AGE) or Age Text (AGETXT) variables should be included into Demographics (DM) domain
This is checked on the level of define.xml, as it is NOT about the population (see rule FDAC191)
:)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/'  :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define)) 
(: Get the DM dataset :)
for $dataset in $definedoc//odm:ItemGroupDef[@Name='DM']
    let $datasetname := ( 
		if($defineversion = '2.1') then $dataset/def21:leaf/@xlink:href
		else $dataset/def:leaf/@xlink:href
	)
    let $datasetlocation := concat($base,$datasetname)
    (: get the OID of the AGE and AGETXT variables (when present) :)
    let $ageoid := (
        for $a in $definedoc//odm:ItemDef[@Name='AGE']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name='DM']/odm:ItemRef/@ItemOID
        return $a
    )
    let $agetxtoid := (
        for $a in $definedoc//odm:ItemDef[@Name='AGETXT']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name='DM']/odm:ItemRef/@ItemOID
        return $a
    )
    where not($ageoid) and not($agetxtoid)
    return <error rule="SD1129" dataset="DM" rulelastupdate="2020-08-08">Neither AGE nor AGETXT variables are present</error>	
]]></rulexquery>
</sdsrule>

<sdsrule id="SD1121" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>AGE or AGETXT variables values must be populated</ruledescription>
<ruledetaileddescription>Value for Age (AGE) of Age Range (AGETXT) variables should be populated for all subjects with only exception for Screen Failures (ARMCD=SCRNFAIL) and Not Assigned (ARMCD=NOTASSGN) subjects</ruledetaileddescription>
<!-- REMARK: not for SDTM-IG 3.1.2 -->
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>DM</domain>
<rulexquery><![CDATA[		
(: Rule SD1121 - Neither of AGE or AGETXT variables values are populated:
Value for Age (AGE) of Age Range (AGETXT) variables should be populated for all subjects with only exception for Screen Failures (ARMCD=SCRNFAIL) and Not Assigned (ARMCD=NOTASSGN) subejcts
:)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/'  :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: Get the DM dataset :)
for $dataset in $definedoc//odm:ItemGroupDef[@Name='DM']
    let $datasetname := (
		if($defineversion = '2.1') then $dataset/def21:leaf/@xlink:href
		else $dataset/def:leaf/@xlink:href
	)
    let $datasetlocation := concat($base,$datasetname)
    (: get the OID of the AGE and AGETXT variables (when present) :)
    let $ageoid := (
        for $a in $definedoc//odm:ItemDef[@Name='AGE']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name='DM']/odm:ItemRef/@ItemOID
        return $a
    )
    let $agetxtoid := (
        for $a in $definedoc//odm:ItemDef[@Name='AGETXT']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name='DM']/odm:ItemRef/@ItemOID
        return $a
    )
    (: we also need the OID of the ARMCD variable :)
    let $armcdoid := (
        for $a in $definedoc//odm:ItemDef[@Name='ARMCD']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name='DM']/odm:ItemRef/@ItemOID
        return $a
    )
    (: iterate over all the records in the DM dataset :)
    for $record in doc($datasetlocation)//odm:ItemGroupData
        let $recnum := $record/@data:ItemGroupDataSeq
        (: and get the values of AGE, AGETXT and ARMCD (when populated) :)
        let $agevalue := $record/odm:ItemData[@ItemOID=$ageoid]/@Value
        let $agetxtvalue := $record/odm:ItemData[@ItemOID=$agetxtoid]/@Value
        let $armcdvalue := $record/odm:ItemData[@ItemOID=$armcdoid]/@Value
        (: AGE or AGETXT must be populated except for ARMCD=SCRNFAIL and ARMCD=NOTASSGN :)
        where not($agevalue) and not($agetxtvalue) and not($armcdvalue='SCRNFAIL') and not($armcdvalue='NOTASSGN')
        return <warning rule="SD1121" dataset="DM" variable="AGE" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">Neither of AGE or AGETXT variables values are populated</warning>
]]></rulexquery>
</sdsrule>

<sdsrule id="SD2023" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>AGE must be provided</ruledescription>
<ruledetaileddescription>Age (AGE) variable values should be provided, when Date/Time of Birth (BRTHDTC) variable values are populated</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>DM</domain>
<rulexquery><![CDATA[		
(: Rule SD2023 - AGE is not provided:
Age (AGE) variable values should be provided, when Date/Time of Birth (BRTHDTC) variable values are populated
:)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: Get the DM dataset :)
for $dataset in $definedoc//odm:ItemGroupDef[@Name='DM']
    let $datasetname := (
		if($defineversion = '2.1') then $dataset/def21:leaf/@xlink:href
		else $dataset/def:leaf/@xlink:href
	)
    let $datasetlocation := concat($base,$datasetname)
    (: get the OID of the AGE, and BRTHDTC (when present) :)
    let $ageoid := (
        for $a in $definedoc//odm:ItemDef[@Name='AGE']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name='DM']/odm:ItemRef/@ItemOID
        return $a
    )
    let $brthdtcoid := (
        for $a in $definedoc//odm:ItemDef[@Name='BRTHDTC']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name='DM']/odm:ItemRef/@ItemOID
        return $a
    )
    (: iterate over all the records in the DM dataset that have BRTHDTC populated :)
    for $record in doc($datasetlocation)//odm:ItemGroupData[odm:ItemData[@ItemOID=$brthdtcoid]]
        let $recnum := $record/@data:ItemGroupDataSeq
        (: and get the values of AGE (when populated) :)
        let $agevalue := $record/odm:ItemData[@ItemOID=$ageoid]/@Value
        (: AGE must be populated :)
        where not($agevalue)
        return <error rule="SD2023" dataset="DM" variable="AGE" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">AGE is not provided</error>
]]></rulexquery>
</sdsrule>

<sdsrule id="SD2020" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>AGE and AGETXT variables values may not be both populated</ruledescription>
<ruledetaileddescription>Age (AGE) and Age Range (AGETXT) variables should never both be populated for the same subject</ruledetaileddescription>
<!-- REMARK: not for 3.1.2 -->
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>DM</domain>
<rulexquery><![CDATA[	
(: Rule SD2020 - Both AGE and AGETXT variables values are populated:
Age (AGE) and Age Range (AGETXT) variables should never both be populated for the same subject :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/'  :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: Get the DM dataset :)
for $dataset in $definedoc//odm:ItemGroupDef[@Name='DM']
    let $datasetname := ( 
		if($defineversion = '2.1') then $dataset/def21:leaf/@xlink:href
		else $dataset/def:leaf/@xlink:href
	)
    let $datasetlocation := concat($base,$datasetname)
    (: get the OID of the AGE and AGETXT variables (when present) :)
    let $ageoid := (
        for $a in $definedoc//odm:ItemDef[@Name='AGE']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name='DM']/odm:ItemRef/@ItemOID
        return $a
    )
    let $agetxtoid := (
        for $a in $definedoc//odm:ItemDef[@Name='AGETXT']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name='DM']/odm:ItemRef/@ItemOID
        return $a
    )
    (: iterate over all the records in the DM dataset :)
    for $record in doc($datasetlocation)//odm:ItemGroupData
        let $recnum := $record/@data:ItemGroupDataSeq
        (: and get the values of AGE, AGETXT (when populated) :)
        let $agevalue := $record/odm:ItemData[@ItemOID=$ageoid]/@Value
        let $agetxtvalue := $record/odm:ItemData[@ItemOID=$agetxtoid]/@Value
        (: AGE or AGETXT may not both be populated for the same subject :)
        where $agevalue and $agetxtvalue
        return <warning rule="SD2020" dataset="DM" variable="AGETXT" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">Both AGE and AGETXT variables values are populated</warning>	
]]></rulexquery>
</sdsrule>

<sdsrule id="SD2005" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>DTHDTC must be populated, when DTHFL is populated</ruledescription>
<ruledetaileddescription>Date/Time of Death (DTHDTC) should be provided, when subject died (DTHFL=Y)</ruledetaileddescription>
<!-- REMARK: not for 3.1.2 -->
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>DM</domain>
<rulexquery><![CDATA[		
(: Rule SD2005 - Missing value for DTHDTC, when DTHFL is populated:
Date/Time of Death (DTHDTC) should be provided, when subject died (DTHFL=Y)
:)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define)) 
(: Get the DM dataset :)
for $dataset in $definedoc//odm:ItemGroupDef[@Name='DM']
    let $datasetname := (
		if($defineversion = '2.1') then $dataset/def21:leaf/@xlink:href
		else $dataset/def:leaf/@xlink:href
	)
    let $datasetlocation := concat($base,$datasetname)
    (: get the OID of the DTHFL and of DTHDTC :)
    let $dthfloid := (
        for $a in $definedoc//odm:ItemDef[@Name='DTHFL']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name='DM']/odm:ItemRef/@ItemOID
        return $a
    )
    let $dhdtcoid := (
        for $a in $definedoc//odm:ItemDef[@Name='DTHDTC']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name='DM']/odm:ItemRef/@ItemOID
        return $a
    )
    (: iterate over all the records in the DM dataset that have DTHFL populated :)
    for $record in doc($datasetlocation)//odm:ItemGroupData[odm:ItemData[@ItemOID=$dthfloid]]
        let $recnum := $record/@data:ItemGroupDataSeq
        (: get the value of DTHDTC :)
        let $dtcdtcvalue := $record/odm:ItemData[@ItemOID=$dhdtcoid]/@Value
        (: and check whether DTHDTC is populated :)
        where not($dtcdtcvalue)
        return <warning rule="SD2005" dataset="DM" variable="DTHDTC" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">Missing value for DTHDTC, when DTHFL is populated</warning>	
]]></rulexquery>
</sdsrule>

<sdsrule id="SD1205-SD" last-update="2020-08-14" originator="FDA" standard="SDTM" requiresDomainOrDataset="Yes">
<ruledescription>ECSTDTC/EXSTDTC date may not be before RFXSTDTC</ruledescription>
<ruledetaileddescription>Start Date/Time of Treatment (EXSTDTC) variable value must be greater than or equal to Date/Time of First Study Treatment (RFXSTDTC)</ruledetaileddescription>
<!-- REMARK: not for 3.1.2 -->
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>EX</domain>
<domain>EC</domain>
<rulexquery><![CDATA[	
(: Rule SD1205 ECSTDTC/EXSTDTC date is before RFXSTDTC :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
declare namespace request="http://exist-db.org/xquery/request";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
declare variable $datasetname external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
(: let $datasetname := 'EX' :)
let $definedoc := doc(concat($base,$define))
(: get the DM dataset :)
let $dmdatasetname := ( 
	if($defineversion = '2.1') then $definedoc//odm:ItemGroupDef[@Name='DM']/def21:leaf/@xlink:href
	else $definedoc//odm:ItemGroupDef[@Name='DM']/def:leaf/@xlink:href
)
let $dmdataset := concat($base,$dmdatasetname)
(: get the OIDs of USUBJID and RFXSTDTC :)
let $usubjoiddm := (
        for $a in $definedoc//odm:ItemDef[@Name='USUBJID']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name='DM']/odm:ItemRef/@ItemOID
        return $a
    )
let $rfxstdtcoid := (
    for $a in $definedoc//odm:ItemDef[@Name='RFXSTDTC']/@OID 
    where $a = $definedoc//odm:ItemGroupDef[@Name='DM']/odm:ItemRef/@ItemOID
    return $a
)
(: return <test>{data($usubjoid)} - {data($rfxstdtcoid)}</test>):)
(: create a temporary structure containing USUBJID and RFXSTDTC :)
let $usubjidrfxstdtcpairs := (
    for $record in doc($dmdataset)//odm:ItemGroupData
        let $usubjid := $record/odm:ItemData[@ItemOID=$usubjoiddm]/@Value
        let $rfxstdtc := $record/odm:ItemData[@ItemOID=$rfxstdtcoid]/@Value
    return <record usubjid="{$usubjid}" rfxstdtc="{$rfxstdtc}" />
)
(: get the OID of EXSTDTC or EGSTDTC :)
let $stdtcname := concat($datasetname,'STDTC')
let $stdtcoid := (
    for $a in $definedoc//odm:ItemDef[@Name=$stdtcname]/@OID 
    where $a = $definedoc//odm:ItemGroupDef[@Name=$datasetname and (@Name='EC' or @Name='EX')]/odm:ItemRef/@ItemOID
    return $a
)
(: get the OID of USUBJID in EX or EC :)
let $usubjidoid := (
    for $a in $definedoc//odm:ItemDef[@Name='USUBJID']/@OID 
    where $a = $definedoc//odm:ItemGroupDef[@Name=$datasetname]/odm:ItemRef/@ItemOID
    return $a
)
(: get the EX and/or EC dataset :)
(: 2020-08-14 :)
let $ecexdataset := $definedoc//odm:ItemDef[@Name=$datasetname][@Name='EX' or @Name='EC']
let $datasetloc := (
	if($defineversion='2.1') then $ecexdataset/def21:leaf/@xlink:href
	else $ecexdataset/def:leaf/@xlink:href
)
let $ecexdoc := (
	if($datasetloc) then doc(concat($base,$datasetloc))
	else ()
)
(: now iterate over all records in EX or EC that have a --STDTC :)
for $record in $ecexdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$stdtcoid]]
	(: get the value of --STDTC :)
	let $stdtcvalue := $record/odm:ItemData[@ItemOID=$stdtcoid]/@Value
	(: get the value of USUBJID :)
	let $usubjidvalue := $record/odm:ItemData[@ItemOID=$usubjidoid]/@Value
	let $rfxstdtcvalue := $usubjidrfxstdtcpairs[@usubjid=$usubjidvalue]/@rfxstdtc
	let $recnum := $record/@data:ItemGroupDataSeq
	(: and compare with the value of RFXSTDTC in DM for the same USUBJID :)
	where $rfxstdtcvalue and xs:date($rfxstdtcvalue) > xs:date($stdtcvalue) 
	(: and when the STDTC is before RFXSTDTC give an error :)
	return <error rule="SD1205" dataset="{$datasetname}" variable="{$stdtcname}" recordnumber="{$recnum}" rulelastupdate="2020-08-08">{data($stdtcname)} date '{data($stdtcvalue)}' is before RFXSTDTC '{data($rfxstdtcvalue)}'</error>
]]>
</rulexquery>
</sdsrule>

<!-- 2019-10-15 evening: SD0040: Inconsistent value for -TEST within -TESTCD -->
<sdsrule id="SD0040" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>--TEST value must be consistent within --TESTCD</ruledescription>
<ruledetaileddescription>All values of Name of Measurement, Test or Examination (--TEST) should be the same for a given value of Short Name of Measurement, Test or Examination (--TESTCD)</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>FINDINGS</domain>
<rulexquery><![CDATA[	
(: Rule SD0040 - 'Inconsistent value for --TEST within --TESTCD
All values of Name of Measurement, Test or Examination (--TEST) should be the same for a given value of Short Name of Measurement, Test or Examination (--TESTCD)
:)
(: Using a "GROUP BY", the speed is enormously improved  :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external; 
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' 
let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: iterate over all FINDINGS datasets that have --TESTCD and --TEST :)
for $dataset in $definedoc//odm:ItemGroupDef[@def:Class='FINDINGS' or ./def21:Class/@Name='FINDINGS']
    let $name := $dataset/@Name
    let $datasetname := (
		if($defineversion = '2.1') then $dataset/def21:leaf/@xlink:href
		else $dataset/def:leaf/@xlink:href
	)
	(: check for "HasNoData" case, i.e. no location is provided :)
	let $datasetdoc := (
		if($datasetname) then doc(concat($base,$datasetname))
		else ()
	)
    (: and get the OID of --TESTCD and --TEST :)
    let $testcdoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'TESTCD')]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a 
    )
    let $testcdname := $definedoc//odm:ItemDef[@OID=$testcdoid]/@Name
    let $testoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'TEST')]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a 
    )
    let $testname := $definedoc//odm:ItemDef[@OID=$testoid]/@Name
    (: Group the records by the value of --TESTCD :)
    let $orderedrecords := (
        for $record in $datasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$testcdoid]]
            group by 
            $b:=$record/odm:ItemData[@ItemOID=$testcdoid]/@Value
            return element group {  
                $record
            }
        )
    (: all the records in the same group have the same value for --TESTCD. 
    They must also all have the same value for --TEST :)
    for $group in $orderedrecords
        let $testcdvalue := $group/odm:ItemGroupData[1]/odm:ItemData[@ItemOID=$testcdoid]/@Value
        for $record in $group/odm:ItemGroupData[position()>1]  (: start from the second one  :)
            let $recnum := $record/@data:ItemGroupDataSeq
            (: get the value of --TEST :)
            let $testvalue := $record/odm:ItemData[@ItemOID=$testoid]/@Value
            (: take the previous record in the same group, and take its --TEST value :)
            let $precrecord := $record/preceding-sibling::odm:ItemGroupData[1]  (: as they come in descending order :)
            let $recnum2 := $precrecord/@data:ItemGroupDataSeq
            let $testvalue2 := $precrecord/odm:ItemData[@ItemOID=$testoid]/@Value 
            (: when the value of--TEST in both records is different, report an error :)
            where not($testvalue2 = $testvalue) 
                return <error rule="SD0040" dataset="{data($name)}" variable="{data($testcdname)}" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">Inconsistent value for {data($testname)} within {data($testcdname)}='{data($testcdvalue)}' in dataset {data($datasetname)}. Following values were found: record number {data($recnum2)}: {data($testname)}={data($testvalue2)} - record number {data($recnum)}: {data($testname)}={data($testvalue)}</error>	
]]></rulexquery>
</sdsrule>

<sdsrule id="SD0040-SD" last-update="2020-08-08" originator="FDA" standard="SDTM" requiresDomainOrDataset="Yes">
<ruledescription>--TEST value must be consistent within --TESTCD</ruledescription>
<ruledetaileddescription>All values of Name of Measurement, Test or Examination (--TEST) should be the same for a given value of Short Name of Measurement, Test or Examination (--TESTCD)</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>FINDINGS</domain>
<rulexquery><![CDATA[	
(: Rule SD0040 - 'Inconsistent value for --TEST within --TESTCD
All values of Name of Measurement, Test or Examination (--TEST) should be the same for a given value of Short Name of Measurement, Test or Examination (--TESTCD)
:)
(: Using a "GROUP BY", the speed is enormously improved  :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external; 
declare variable $define external; 
declare variable $defineversion external;
declare variable $datasetname external;
(: let $base := '/db/fda_submissions/cdisc01/' 
let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: iterate over all FINDINGS datasets that have --TESTCD and --TEST :)
for $dataset in $definedoc//odm:ItemGroupDef[@Name=$datasetname][@def:Class='FINDINGS' or ./def21:Class/@Name='FINDINGS']
    let $name := $dataset/@Name
    let $datasetname := (
		if($defineversion = '2.1') then $dataset/def21:leaf/@xlink:href
		else $dataset/def:leaf/@xlink:href
	)
    let $datasetlocation := concat($base,$datasetname)
    (: and get the OID of --TESTCD and --TEST :)
    let $testcdoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'TESTCD')]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a 
    )
    let $testcdname := $definedoc//odm:ItemDef[@OID=$testcdoid]/@Name
    let $testoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'TEST')]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a 
    )
    let $testname := $definedoc//odm:ItemDef[@OID=$testoid]/@Name
    (: Group the records by the value of --TESTCD :)
    let $orderedrecords := (
        for $record in doc($datasetlocation)//odm:ItemGroupData[odm:ItemData[@ItemOID=$testcdoid]]
            group by 
            $b:=$record/odm:ItemData[@ItemOID=$testcdoid]/@Value
            return element group {  
                $record
            }
        )
    (: all the records in the same group have the same value for --TESTCD. 
    They must also all have the same value for --TEST :)
    for $group in $orderedrecords
        let $testcdvalue := $group/odm:ItemGroupData[1]/odm:ItemData[@ItemOID=$testcdoid]/@Value
        for $record in $group/odm:ItemGroupData[position()>1]  (: start from the second one  :)
            let $recnum := $record/@data:ItemGroupDataSeq
            (: get the value of --TEST :)
            let $testvalue := $record/odm:ItemData[@ItemOID=$testoid]/@Value
            (: take the previous record in the same group, and take its --TEST value :)
            let $precrecord := $record/preceding-sibling::odm:ItemGroupData[1]  (: as they come in descending order :)
            let $recnum2 := $precrecord/@data:ItemGroupDataSeq
            let $testvalue2 := $precrecord/odm:ItemData[@ItemOID=$testoid]/@Value 
            (: when the value of--TEST in both records is different, report an error :)
            where not($testvalue2 = $testvalue) 
                return <error rule="SD0040" dataset="{data($name)}" variable="{data($testcdname)}" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">Inconsistent value for {data($testname)} within {data($testcdname)}='{data($testcdvalue)}' in dataset {data($datasetname)}. Following values were found: record number {data($recnum2)}: {data($testname)}={data($testvalue2)} - record number {data($recnum)}: {data($testname)}={data($testvalue)}</error>	
]]></rulexquery>
</sdsrule>

<sdsrule id="SD1043" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>--TESTCD value must be consistent within --TEST</ruledescription>
<ruledetaileddescription>All values of Short Name of Measurement, Test or Examination (--TESTCD) should be the same for a given value of Name of Measurement, Test or Examination (--TEST)</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>FINDINGS</domain>
<rulexquery><![CDATA[		
(: Rule SD1043 - Inconsistent value for --TESTCD within --TEST
All values of Short Name of Measurement, Test or Examination (--TESTCD) should be the same for a given value of Name of Measurement, Test or Examination (--TEST)
:)
(: Using a "GROUP BY", the speed is enormously improved  :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external; 
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
(: iterate over all datasets that have --TESTCD and --TEST :)
let $definedoc := doc(concat($base,$define))
for $dataset in $definedoc//odm:ItemGroupDef
    let $name := $dataset/@Name
    let $datasetname := (
		if($defineversion = '2.1') then $dataset/def21:leaf/@xlink:href
		else $dataset/def:leaf/@xlink:href
	)
    let $datasetdoc := (
		if($datasetname) then doc(concat($base,$datasetname))
		else ()
	)
    (: and get the OID of --TESTCD and --TEST :)
    let $testcdoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'TESTCD')]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a 
    )
    let $testcdname := $definedoc//odm:ItemDef[@OID=$testcdoid]/@Name
    let $testoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'TEST')]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a 
    )
    let $testname := $definedoc//odm:ItemDef[@OID=$testoid]/@Name
    (: Group the records by the value of --TEST :)
    let $orderedrecords := (
        for $record in $datasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$testoid]]
            group by 
            $b:=$record/odm:ItemData[@ItemOID=$testoid]/@Value
            return element group {  
                $record
            }
        )
    (: all the records in the same group have the same value for --TEST. 
    They must also all have the same value for --TESTCD :)
    for $group in $orderedrecords
        let $testvalue := $group/odm:ItemGroupData[1]/odm:ItemData[@ItemOID=$testoid]/@Value
        for $record in $group/odm:ItemGroupData[position()>1]  (: start from the second one  :)
            let $recnum := $record/@data:ItemGroupDataSeq
            let $testcdvalue := $record/odm:ItemData[@ItemOID=$testcdoid]/@Value
            (: take the previous record in the same group, and take its --TESTCD value :)
            let $precrecord := $record/preceding-sibling::odm:ItemGroupData[1]  (: as they come in descending order :)
            let $recnum2 := $precrecord/@data:ItemGroupDataSeq
            let $testcdvalue2 := $precrecord/odm:ItemData[@ItemOID=$testcdoid]/@Value 
            let $recnum2 := $precrecord/@data:ItemGroupDataSeq
            (: when the value of--TESTCD in both records is different, report an error :)
            where not($testcdvalue2 = $testcdvalue) 
                return <error rule="SD1043" dataset="{data($name)}" variable="{data($testname)}" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">Inconsistent value for {data($testcdname)} within {data($testname)}='{data($testvalue)}' in dataset {data($datasetname)}. Following values were found: record number {data($recnum2)}: {data($testcdname)}={data($testcdvalue2)} - record number {data($recnum)}: {data($testcdname)}={data($testcdvalue)}</error>	
]]></rulexquery>
</sdsrule>

<sdsrule id="SD1043-SD" last-update="2020-08-08" originator="FDA" requiresDomainOrDataset="Yes" standard="SDTM">
<ruledescription>--TESTCD value must be consistent within --TEST, single dataset or domain</ruledescription>
<ruledetaileddescription>All values of Short Name of Measurement, Test or Examination (--TESTCD) should be the same for a given value of Name of Measurement, Test or Examination (--TEST)</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>FINDINGS</domain>
<rulexquery><![CDATA[		
(: Rule SD1043 - Inconsistent value for --TESTCD within --TEST
All values of Short Name of Measurement, Test or Examination (--TESTCD) should be the same for a given value of Name of Measurement, Test or Examination (--TEST)
:)
(: Using a "GROUP BY", the speed is enormously improved  :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external; 
declare variable $define external; 
declare variable $defineversion external;
declare variable $datasetname external;
(: let $base := '/db/fda_submissions/cdiscpilot01/' :)
(: let $define := 'define_2_0.xml' :)
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
(: iterate over all datasets that have --TESTCD and --TEST :)
let $definedoc := doc(concat($base,$define))
for $dataset in $definedoc//odm:ItemGroupDef[@Name=$datasetname or @Domain=$datasetname]
    let $name := $dataset/@Name
    let $dsname := ( 
		if($defineversion = '2.1') then $dataset/def21:leaf/@xlink:href
		else $dataset/def:leaf/@xlink:href
	)
    let $datasetdoc := (
		if($dsname) then doc(concat($base,$dsname))
		else ()
	)
    (: and get the OID of --TESTCD and --TEST :)
    let $testcdoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'TESTCD')]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a 
    )
    let $testcdname := $definedoc//odm:ItemDef[@OID=$testcdoid]/@Name
    let $testoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'TEST')]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a 
    )
    let $testname := $definedoc//odm:ItemDef[@OID=$testoid]/@Name
    (: Group the records by the value of --TEST :)
    let $orderedrecords := (
        for $record in $datasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$testoid]]
            group by 
            $b:=$record/odm:ItemData[@ItemOID=$testoid]/@Value
            return element group {  
                $record
            }
        )
    (: all the records in the same group have the same value for --TEST. 
    They must also all have the same value for --TESTCD :)
    for $group in $orderedrecords
        let $testvalue := $group/odm:ItemGroupData[1]/odm:ItemData[@ItemOID=$testoid]/@Value
        for $record in $group/odm:ItemGroupData[position()>1]  (: start from the second one  :)
            let $recnum := $record/@data:ItemGroupDataSeq
            let $testcdvalue := $record/odm:ItemData[@ItemOID=$testcdoid]/@Value
            (: take the previous record in the same group, and take its --TESTCD value :)
            let $precrecord := $record/preceding-sibling::odm:ItemGroupData[1]  (: as they come in descending order :)
            let $recnum2 := $precrecord/@data:ItemGroupDataSeq
            let $testcdvalue2 := $precrecord/odm:ItemData[@ItemOID=$testcdoid]/@Value 
            let $recnum2 := $precrecord/@data:ItemGroupDataSeq
            (: when the value of--TESTCD in both records is different, report an error :)
            where not($testcdvalue2 = $testcdvalue) 
                return <error rule="SD1043" dataset="{data($name)}" variable="{data($testname)}" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">Inconsistent value for {data($testcdname)} within {data($testname)}='{data($testvalue)}' in dataset {data($datasetname)}. Following values were found: record number {data($recnum2)}: {data($testcdname)}={data($testcdvalue2)} - record number {data($recnum)}: {data($testcdname)}={data($testcdvalue)}</error>	
]]></rulexquery>
</sdsrule>

<sdsrule id="SD0046" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>QLABEL value must be consistent within QNAM</ruledescription>
<ruledetaileddescription>All values of Qualifier Variable Label (QLABEL) should be the same for a given value of Qualifier Variable Name (QNAM)</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>SUPPQUAL</domain>
<rulexquery><![CDATA[	
(: Rule SD0046 - Inconsistent value for QLABEL within QNAM
All values of Qualifier Variable Label (QLABEL) should be the same for a given value of Qualifier Variable Name (QNAM) in SUPPxx
:)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: Iterate over all SUPPxx datasets :)
for $dataset in $definedoc//odm:ItemGroupDef[starts-with(@Name,'SUPP')]
    let $name := $dataset/@Name
    let $datasetname := (
		if($defineversion = '2.1') then $dataset/def21:leaf/@xlink:href
		else $dataset/def:leaf/@xlink:href
	)
    let $datasetdoc := (
		if($datasetname) then doc(concat($base,$datasetname))
		else ()
	)
    (: and get the OID of QLABEL and of QNAM :)
    let $qlabeloid := (
        for $a in $definedoc//odm:ItemDef[@Name='QLABEL']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a 
    )
    let $qnamoid := (
        for $a in $definedoc//odm:ItemDef[@Name='QNAM']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a 
    )
    (: get all unique QNAM values within the dataset :)
    let $uniqueqnamvalues := distinct-values($datasetdoc//odm:ItemGroupData/odm:ItemData[@ItemOID=$qnamoid]/@Value)
        (: iterate over these unique QNAM values and for each of them, 
        find all records - also all with the same value for QNAM :)
        for $uniqueqnam in $uniqueqnamvalues
            let $records := $datasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$qnamoid][@Value=$uniqueqnam]]
            (: now get the distinct values for QLABEL within each QNAM :)
            let $uniqueqlabelvalues := distinct-values($records/odm:ItemData[@ItemOID=$qlabeloid]/@Value)
            (: the number of unique QLABEL values for each QNAM must be 1 :)
            let $uniqueqlabelvaluescount := count($uniqueqlabelvalues)
            where $uniqueqlabelvaluescount != 1
            return <warning rule="SD0046" dataset="{data($name)}" variable="QLABEL" rulelastupdate="2020-08-08">Inconsistent value for QLABEL within QNAM={data($uniqueqnam)} in dataset {data($datasetname)}. {data($uniqueqlabelvaluescount)} different combinations were found</warning>		
]]></rulexquery>
</sdsrule>

<sdsrule id="SD1130" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>QNAM value must be consistent within QLABEL</ruledescription>
<ruledetaileddescription>All values of Qualifier Variable Name (QNAM) should be the same for a given value of Qualifier Variable Label (QLABEL)</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>SUPPQUAL</domain>
<rulexquery><![CDATA[		
(: Rule SD1130 - Inconsistent value for QNAM within QLABEL
All values of Qualifier Variable Name (QNAM) should be the same for a given value of Qualifier Variable Label (QLABEL)
:)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: Iterate over all SUPPxx datasets :)
for $dataset in $definedoc//odm:ItemGroupDef[starts-with(@Name,'SUPP')]
    let $name := $dataset/@Name
	let $datasetname := (
		if($defineversion = '2.1') then $dataset/def21:leaf/@xlink:href
		else $dataset/def:leaf/@xlink:href
	)
    let $datasetdoc := (
		if($datasetname) then doc(concat($base,$datasetname))
		else ()
	)
    (: and get the OID of QLABEL and of QNAM :)
    let $qlabeloid := (
        for $a in $definedoc//odm:ItemDef[@Name='QLABEL']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a 
    )
    let $qnamoid := (
        for $a in $definedoc//odm:ItemDef[@Name='QNAM']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a 
    )
    (: get all unique QLABEL values within the dataset :)
    let $uniqueqlabelvalues := distinct-values($datasetdoc//odm:ItemGroupData/odm:ItemData[@ItemOID=$qlabeloid]/@Value)
        (: iterate over these unique QLABEL values and for each of them, 
        find all records - also all with the same value for QLABEL :)
        for $uniqueqlabel in $uniqueqlabelvalues
            let $records := $datasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$qlabeloid][@Value=$uniqueqlabel]]
            (: now get the distinct values for QNAM within each QLABEL :)
            let $uniqueqnamvalues := distinct-values($records/odm:ItemData[@ItemOID=$qnamoid]/@Value)
            (: the number of unique QNAM values for each QLABEL must be 1 :)
            let $uniqueqnamvaluescount := count($uniqueqnamvalues)
            where $uniqueqnamvaluescount != 1
            return <warning rule="SD1130" dataset="{data($name)}" variable="QNAM" rulelastupdate="2020-08-08">Inconsistent value for QNAM within QLABEL={data($uniqueqlabel)} in dataset {data($datasetname)}. {data($uniqueqnamvaluescount)} different combinations were found</warning>			
]]></rulexquery>
</sdsrule>

<sdsrule id="SD1033" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>ARM value must be consistent</ruledescription>
<ruledetaileddescription>A value for Description of Planned Arm (ARM) must have a unique value for Planned Arm Code (ARMCD) with the domain DM, TA, TV</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>DM</domain>
<domain>TA</domain>
<domain>TV</domain>
<rulexquery><![CDATA[		
(: Rule SD1033 - Inconsistent value for ARM: 
A value for Description of Planned Arm (ARM) must have a unique value for Planned Arm Code (ARMCD) with the domain DM, TA, TV
:)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: Iterate over the DM, TA and TV datasets :)
for $dataset in $definedoc//odm:ItemGroupDef[@Name='DM' or @Name='TA' or @Name='TV']
    let $name := $dataset/@Name
	let $datasetname := (
		if($defineversion = '2.1') then $dataset/def21:leaf/@xlink:href
		else $dataset/def:leaf/@xlink:href
	)
    let $datasetlocation := concat($base,$datasetname)
    (: and get the OID of ARMCD and ARM :)
    let $armcdoid := (
        for $a in $definedoc//odm:ItemDef[@Name='ARMCD']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a 
    )
    let $armoid := (
        for $a in $definedoc//odm:ItemDef[@Name='ARM']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a 
    )
    (: get all unique ARMCD values within the dataset :)
    let $uniquearmcdvalues := distinct-values(doc($datasetlocation)//odm:ItemGroupData/odm:ItemData[@ItemOID=$armcdoid]/@Value)
        (: iterate over these unique ARMCD values and for each of them, find all records - also all with the same value for ARMCD:)
        for $uniquearmcd in $uniquearmcdvalues
            let $records := doc($datasetlocation)//odm:ItemGroupData[odm:ItemData[@ItemOID=$armcdoid][@Value=$uniquearmcd]]
            (: now get the distinct values for ARM within each ARMCD :)
            let $uniquearmvalues := distinct-values($records/odm:ItemData[@ItemOID=$armoid]/@Value)
            (: the number of unique ARM values for each ARMCD must be 1 :)
            let $uniquearmvaluescount := count($uniquearmvalues)
            where $uniquearmvaluescount != 1 
            return <error rule="SD1033" dataset="{data($name)}" variable="ARM" rulelastupdate="2020-08-08">Inconsistent value for ARM within ARMCD={data($uniquearmcd)} in dataset {data($datasetname)}. {data($uniquearmvaluescount)} different combinations were found</error>		
]]></rulexquery>
</sdsrule>

<sdsrule id="SD1034" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>ARMCD value must be consistent</ruledescription>
<ruledetaileddescription>A value for Planned Arm Code (ARMCD) must have a unique value for Description of Planned Arm (ARM) with the domain within the domain DM, TA, TV</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>DM</domain>
<domain>TA</domain>
<domain>TV</domain>
<rulexquery><![CDATA[		
(: Rule SD1034 - Inconsistent value for ARMCD
A value for Planned Arm Code (ARMCD) must have a unique value for Description of Planned Arm (ARM) with the domain within the domain DM, TA, TV
:)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: Iterate over the TV and VS datasets :)
for $dataset in $definedoc//odm:ItemGroupDef[@Name='DM' or @Name='TA' or @Name='TV']
    let $name := $dataset/@Name
	let $datasetname := (
		if($defineversion = '2.1') then $dataset/def21:leaf/@xlink:href
		else $dataset/def:leaf/@xlink:href
	)
    let $datasetlocation := concat($base,$datasetname)
    (: and get the OID of ARMCD and ARM :)
    let $armcdoid := (
        for $a in $definedoc//odm:ItemDef[@Name='ARMCD']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a 
    )
    let $armoid := (
        for $a in $definedoc//odm:ItemDef[@Name='ARM']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a 
    )
    (: get all unique ARM values within the dataset :)
	(: TODO: get the record number :)
    let $uniquearmvalues := distinct-values(doc($datasetlocation)//odm:ItemGroupData/odm:ItemData[@ItemOID=$armoid]/@Value)
        (: iterate over these unique ARM values and for each of them, find all records - also all with the same value for ARM :)
        for $uniquearm in $uniquearmvalues
            let $records := doc($datasetlocation)//odm:ItemGroupData[odm:ItemData[@ItemOID=$armoid][@Value=$uniquearm]]
            (: now get the distinct values for ARMCD within each ARM :)
            let $uniquearmcdvalues := distinct-values($records/odm:ItemData[@ItemOID=$armcdoid]/@Value)
            (: the number of unique ARMCD values for each ARM must be 1 :)
            let $uniquearmcdvaluescount := count($uniquearmcdvalues)
            where $uniquearmcdvaluescount != 1 
            return <error rule="SD1034" dataset="{data($name)}" variable="ARMCD" recordnumber="TODO" rulelastupdate="2020-08-08">Inconsistent value for ARMCD within ARM={data($uniquearm)} in dataset {data($datasetname)}. {data($uniquearmcdvaluescount)} different combinations were found</error>		
]]></rulexquery>
</sdsrule>

<sdsrule id="SD1068" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>ELEMENT value may not be duplicate in TE</ruledescription>
<ruledetaileddescription>The value of Element (Description of Element) variable must be unique within Trial Elements (TE) domain</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>TE</domain>
<rulexquery><![CDATA[	
(: Rule SD1068 - Duplicate ELEMENT value:
The value of Element (Description of Element) variable must be unique within Trial Elements (TE) domain
:)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))  
(: get the TE dataset :)
let $tedataset := $definedoc//odm:ItemGroupDef[@Name='TE']
let $tedatasetname := (
	if($defineversion = '2.1') then $tedataset/def21:leaf/@xlink:href
	else $tedataset/def:leaf/@xlink:href
)
let $tedatasetlocation := concat($base,$tedatasetname)
(: get the OID of the ELEMENT variable :)
let $elementoid := (
    for $a in $definedoc//odm:ItemDef[@Name='ELEMENT']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name='TE']/odm:ItemRef/@ItemOID
        return $a
)
(: iterate over all the records in the TE dataset :)
for $record in doc($tedatasetlocation)//odm:ItemGroupData
    let $recnum := $record/@data:ItemGroupDataSeq
    (: get the value of ELEMENT :)
    let $elementvalue := $record//odm:ItemData[@ItemOID=$elementoid]/@Value
    (: iterate over all following records :)
    for $record2 in doc($tedatasetlocation)//odm:ItemGroupData[@data:ItemGroupDataSeq > $recnum]
        (: and compare the value of ELEMENT in the record with the original one :)
        let $recnum2 := $record2/@data:ItemGroupDataSeq
        let $elementvalue2 := $record2//odm:ItemData[@ItemOID=$elementoid]/@Value
        where $elementvalue=$elementvalue2
        return <warning rule="SD1068" dataset="TE" variable="ELEMENT" rulelastupdate="2020-08-08" recordnumber="{data($recnum2)}">Duplicate ELEMENT value: records {data($recnum)} and {data($recnum2)} have the same value for ELEMENT, value={data($elementvalue)}</warning>
]]></rulexquery>
</sdsrule>

<sdsrule id="SD1064" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>ETCD value may not be duplicate in TE</ruledescription>
<ruledetaileddescription>The value of Element Code (ETCD) variable must be unique within Trial Elements (TE) domain</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>TE</domain>
<rulexquery><![CDATA[		
(: Rule SD1064 - Duplicate ELEMENT value:
The value of Element (Description of Element) variable must be unique within Trial Elements (TE) domain
:)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: get the TE dataset :)
let $tedataset := $definedoc//odm:ItemGroupDef[@Name='TE']
let $tedatasetname := (
	if($defineversion = '2.1') then $tedataset/def21:leaf/@xlink:href
	else $tedataset/def:leaf/@xlink:href
)
let $tedatasetlocation := concat($base,$tedatasetname)
(: get the OID of the ETCD variable :)
let $etcdoid := (
    for $a in $definedoc//odm:ItemDef[@Name='ETCD']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name='TE']/odm:ItemRef/@ItemOID
        return $a
)
(: iterate over all the records in the TE dataset :)
for $record in doc($tedatasetlocation)//odm:ItemGroupData
    let $recnum := $record/@data:ItemGroupDataSeq
    (: get the value of ETCD :)
    let $etcdvalue := $record//odm:ItemData[@ItemOID=$etcdoid]/@Value
    (: iterate over all following records :)
    for $record2 in doc($tedatasetlocation)//odm:ItemGroupData[@data:ItemGroupDataSeq > $recnum]
        (: and compare the value of ETCD in the record with the original one :)
        let $recnum2 := $record2/@data:ItemGroupDataSeq
        let $etcdvalue2 := $record2//odm:ItemData[@ItemOID=$etcdoid]/@Value
        where $etcdvalue=$etcdvalue2
        return <error rule="SD1064" dataset="TE" variable="ETCD" rulelastupdate="2020-08-08" recordnumber="{data($recnum2)}">Duplicate ETCD value: records {data($recnum)} and {data($recnum2)} have the same value for ETCD, value={data($etcdvalue)}</error>
]]></rulexquery>
</sdsrule>

<sdsrule id="SD1050" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>ETCD must be unique within ELEMENT</ruledescription>
<ruledetaileddescription>Element Code (ETCD) must have a unique value for a given value of Description of Element (ELEMENT) within the domain</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>SE</domain>
<domain>TA</domain>
<rulexquery><![CDATA[
(: Rule SD1050: Non-unique value for ETCD within ELEMENT :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external; 
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: Iterate over the SE and TA datasets :)
for $dataset in $definedoc//odm:ItemGroupDef[@Name='SE' or @Name='TA']
    let $name := $dataset/@Name
	let $datasetname := (
		if($defineversion = '2.1') then $dataset/def21:leaf/@xlink:href
		else $dataset/def:leaf/@xlink:href
	)
    let $datasetlocation := concat($base,$datasetname)
    (: and get the OID of ETCD and ELEMENT :)
    let $etcdoid := (
        for $a in $definedoc//odm:ItemDef[@Name='ETCD']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a 
    )
    let $elementoid := (
        for $a in $definedoc//odm:ItemDef[@Name='ELEMENT']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a 
    )
    (: group by ELEMENT - each group has the same valueELEMENT :)
    let $orderedrecords := (
        for $record in doc($datasetlocation)//odm:ItemGroupData
            group by 
            $b := $record/odm:ItemData[@ItemOID=$elementoid]/@Value
            return element group {  
                $record
            }
    )
    (: iterate over the groups, get the first record only to indentify ETCD and ELEMENT :)
    for $group in $orderedrecords
        let $etcdvalue := $group/odm:ItemGroupData[1]/odm:ItemData[@ItemOID=$etcdoid]/@Value
        let $elementvalue := $group/odm:ItemGroupData[1]/odm:ItemData[@ItemOID=$elementoid]/@Value
        let $recnum := $group/odm:ItemGroupData[1]/@data:ItemGroupDataSeq
        (: now check whether there are any other records that have a different value for ETCD :)
        for $record in $group/odm:ItemGroupData[position()>1]
            let $etcdvalue2 := $record/odm:ItemData[@ItemOID=$etcdoid]/@Value
            (: if the ETCD value is different from that within the first record of the group, give an error :)
            let $recnum2 := $record/@data:ItemGroupDataSeq
            where $etcdvalue != $etcdvalue2
            return <error rule="SD1050" dataset="{data($name)}" variable="ETCD" recordnumber="{data($recnum2)}" rulelastupdate="2020-08-08">Non-unique value for ETCD within ELEMENT '{data($elementvalue)}' in dataset {data($datasetname)}. Record {data($recnum2)} has ETCD value '{data($etcdvalue2)}' whereas record {data($recnum)} has ETCD value '{data($etcdvalue)}'</error>
]]>
</rulexquery>
</sdsrule>

<sdsrule id="SD1133" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>ACTARM must be consistent in DM</ruledescription>
<ruledetaileddescription>A value for Description of Actual Arm (ACTARM) must have a unique value for Actual Arm Code (ACTARMCD) with the domain</ruledetaileddescription>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>DM</domain>
<rulexquery><![CDATA[
(: Rule SD1133: Inconsistent value for ACTARM
A value for Description of Actual Arm (ACTARM) must have a unique value for Actual Arm Code (ACTARMCD) with the domain. :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external; 
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: Iterate over the DM dataset :)
for $dataset in $definedoc//odm:ItemGroupDef[@Name='DM']
    let $name := $dataset/@Name
    let $datasetname := ( 
		if($defineversion = '2.1') then $dataset/def21:leaf/@xlink:href
		else $dataset/def:leaf/@xlink:href
	)
    let $datasetlocation := concat($base,$datasetname)
    (: and get the OID of ACTARMCD and ACTARM :)
    let $actarmcdoid := (
        for $a in $definedoc//odm:ItemDef[@Name='ACTARMCD']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a 
    )
    let $actarmoid := (
        for $a in $definedoc//odm:ItemDef[@Name='ACTARM']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a 
    )
    (: group by ACTARMCD - each group has the same value of ACTARMCD :)
    let $orderedrecords := (
        for $record in doc($datasetlocation)//odm:ItemGroupData
            group by 
            $b := $record/odm:ItemData[@ItemOID=$actarmcdoid]/@Value
            return element group {  
                $record
            }
    )
    (: iterate over the groups, get the first record only to indentify ACTARMCD and ACTARM :)
    for $group in $orderedrecords
        let $actarmcdvalue := $group/odm:ItemGroupData[1]/odm:ItemData[@ItemOID=$actarmcdoid]/@Value
        let $actarmvalue := $group/odm:ItemGroupData[1]/odm:ItemData[@ItemOID=$actarmoid]/@Value
        let $recnum := $group/odm:ItemGroupData[1]/@data:ItemGroupDataSeq
        (: now check whether there are any other records that have a different value for ACTARM :)
        for $record in $group/odm:ItemGroupData[position()>1]
            let $actarmvalue2 := $record/odm:ItemData[@ItemOID=$actarmoid]/@Value
            (: if the ACTARM value is different from that within the first record of the group, give an error :)
            let $recnum2 := $record/@data:ItemGroupDataSeq
            where $actarmvalue != $actarmvalue2
            return <error rule="SD1133" dataset="{data($name)}" variable="ACTARM" recordnumber="{data($recnum2)}" rulelastupdate="2020-08-08">Non-unique value for ACTARM within ACTARMCD '{data($actarmcdvalue)}' in dataset {data($datasetname)}. Record {data($recnum2)} has ACTARM value '{data($actarmvalue2)}' whereas record {data($recnum)} has ACTARM value '{data($actarmvalue)}'</error>
]]>
</rulexquery>
</sdsrule>

<sdsrule id="SD1134" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>ACTARMCD must be consistent in DM</ruledescription>
<ruledetaileddescription>A value for Actual Arm Code (ACTARMCD) must have a unique value for Description of Actual Arm (ACTARM) with the domain</ruledetaileddescription>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>DM</domain>
<rulexquery><![CDATA[
(: Rule SD1134: Inconsistent value for ACTARMCD
A value for Actual Arm Code (ACTARMCD) must have a unique value for Description of Actual Arm (ACTARM) with the domain. :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external; 
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: Iterate over the DM dataset :)
for $dataset in $definedoc//odm:ItemGroupDef[@Name='DM']
    let $name := $dataset/@Name
    let $datasetname := ( 
		if($defineversion = '2.1') then $dataset/def21:leaf/@xlink:href
		else $dataset/def:leaf/@xlink:href
	)
    let $datasetlocation := concat($base,$datasetname)
    (: and get the OID of ACTARMCD and ACTARM :)
    let $actarmcdoid := (
        for $a in $definedoc//odm:ItemDef[@Name='ACTARMCD']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a 
    )
    let $actarmoid := (
        for $a in $definedoc//odm:ItemDef[@Name='ACTARM']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a 
    )
    (: group by ACTARM - each group has the same value of ACTARM :)
    let $orderedrecords := (
        for $record in doc($datasetlocation)//odm:ItemGroupData
            group by 
            $b := $record/odm:ItemData[@ItemOID=$actarmoid]/@Value
            return element group {  
                $record
            }
    )
    (: iterate over the groups, get the first record only to indentify ACTARMCD and ACTARM :)
    for $group in $orderedrecords
        let $actarmcdvalue := $group/odm:ItemGroupData[1]/odm:ItemData[@ItemOID=$actarmcdoid]/@Value
        let $actarmvalue := $group/odm:ItemGroupData[1]/odm:ItemData[@ItemOID=$actarmoid]/@Value
        let $recnum := $group/odm:ItemGroupData[1]/@data:ItemGroupDataSeq
        (: now check whether there are any other records that have a different value for ACTARMCD :)
        for $record in $group/odm:ItemGroupData[position()>1]
            let $actarmcdvalue2 := $record/odm:ItemData[@ItemOID=$actarmcdoid]/@Value
            (: if the ACTARMCD value is different from that within the first record of the group, give an error :)
            let $recnum2 := $record/@data:ItemGroupDataSeq
            where $actarmcdvalue != $actarmcdvalue2
            return <error rule="SD1134" dataset="{data($name)}" variable="ACTARMCD" recordnumber="{data($recnum2)}" rulelastupdate="2020-08-08">Non-unique value for ACTARMCD within ACTARM '{data($actarmvalue)}' in dataset {data($datasetname)}. Record {data($recnum2)} has ACTARMCD value '{data($actarmcdvalue2)}' whereas record {data($recnum)} has ACTARMCD value '{data($actarmcdvalue)}'</error>
]]>
</rulexquery>
</sdsrule>

<sdsrule id="SD1132" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>AESER must be 'Y' when any of AESCAN, AESCONG, AESDISAB, AESDTH, AESHOSP, AESLIFE, AESMIE has value 'Y'</ruledescription>
<ruledetaileddescription>Serious Event (AESER) variable value is expected to be 'Y', when either of Seriousness Criteria variables (AESCAN, AESCONG, AESDISAB, AESDTH, AESHOSP, AESLIFE, AESMIE) has value 'Y'</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>AE</domain>
<rulexquery><![CDATA[
(: Rule SD1132: Serious Event (AESER) variable value is expected to be 'Y', when either of Seriousness Criteria variables (AESCAN, AESCONG, AESDISAB, AESDTH, AESHOSP, AESLIFE, AESMIE) has value 'Y' :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' 
let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: Get the AE dataset(s) :)
for $aedataset in $definedoc//odm:ItemGroupDef[@Name='AE']
    let $aename := $aedataset/@Name
    let $aedatasetname := ( 
		if($defineversion = '2.1') then $aedataset/def21:leaf/@xlink:href
		else $aedataset/def:leaf/@xlink:href
	)
    let $aedatasetlocation := concat($base,$aedatasetname)
    (: and get the OID of the AESER and AESOD variables :)
    let $aeseroid := (
        for $a in $definedoc//odm:ItemDef[@Name='AESER']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$aename]/odm:ItemRef/@ItemOID
        return $a
    )
    (: get the OIDs of AESCAN, AESCONG, AESDISAB, AESDTH, AESHOSP, AESLIFE, AESMIE  :)
    let $aescanoid := (
        for $a in $definedoc//odm:ItemDef[@Name='AESCAN']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$aename]/odm:ItemRef/@ItemOID
        return $a
    )
    let $aescongoid := (
        for $a in $definedoc//odm:ItemDef[@Name='AESCONG']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$aename]/odm:ItemRef/@ItemOID
        return $a
    )
    let $aesdisaboid := (
        for $a in $definedoc//odm:ItemDef[@Name='AESDISAB']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$aename]/odm:ItemRef/@ItemOID
        return $a
    )
    let $aesdthoid := (
        for $a in $definedoc//odm:ItemDef[@Name='AESDTH']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$aename]/odm:ItemRef/@ItemOID
        return $a
    )
    let $aeshospoid := (
        for $a in $definedoc//odm:ItemDef[@Name='AESHOSP']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$aename]/odm:ItemRef/@ItemOID
        return $a
    )
    let $aeslifeoid := (
        for $a in $definedoc//odm:ItemDef[@Name='AESLIFE']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$aename]/odm:ItemRef/@ItemOID
        return $a
    )
    let $aesmieoid := (
        for $a in $definedoc//odm:ItemDef[@Name='AESMIE']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$aename]/odm:ItemRef/@ItemOID
        return $a
    )
    (: iterate over all records that have any of AESCAN, AESCONG, AESDISAB, AESDTH, AESHOSP, AESLIFE, AESMIE = 'Y' :)
    for $record in doc($aedatasetlocation)//odm:ItemGroupData[odm:ItemData[@ItemOID=$aescanoid and @Value='Y']
        or odm:ItemData[@ItemOID=$aescongoid and @Value='Y']
        or odm:ItemData[@ItemOID=$aesdisaboid and @Value='Y']
        or odm:ItemData[@ItemOID=$aesdthoid and @Value='Y']
        or odm:ItemData[@ItemOID=$aeshospoid and @Value='Y']
        or odm:ItemData[@ItemOID=$aeslifeoid and @Value='Y']
        or odm:ItemData[@ItemOID=$aesmieoid and @Value='Y'] ]
        let $recnum := $record/@data:ItemGroupDataSeq
        (: get the value of AESER (if any) :)
        let $aeservalue := $record/odm:ItemData[@ItemOID=$aeseroid]/@Value
        (: AESER must have the value 'Y' :)
        where not($aeservalue) or not($aeservalue='Y') 
        return <error rule="SD1132" dataset="AE" variable="AESER" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">AESER is not 'Y', when any of AESCAN, AESCONG, AESDISAB, AESDTH, AESHOSP, AESLIFE, AESMIE equals 'Y', value found for AESER={data($aeservalue)}</error>	
]]>
</rulexquery>
</sdsrule>

<sdsrule id="SD0090" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>AESDTH must be 'Y', when AEOUT='FATAL'</ruledescription>
<ruledetaileddescription>Results in Death (AEDTH) should equal 'Y', when Outcome of Adverse Event (AEOUT)  is 'FATAL'</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>AE</domain>
<rulexquery><![CDATA[		
(: Rule SD0090 - AESDTH is not 'Y', when AEOUT='FATAL':
Results in Death (AEDTH) should equal 'Y', when Outcome of Adverse Event (AEOUT)  is 'FATAL'
:)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/'  :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: Get the AE dataset(s) :)
for $aedataset in $definedoc//odm:ItemGroupDef[@Name='AE']
    let $aename := $aedataset/@Name
    let $aedatasetname := ( 
		if($defineversion = '2.1') then $aedataset/def21:leaf/@xlink:href
		else $aedataset/def:leaf/@xlink:href
	)
    let $aedatasetlocation := concat($base,$aedatasetname)
    (: and get the OID of AESDTH and AEOUT :)
    let $aesdthoid := (
        for $a in $definedoc//odm:ItemDef[@Name='AESDTH']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$aename]/odm:ItemRef/@ItemOID
        return $a
    )
    let $aeoutoid := (
        for $a in $definedoc//odm:ItemDef[@Name='AEOUT']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$aename]/odm:ItemRef/@ItemOID
        return $a
    )
    (: iterate over all records that have AEOUT='FATAL' :)
    for $record in doc($aedatasetlocation)//odm:ItemGroupData[odm:ItemData[@ItemOID=$aeoutoid and @Value='FATAL']]
        let $recnum := $record/@data:ItemGroupDataSeq
        let $aesdthvalue := $record/odm:ItemData[@ItemOID=$aesdthoid]/@Value
        (: AEDTH must be 'Y' :)
        let $recnum := $record/@data:ItemGroupDataSeq
        where not($aesdthvalue='Y')
        return <error rule="SD0090" dataset="AE" variable="AEDTH" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">AESDTH is not 'Y', when AEOUT='FATAL', value for AESDTH found is '{data($aesdthvalue)}'</error>
]]></rulexquery>
</sdsrule>

<sdsrule id="SD1112" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>TA dataset must be present</ruledescription>
<ruledetaileddescription>Trial Arms (TA) dataset should be included in every submission</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>TA</domain>
<rulexquery><![CDATA[	
(: Rule SD1112 - Missing TA dataset :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
declare namespace request="http://exist-db.org/xquery/request";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external;
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: get the location of the TA dataset :)
let $tadatasetlocation := (
	if($defineversion = '2.1') then $definedoc//odm:ItemGroupDef[@Name='TA']/def21:leaf/@xlink:href
	else $definedoc//odm:ItemGroupDef[@Name='TA']/def:leaf/@xlink:href
)
let $tadatasetdoc := (
	if($tadatasetlocation) then doc(concat($base,$tadatasetlocation))
	else ()
)
(: we now have the location of the dataset, check whether it is available there :)
where not($tadatasetlocation) or (not($tadatasetdoc))
return <warning rule="SD1112" dataset="TA" rulelastupdate="2020-08-08">Dataset 'TA' could not be found in collection {data($base)}</warning>	
]]></rulexquery>
</sdsrule>

<sdsrule id="SD1113" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>TE dataset must be present</ruledescription>
<ruledetaileddescription>Trial Elements (TE) dataset should be included in every submission</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>TE</domain>
<rulexquery><![CDATA[	
(: Rule SD1113 - Missing TE dataset :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
declare namespace request="http://exist-db.org/xquery/request";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external;
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: get the location of the TE dataset :)
let $tedatasetlocation := ( 
	if($defineversion = '2.1') then $definedoc//odm:ItemGroupDef[@Name='TE']/def21:leaf/@xlink:href
	else $definedoc//odm:ItemGroupDef[@Name='TE']/def:leaf/@xlink:href
)
let $tedatasetdoc := (
	if($tedatasetlocation) then doc(concat($base,$tedatasetlocation))
	else ()
)
(: we now have the location of the dataset, check whether it is available there :)
where not($tedatasetlocation) or not(doc-available($tedatasetdoc))
return <warning rule="SD1113" dataset="TE" rulelastupdate="2020-08-08">Dataset 'TE'  could not be found in collection {data($base)}</warning>
]]></rulexquery>
</sdsrule>

<!-- Rule "SE0007 Missing TX dataset" is only for SEND -->


<sdsrule id="SD1214" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>Only one ADDON record is allowed in TS</ruledescription>
<ruledetaileddescription>Trial Summary (TS) domain must contain only one record for 'ADDON' Parameter</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>TS</domain>
<rulexquery><![CDATA[		
(: Rule SD1214 - Multiple ADDON records:
Trial Summary (TS) domain must contain only one record for 'ADDON' Parameter
:)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: get the TS dataset :)
let $tsdataset := $definedoc//odm:ItemGroupDef[@Name='TS']
let $tsdatasetname := (
	if($defineversion = '2.1') then $tsdataset/def21:leaf/@xlink:href
	else $tsdataset/def:leaf/@xlink:href
)
let $tsdatasetlocation := concat($base,$tsdatasetname)
(: get the OID of the TSPARMCD variable :)
let $tsparmcdoid := (
    for $a in $definedoc//odm:ItemDef[@Name='TSPARMCD']/@OID 
    where $a = $definedoc//odm:ItemGroupDef[@Name='TS']/odm:ItemRef/@ItemOID
    return $a
)
(: count the number of records in the TS dataset that have TSPARMCD=ADDON :)
let $count := count(doc($tsdatasetlocation)//odm:ItemGroupData/odm:ItemData[@ItemOID=$tsparmcdoid and @Value='ADDON'])
(: there must be not more than 1 such records :)
where $count > 1
return <error rule="SD1214" dataset="TS" variable="TSPARMCD" rulelastupdate="2020-08-08">Multiple ADDON records in dataset TS, number of ADDON records found ={data($count)}</error>
]]></rulexquery>
</sdsrule>

<sdsrule id="SD1216" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>Only one AGEMAX record is allowed in TS</ruledescription>
<ruledetaileddescription>Trial Summary (TS) domain must contain only one record for 'AGEMAX' Parameter</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>TS</domain>
<rulexquery><![CDATA[
(: Rule SD1216 - Multiple AGEMAX records:
Trial Summary (TS) domain must contain only one record for 'AGEMAX' Parameter
:)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
declare namespace xs="http://www.w3.org/2001/XMLSchema";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/'  :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: get the TS dataset :)
let $tsdataset := $definedoc//odm:ItemGroupDef[@Name='TS']
let $tsdatasetname := (
	if($defineversion = '2.1') then $tsdataset/def21:leaf/@xlink:href
	else $tsdataset/def:leaf/@xlink:href
)
let $tsdatasetlocation := concat($base,$tsdatasetname)
(: get the OID of the TSPARMCD variable :)
let $tsparmcdoid := (
    for $a in $definedoc//odm:ItemDef[@Name='TSPARMCD']/@OID 
    where $a = $definedoc//odm:ItemGroupDef[@Name='TS']/odm:ItemRef/@ItemOID
    return $a
)
(: count the number of records for which TSPARMCD=AGEMAX :)
let $count := count(doc($tsdatasetlocation)//odm:ItemGroupData[odm:ItemData[@ItemOID=$tsparmcdoid and @Value='AGEMAX']])
(: The number of AGEMAX records may not be larger than 1 :)
where $count > 1
return <error rule="SD1216" dataset="TS" variable="TSPARMCD" rulelastupdate="2020-08-08">Multiple AGEMAX records in TS: number of AGEMAX records found={data($count)}</error>	
]]></rulexquery>
</sdsrule>

<sdsrule id="SD1218" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>Only one AGEMIN record is allowed in TS</ruledescription>
<ruledetaileddescription>Trial Summary (TS) domain must contain only one record for 'AGEMIN' Parameter</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>TS</domain>
<rulexquery><![CDATA[	
(: Rule SD1218 - Multiple AGEMIN records:
Trial Summary (TS) domain must contain only one record for 'AGEMIN' Parameter
:)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
declare namespace xs="http://www.w3.org/2001/XMLSchema";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: get the TS dataset :)
let $tsdataset := $definedoc//odm:ItemGroupDef[@Name='TS']
let $tsdatasetname := ( 
	if($defineversion = '2.1') then $tsdataset/def21:leaf/@xlink:href
	else $tsdataset/def:leaf/@xlink:href
)
let $tsdatasetlocation := concat($base,$tsdatasetname)
(: get the OID of the TSPARMCD variable :)
let $tsparmcdoid := (
    for $a in $definedoc//odm:ItemDef[@Name='TSPARMCD']/@OID 
    where $a = $definedoc//odm:ItemGroupDef[@Name='TS']/odm:ItemRef/@ItemOID
    return $a
)
(: count the number of records for which TSPARMCD=AGEMIN :)
let $count := count(doc($tsdatasetlocation)//odm:ItemGroupData[odm:ItemData[@ItemOID=$tsparmcdoid and @Value='AGEMIN']])
(: The number of AGEMIN records may not be larger than 1 :)
where $count > 1
return <error rule="SD1218" dataset="TS" variable="AGEMIN" rulelastupdate="2020-08-08">Multiple AGEMIN records in dataset TS: number of AGEMIN records found={data($count)}</error>	
]]></rulexquery>
</sdsrule>

<sdsrule id="SD1220" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>Only one LENGTH record is allowed in TS</ruledescription>
<ruledetaileddescription>Trial Summary (TS) domain must contain only one record for 'LENGTH' Parameter</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>TS</domain>
<rulexquery><![CDATA[	
(: Rule SD1220 - Multiple LENGTH records:
Trial Summary (TS) domain must contain only one record for 'LENGTH' Parameter
:)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
declare namespace xs="http://www.w3.org/2001/XMLSchema";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define)) 
(: get the TS dataset :)
let $tsdataset := $definedoc//odm:ItemGroupDef[@Name='TS']
let $tsdatasetname := (
	if($defineversion = '2.1') then $tsdataset/def21:leaf/@xlink:href
	else $tsdataset/def:leaf/@xlink:href
)
let $tsdatasetlocation := concat($base,$tsdatasetname)
(: get the OID of the TSPARMCD variable :)
let $tsparmcdoid := (
    for $a in $definedoc//odm:ItemDef[@Name='TSPARMCD']/@OID 
    where $a = $definedoc//odm:ItemGroupDef[@Name='TS']/odm:ItemRef/@ItemOID
    return $a
)
(: count the number of records for which TSPARMCD=LENGTH :)
let $count := count(doc($tsdatasetlocation)//odm:ItemGroupData[odm:ItemData[@ItemOID=$tsparmcdoid and @Value='LENGTH']])
(: The number of LENGTH records may not be larger than 1 :)
where $count > 1
return <error rule="SD1220" dataset="TS" rulelastupdate="2020-08-08">Multiple LENGTH records in dataset TS: number of LENGTH records found={data($count)}</error>	
]]></rulexquery>
</sdsrule>

<sdsrule id="SD1222" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>Only one PLANSUB record is allowed in TS</ruledescription>
<ruledetaileddescription>Trial Summary (TS) domain must contain only one record for 'PLANSUB' Parameter</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>TS</domain>
<rulexquery><![CDATA[
(: Rule SD1222 - Multiple PLANSUB records:
Trial Summary (TS) domain must contain only one record for 'PLANSUB' Parameter
:)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
declare namespace xs="http://www.w3.org/2001/XMLSchema";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/'  :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define)) 
(: get the TS dataset :)
let $tsdataset := $definedoc//odm:ItemGroupDef[@Name='TS']
let $tsdatasetname := (
	if($defineversion = '2.1') then $tsdataset/def21:leaf/@xlink:href
	else $tsdataset/def:leaf/@xlink:href
)
let $tsdatasetlocation := concat($base,$tsdatasetname)
(: get the OID of the TSPARMCD variable :)
let $tsparmcdoid := (
    for $a in $definedoc//odm:ItemDef[@Name='TSPARMCD']/@OID 
    where $a = $definedoc//odm:ItemGroupDef[@Name='TS']/odm:ItemRef/@ItemOID
    return $a
)
(: count the number of records for which TSPARMCD=PLANSUB :)
let $count := count(doc($tsdatasetlocation)//odm:ItemGroupData[odm:ItemData[@ItemOID=$tsparmcdoid and @Value='PLANSUB']])
(: The number of PLANSUB records may not be larger than 1 :)
where $count > 1
return <error rule="SD1222" dataset="TS" rulelastupdate="2020-08-08">Multiple PLANSUB Records in dataset TS: number of PLANSUB records found={data($count)}</error>	
]]></rulexquery>
</sdsrule>

<sdsrule id="SD1224" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>Only one RANDOM record is allowed in TS</ruledescription>
<ruledetaileddescription>Trial Summary (TS) domain must contain only one record for 'RANDOM' Parameter</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>TS</domain>
<rulexquery><![CDATA[	
(: Rule SD1224 - Multiple RANDOM records:
Trial Summary (TS) domain must contain only one record for 'RANDOM' Parameter
:)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
declare namespace xs="http://www.w3.org/2001/XMLSchema";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: get the TS dataset :)
let $tsdataset := doc(concat($base,$define))//odm:ItemGroupDef[@Name='TS']
let $tsdatasetname := (
	if($defineversion = '2.1') then $tsdataset/def21:leaf/@xlink:href
	else $tsdataset/def:leaf/@xlink:href
)
let $tsdatasetlocation := concat($base,$tsdatasetname)
(: get the OID of the TSPARMCD variable :)
let $tsparmcdoid := (
    for $a in $definedoc//odm:ItemDef[@Name='TSPARMCD']/@OID 
    where $a = $definedoc//odm:ItemGroupDef[@Name='TS']/odm:ItemRef/@ItemOID
    return $a
)
(: count the number of records for which TSPARMCD=RANDOM :)
let $count := count(doc($tsdatasetlocation)//odm:ItemGroupData[odm:ItemData[@ItemOID=$tsparmcdoid and @Value='RANDOM']])
(: The number of RANDOM records may not be larger than 1 :)
where $count > 1
return <error rule="SD1224" dataset="TS" rulelastupdate="2020-08-08">Multiple RANDOM Records in dataset TS: number of RANDOM records found={data($count)}</error>	
]]></rulexquery>
</sdsrule>

<sdsrule id="SD1225" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>Only one SEXPOP record is allowed in TS</ruledescription>
<ruledetaileddescription>Trial Summary (TS) domain must contain only one record for 'SEXPOP' Parameter</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>TS</domain>
<rulexquery><![CDATA[	
(: Rule SD1225 - Multiple SEXPOP records:
Trial Summary (TS) domain must contain only one record for 'SEXPOP' Parameter
:)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
declare namespace xs="http://www.w3.org/2001/XMLSchema";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: get the TS dataset :)
let $tsdataset := $definedoc//odm:ItemGroupDef[@Name='TS']
let $tsdatasetname := (
	if($defineversion = '2.1') then $tsdataset/def21:leaf/@xlink:href
	else $tsdataset/def:leaf/@xlink:href
)
let $tsdatasetlocation := concat($base,$tsdatasetname)
(: get the OID of the TSPARMCD variable :)
let $tsparmcdoid := (
    for $a in $definedoc//odm:ItemDef[@Name='TSPARMCD']/@OID 
    where $a = $definedoc//odm:ItemGroupDef[@Name='TS']/odm:ItemRef/@ItemOID
    return $a
)
(: count the number of records for which TSPARMCD=SEXPOP :)
let $count := count(doc($tsdatasetlocation)//odm:ItemGroupData[odm:ItemData[@ItemOID=$tsparmcdoid and @Value='SEXPOP']])
(: The number of SEXPOP records may not be larger than 1 :)
where $count > 1
return <error rule="SD1225" dataset="TS" variable="SEXPOP" rulelastupdate="2020-08-08">Multiple SEXPOP Records: number of SEXPOP records found={data($count)}</error>
]]></rulexquery>
</sdsrule>

<!-- TODO: Rule SD2259: TSVAL/TSVALCD value mismatch for INDIC: TSVAL and TSVALCD values must be populated from the same concept description record in SNOMED CT -->
<!-- Is there any RESTful web service for this? -->

<sdsrule originator="FDA" id="SD2240" standard="SDTM" last-update="2020-08-08" >
		<ruledescription>TSVCDREF variable value must be 'SNOMED', when TSPARAMCD='INDIC'</ruledescription>
		<ruledetaileddescription>TSVCDREF variable value must be 'SNOMED', when TSPARAMCD='INDIC'</ruledetaileddescription>
		<igversion>3.1.2</igversion>
		<igversion>3.1.3</igversion>
		<igversion>3.2</igversion>
		<domain>TS</domain>
		<synonym context="CDISC">SD2240</synonym>
		<rulexquery><![CDATA[
(: Rule SD2240 - When TSPARMCD is 'INDIC' then TSVCDREF = 'SNOMED'  :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: get the TS dataset :)
let $tsdataset := $definedoc//odm:ItemGroupDef[@Name='TS']
let $tsdatasetname := ( 
	if($defineversion = '2.1') then $tsdataset/def21:leaf/@xlink:href
	else $tsdataset/def:leaf/@xlink:href
)
let $tsdatasetdoc := doc(concat($base,$tsdatasetname))
(: get the OID of the TSPARMCD and TSVCDREF :)
let $tsparmcdoid := (
    for $a in $definedoc//odm:ItemDef[@Name='TSPARMCD']/@OID 
    where $a = $definedoc//odm:ItemGroupDef[@Name='TS']/odm:ItemRef/@ItemOID
    return $a
)
let $tsvcdrefoid := (
    for $a in $definedoc//odm:ItemDef[@Name='TSVCDREF']/@OID 
    where $a = $definedoc//odm:ItemGroupDef[@Name='TS']/odm:ItemRef/@ItemOID
    return $a
)
(: iterate over all the records for which TSPARMCD is 'INDIC' :)
for $record in $tsdatasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$tsparmcdoid and @Value='INDIC']]
    let $recnum := $record/@data:ItemGroupDataSeq
    (: get the value of TSVCDREF and of TSPARMCD :)
    let $tsvcdrefvalue := $record/odm:ItemData[@ItemOID=$tsvcdrefoid]/@Value
    let $tsparmcdvalue := $record/odm:ItemData[@ItemOID=$tsparmcdoid]/@Value
    (: TSVCDREF must be 'SNOMED' :)
    where not($tsvcdrefvalue='SNOMED')
    return <error rule="SD2240" dataset="TS" variable="TSVCDREF" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">Invalid TSVCDREF value '{data($tsvcdrefvalue)}' for Trial Summary Parameter TSPARMCD={data($tsparmcdvalue)} in dataset TS</error>				
		]]>
		</rulexquery>
	</sdsrule>
	
<sdsrule id="SD2262" last-update="2020-08-08" originator="FDA" standard="SDTM" webservice="Uses NLM UNII Web Service">
<ruledescription>TSVAL/TSVALCD value must match for TRT</ruledescription>
<ruledetaileddescription>TSVAL and TSVALCD values must be populated from the same name record in FDA Substance Registration System (SRS)</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>TS</domain>
<rulexquery><![CDATA[
(: Rule SD2262: TSVAL/TSVALCD value mismatch for TRT: TSVAL and TSVALCD values must be populated from the same name record in FDA Substance Registration System (SRS) :)
(: This query uses the DailyMed NLM web service - see https://dailymed.nlm.nih.gov/dailymed/webservices-help/v2/uniis_api.cfm
 e.g.: https://dailymed.nlm.nih.gov/dailymed/services/v2/uniis.xml?unii_code=1X0094V6JV :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
declare namespace xs="http://www.w3.org/2001/XMLSchema";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdiscpilot01/' :)
(: let $define := 'define_2_0.xml' :)
let $definedoc := doc(concat($base,$define))
(: Get the TS dataset :)
let $tsitemgroupdef := $definedoc//odm:ItemGroupDef[@Name='TS']
let $tsdatasetname := (
	if($defineversion = '2.1') then $tsitemgroupdef/def21:leaf/@xlink:href
	else $tsitemgroupdef/def:leaf/@xlink:href
)
let $tsdataset := doc(concat($base,$tsdatasetname))
(: get the OID of the TSPARMCD variable :)
let $tsparmcdoid := (
    for $a in $definedoc//odm:ItemDef[@Name='TSPARMCD']/@OID 
    where $a = $definedoc//odm:ItemGroupDef[@Name='TS']/odm:ItemRef/@ItemOID
    return $a
)
(: get the OID of the TSVALCD variable :)
let $tsvalcdoid := (
    for $a in $definedoc//odm:ItemDef[@Name='TSVALCD']/@OID 
    where $a = $definedoc//odm:ItemGroupDef[@Name='TS']/odm:ItemRef/@ItemOID
    return $a
)
(: get the OID of the TSVAL variable :)
let $tsvaloid := (
    for $a in $definedoc//odm:ItemDef[@Name='TSVAL']/@OID 
    where $a = $definedoc//odm:ItemGroupDef[@Name='TS']/odm:ItemRef/@ItemOID
    return $a
)
(: now get the records TSPARMCD=TRT, but only when there also IS a TSVALCD AND a TSVAL variable value present :)
for $record in $tsdataset//odm:ItemGroupData[odm:ItemData[@ItemOID=$tsparmcdoid and @Value='TRT']  and odm:ItemData[@ItemOID=$tsvaloid] and odm:ItemData[@ItemOID=$tsvalcdoid]]
    let $recnum := $record/@data:ItemGroupDataSeq
    (: get the ItemData point for TSVAL :)
    let $tsvalcd := $record/odm:ItemData[@ItemOID=$tsvalcdoid]/@Value
    let $tsval := $record/odm:ItemData[@ItemOID=$tsvaloid]/@Value
    (: return <test>{data($tsval)}</test> :)
    (: now invoke the web service to check whether it is a valid UNII, e.g.:
    https://dailymed.nlm.nih.gov/dailymed/services/v2/uniis.xml?unii_code=1X0094V6JV
    :)
    (: ONLY FOR TESTING :)
    (: let $tsvalcd := '1X0094V6JV' 
    let $tsval := 'NAFARELINx' :)
    (: call the web service :)
    let $webservice := concat('https://dailymed.nlm.nih.gov/dailymed/services/v2/uniis.xml?unii_code=',$tsvalcd)
    let $webserviceresult := doc($webservice) 
    (: this is a document with the structure /uniis/unii/unii_code and /uniis/unii/active_moiety - it these must match TSVALCD and TSVAL :)
    let $srscode := $webserviceresult/uniis/unii/unii_code
    let $srsname := $webserviceresult/uniis/unii/active_moiety
    where not($tsvalcd = $srscode and $tsval = $srsname)
    return <error rule="SD2262" dataset="TS" variable="TSVALCD" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">TSVAL/TSVALCD value mismatch for TRT in dataset {data($tsdatasetname)}: TSVAL and TSVALCD values must be populated from the same name record in FDA Substance Registration System (SRS): values '{data($tsvalcd)}' and '{data($tsval)}' do not belong to the same SRS record</error>	
]]></rulexquery>
</sdsrule>


<sdsrule id="SD1227" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>Only one NARMS record is allowed in TS</ruledescription>
<ruledetaileddescription>Trial Summary (TS) domain must contain only one record for 'NARMS' Parameter</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>TS</domain>
<rulexquery><![CDATA[
(: Rule SD1227 - Multiple NARMS records:
Trial Summary (TS) domain must contain only one record for 'NARMS' Parameter
:)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define)) 
(: get the TS dataset :)
let $tsdataset := $definedoc//odm:ItemGroupDef[@Name='TS']
let $tsdatasetname := (
	if($defineversion = '2.1') then $tsdataset/def21:leaf/@xlink:href
	else $tsdataset/def:leaf/@xlink:href
)
let $tsdatasetlocation := concat($base,$tsdatasetname)
(: get the OID of the TSPARMCD and TSVAL :)
let $tsparmcdoid := (
    for $a in $definedoc//odm:ItemDef[@Name='TSPARMCD']/@OID 
    where $a = $definedoc//odm:ItemGroupDef[@Name='TS']/odm:ItemRef/@ItemOID
    return $a
)
let $tsvaloid := (
    for $a in $definedoc//odm:ItemDef[@Name='TSVAL']/@OID 
    where $a = $definedoc//odm:ItemGroupDef[@Name='TS']/odm:ItemRef/@ItemOID
    return $a
)
(: count the number of the records with TSPARMCD=NARMS :)
let $count := count(doc($tsdatasetlocation)//odm:ItemGroupData[odm:ItemData[@ItemOID=$tsparmcdoid and @Value='NARMS']])
(: the number of NARMS records may not be more than 1 :)
where $count > 1
return <error rule="SD1227" dataset="TS" rulelastupdate="2020-08-08">Multiple NARMS records in dataset {data($tsdatasetname)}, number of NARMS records found={data($count)}</error>
]]></rulexquery>
</sdsrule>

<!-- SExxxx rules are not included here as they are on SEND, and this file is specifically for SDTM -->

<!-- Rule SD0026: "" is not implemented as it is "Bullshit" and cannot be implemented without the LOINC code -->



<sdsrule id="SD0044" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>--VAMTU must be populated, when --VAMT is populated</ruledescription>
<ruledetaileddescription>Treatment Vehicle Amount Units (--VAMTU) should not be NULL,  when Treatment Vehicle Amount (--VAMT) is populated</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>INTERVENTIONS</domain>
<rulexquery><![CDATA[	
(: TODO: needs further testing, did not have a good test dataset :)
(: Rule SD0044 - Missing value for --VAMTU, when --VAMT is populated:
Treatment Vehicle Amount Units (--VAMTU) should not be NULL,  when Treatment Vehicle Amount (--VAMT) is populated
INTERVENTIONS
:)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: iterate over all INTERVENTION domains :)
for $dataset in $definedoc//odm:ItemGroupDef[upper-case(@def:Class)='INTERVENTIONS' or upper-case(./def21:Class/@Name)='INTERVENTIONS']
    let $name := $dataset/@Name
    let $domain := $dataset/@Domain
    let $datasetname := ( 
		if($defineversion = '2.1') then $dataset/def21:leaf/@xlink:href
		else $dataset/def:leaf/@xlink:href
	)
    let $datasetlocation := concat($base,$datasetname)
    (: get the OID of VAMTU and VAMT variables :)
    let $vamtuoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'VAMTU')]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
    )
    let $vamtuname := $definedoc//odm:ItemDef[@OID=$vamtuoid]/@Name
    let $vamtoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'VAMT')]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
    )
    let $vamtname := $definedoc//odm:ItemDef[@OID=$vamtoid]/@Name
    (: iterate over all records for which VAMT is populated :)
    for $record in doc($datasetlocation)//odm:ItemGroupData[odm:ItemData[@ItemOID=$vamtoid]]
        let $recnum := $record/@data:ItemGroupDataSeq
        (: and get the value of the VAMT variable :)
        let $vamtvalue := $record/odm:ItemData[@ItemOID=$vamtoid]/@Value
        (: and check whether VAMTU is populated :)
        let $vamtuvalue := $record/odm:ItemData[@ItemOID=$vamtuoid]/@Value
        where not($vamtuvalue)
        return <warning rule="SD0044" dataset="{data($name)}" variable="{data($vamtuname)}" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">Missing value for {data($vamtuname)}, when {data($vamtname)} is populated</warning>	
]]></rulexquery>
</sdsrule>



<sdsrule id="SD0006" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>Every subject must have a baseline result</ruledescription>
<ruledetaileddescription>All subjects should have at least one baseline observation (--BLFL = 'Y') in EG, LB, MB, MS, PC and VS domains, except for subjects who failed screening (ARMCD = 'SCRNFAIL') or were not fully assigned to an Arm (ARMCD = 'NOTASSGN') or were not treated (ACTARMCD = 'NOTTRT')</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>EG</domain>
<domain>LB</domain>
<domain>MB</domain>
<domain>MS</domain>
<domain>PC</domain>
<domain>VS</domain>
<rulexquery><![CDATA[	
(: TODO: test on large submission with more splitted datasets :)
(: Rule SD0006 - No baseline result in Domain for subject
All subjects should have at least one baseline observation (--BLFL = 'Y') in EG, LB, MB, MS, PC and VS domains, except for subjects who failed screening (ARMCD = 'SCRNFAIL') or were not fully assigned to an Arm (ARMCD = 'NOTASSGN') or were not treated (ACTARMCD = 'NOTTRT') in datasets EG, LB, MB, MS, PC, VS
:)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define)) 
(: find all subjects in DM for which ARMCD is NOT SCRNFAIL, NOT NOTASSGN and NOT NOTTRT :)
(: we need the OID of ARMCD :)
let $armcdoid := (
    for $a in $definedoc//odm:ItemDef[@Name='ARMCD']/@OID 
    where $a = $definedoc//odm:ItemGroupDef[@Name='DM']/odm:ItemRef/@ItemOID
    return $a
)
(:  and the OID of the USUBJID variable :)
let $dmusubjidoid := (
    for $a in $definedoc//odm:ItemDef[@Name='USUBJID']/@OID 
    where $a = $definedoc//odm:ItemGroupDef[@Name='DM']/odm:ItemRef/@ItemOID
    return $a
)
(: and the location of the DM dataset :)
let $dmdatasetname := (
	if($defineversion = '2.1') then $definedoc//odm:ItemGroupDef[@Name='DM']/def21:leaf/@xlink:href
	else $definedoc//odm:ItemGroupDef[@Name='DM']/def:leaf/@xlink:href
)
let $dmdatasetlocation := concat($base,$dmdatasetname)
(: find all subjects in DM for which ARMCD is NOT SCRNFAIL, NOT NOTASSGN and NOT NOTTRT :)
let $randomizedsubjects := doc($dmdatasetlocation)//odm:ItemGroupData[odm:ItemData[@ItemOID=$armcdoid][not(@Value='SCRNFAIL') and not(@Value='NOTASSGN') and not(@Value=NOTTRT)]]/odm:ItemData[@ItemOID=$dmusubjidoid]/@Value
(: iterate over all applicable DOMAINS, taking splitted datasets into account :)
let $domains := ('EG','LB','MB','MS','PC','VS')
(: there can be more than 1 dataset per domain :)
for $domain in $domains
    let $datasets := $definedoc//odm:ItemGroupDef[@Domain=$domain or starts-with(@Name,$domain)] (: provides a list of nodes :)
    let $datasetslocationsindomain := (
		if($defineversion = '2.1') then (concat($base,$definedoc//odm:ItemGroupDef[@Domain=$domain]/def21:leaf/@xlink:href))
		else (concat($base,$definedoc//odm:ItemGroupDef[@Domain=$domain]/def:leaf/@xlink:href))
	)
    (: get the name of the BLFL variable :)
    let $blflname := concat($domain,'BLFL') 
    (: and the OID - should be the same for each of the datasets within the single domain :)
    let $blfloid := $definedoc//odm:ItemDef[@Name=$blflname]/@OID
    let $usubjidoid := (
        for $a in $definedoc//odm:ItemDef[@Name='USUBJID']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Domain=$domain][1]/odm:ItemRef/@ItemOID
        return $a
    )
    (: return <test>{data($domain)} - {data($blfloid)}</test> :)
    (: iterate over all the randomized subjects and count the number of baseline flag records 
    over all the datasets within that domain :)
    for $subject in $randomizedsubjects
        let $count := count( 
            for $dataset in $datasetslocationsindomain
                let $c := doc($dataset)//odm:ItemGroupData[odm:ItemData[@ItemOID=$usubjidoid][@Value=$subject] and odm:ItemData[@ItemOID=$blfloid]]/@data:ItemGroupDataSeq
                return $c
        )  
        (: $count gives the number of records with a baseline flag for the subject :)
        where count($datasets) > 0 and $count = 0   (: no baseline record for this subject found in any of the datasets in this domain :)
        return <warning rule="SD0006" dataset="{data($domain)}" variable="{data($blflname)}" rulelastupdate="2020-08-08">No baseline result in Domain {data($domain)} for subject {data($subject)}</warning>
]]></rulexquery>
</sdsrule>

<!-- Remark: first grouping the data by USUBJID does NOT increase speed (78 seconds for LB with grouping against 67 seconds without grouping -->
<sdsrule id="SD0006-SD" last-update="2020-08-08" originator="FDA" standard="SDTM" requiresDomainOrDataset="Yes">
<ruledescription>Every subject must have a baseline result</ruledescription>
<ruledetaileddescription>All subjects should have at least one baseline observation (--BLFL = 'Y') in EG, LB, MB, MS, PC and VS domains, except for subjects who failed screening (ARMCD = 'SCRNFAIL') or were not fully assigned to an Arm (ARMCD = 'NOTASSGN') or were not treated (ACTARMCD = 'NOTTRT')</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>EG</domain>
<domain>LB</domain>
<domain>MB</domain>
<domain>MS</domain>
<domain>PC</domain>
<domain>VS</domain>
<rulexquery><![CDATA[	
(: Rule SD0006 - No baseline result in Domain for subject
All subjects should have at least one baseline observation (--BLFL = 'Y') in EG, LB, MB, MS, PC and VS domains, except for subjects who failed screening (ARMCD = 'SCRNFAIL') or were not fully assigned to an Arm (ARMCD = 'NOTASSGN') or were not treated (ACTARMCD = 'NOTTRT') in datasets EG, LB, MB, MS, PC, VS
:)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
declare variable $datasetname external; 
(: let $base := '/db/fda_submissions/cdisc01/' 
let $define := 'define2-0-0-example-sdtm.xml' 
let $datasetname := 'LB' :)
let $definedoc := doc(concat($base,$define))
(: find all subjects in DM for which ARMCD is NOT SCRNFAIL, NOT NOTASSGN and NOT NOTTRT :)
(: we need the OID of ARMCD :)
let $armcdoid := (
    for $a in $definedoc//odm:ItemDef[@Name='ARMCD']/@OID 
    where $a = $definedoc//odm:ItemGroupDef[@Name='DM']/odm:ItemRef/@ItemOID
    return $a
)
(:  and the OID of the USUBJID variable :)
let $dmusubjidoid := (
    for $a in $definedoc//odm:ItemDef[@Name='USUBJID']/@OID 
    where $a = $definedoc//odm:ItemGroupDef[@Name='DM']/odm:ItemRef/@ItemOID
    return $a
)
(: and the location of the DM dataset :)
let $dmdatasetname := (
	if($defineversion = '2.1') then $definedoc//odm:ItemGroupDef[@Name='DM']/def21:leaf/@xlink:href
	else $definedoc//odm:ItemGroupDef[@Name='DM']/def:leaf/@xlink:href
)
let $dmdatasetlocation := concat($base,$dmdatasetname)
(: generate a list of all subjects in DM for which ARMCD is NOT SCRNFAIL, NOT NOTASSGN and NOT NOTTRT :)
let $randomizedsubjects := doc($dmdatasetlocation)//odm:ItemGroupData[odm:ItemData[@ItemOID=$armcdoid][not(@Value='SCRNFAIL') and not(@Value='NOTASSGN') and not(@Value=NOTTRT)]]/odm:ItemData[@ItemOID=$dmusubjidoid]/@Value
(: iterate over all applicable DOMAINS, taking splitted datasets into account :)
(: let $domains := ('EG','LB','MB','MS','PC','VS') :)
(: apply to the provided dataset but only if in one of the six applicable domains :)
for $dataset in $definedoc//odm:ItemGroupDef[@Name=$datasetname][@Domain='EG' or @Domain='LB' or @Domain='MB' or @Domain='MS' or @Domain='PC' or @Domain='VS'] (: provides a list of nodes :)
    let $datasetlocation := (
		if($defineversion = '2.1') then $dataset/def21:leaf/@xlink:href 
		else $dataset/def:leaf/@xlink:href
	)
    (: get the name of the BLFL variable :)
    let $domain := $dataset/@Domain
    let $blflname := concat($domain,'BLFL') 
    (: and the OID of --BLFL and USUBJID - should be the same for each of the datasets within the single domain :)
    let $blfloid := $definedoc//odm:ItemDef[@Name=$blflname]/@OID
    let $usubjidoid := (
        for $a in $definedoc//odm:ItemDef[@Name='USUBJID']/@OID 
        where $a = $dataset/odm:ItemRef/@ItemOID
        return $a
    )
    (: return <test>{data($domain)} - {data($blfloid)}</test> :)
    (: iterate over all the randomized subjects and count the number of baseline flag records 
    over all the datasets within that domain :)
    for $subject in $randomizedsubjects (: $randomizedsubjects is a list of randomized subjects :)
        (: $count gives the number of records with a baseline flag for the subject :)
        let $count := count(doc(concat($base,$datasetlocation))//odm:ItemGroupData[odm:ItemData[@ItemOID=$usubjidoid and @Value=$subject] and odm:ItemData[@ItemOID=$blfloid and @Value='Y']])
        where $count = 0   (: no baseline record for this subject found in any of the datasets in this domain :)
        return <warning rule="SD0006" dataset="{data($datasetname)}" variable="{data($blflname)}" rulelastupdate="2020-08-08">No baseline result in Dataset {data($domain)} for subject {data($subject)} </warning>	
]]></rulexquery>
</sdsrule>

<sdsrule id="SD1044" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>--BLFL variable must be present in every custom Findings domain</ruledescription>
<ruledetaileddescription>Baseline Flag (--BLFL) should be present in all custom Findings domains</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>FINDINGS</domain>
<rulexquery><![CDATA[	
(: No --BLFL variable in custom Findings domain :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' 
let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: In this implementation, we trust that a custom domain name starts with X, Y or Z
 as define.xml 2.0 does not have a notion of "custom domain" yet - this comes first with define.xml 2.1 :)
for $datasetdef in $definedoc//odm:ItemGroupDef[starts-with(@Name,'X') or starts-with(@Name,'Y') or starts-with(@Name,'Z')][upper-case(@def:Class) = 'FINDINGS' or upper-case(./def21:Class/@Name) = 'FINDINGS']
    let $name := $datasetdef/@Name
    let $blflname := concat($name,'BLFL')
    (: get the definition of --BLFL (if any) :)
    let $blfloid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'BLFL')]/@OID 
        where $a = $datasetdef/odm:ItemRef/@ItemOID
        return $a
    )
    where not($blfloid)
    return <warning rule="SD1044" dataset="{data($name)}" variable="{data($blflname)}" rulelastupdate="2020-08-08">No baseline variable {data($blflname)} in custom Findings dataset {data(name)}</warning>	 
]]>
</rulexquery>
</sdsrule>

<sdsrule id="SD1035" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>DSCAT must be populated</ruledescription>
<ruledetaileddescription>Category for Disposition Event (DSCAT) should be populated</ruledetaileddescription>
<domain>DS</domain>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<rulexquery><![CDATA[	
(: Rule FDAC203 - Missing value for DSCAT:
Category for Disposition Event (DSCAT) should be populated
:)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: Get the DS dataset :)
let $dsdataset := $definedoc//odm:ItemGroupDef[@Name='DS']
let $dsdatasetname := (
	if($defineversion = '2.1') then $dsdataset/def21:leaf/@xlink:href
	else $dsdataset/def:leaf/@xlink:href
)
let $dsdatasetlocation := concat($base,$dsdatasetname)
(: and get the OID of the DSCAT variable :)
let $dscatoid := (
    for $a in $definedoc//odm:ItemDef[@Name='DSCAT']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name='DS']/odm:ItemRef/@ItemOID
        return $a
)
(: iterate over all records in the DS dataset :)
for $record in doc($dsdatasetlocation)//odm:ItemGroupData
    let $recnum := $record/@data:ItemGroupDataSeq
    (: and check whether DSCAT is populated :)
    let $dscatvalue := $record/odm:ItemData[@ItemOID=$dscatoid]/@Value
    where not($dscatvalue)
    return <warning rule="SD1035" dataset="DS" variable="DSCAT" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">Missing value for DSCAT</warning>	
]]></rulexquery>
</sdsrule>

<sdsrule id="SD1021-SD" last-update="2020-08-08" originator="FDA" standard="SDTM" requiresDomainOrDataset="Yes">
<ruledescription>Character values may not have leading spaces</ruledescription>
<ruledetaileddescription>Character values should not have leading space ' ' characters, or '.' as an entire value. The only exceptions are COVALn and TSVALn variables</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>ALL</domain>
<rulexquery><![CDATA[	
(: Rule SD SD1021: Character values should not have leading spaces or only have a period character :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
declare namespace functx = "http://www.functx.com";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
declare variable $datasetname external;
(: function to find out whether a value is in a sequence (array) :)
declare function functx:is-value-in-sequence
  ( $value as xs:anyAtomicType? ,
    $seq as xs:anyAtomicType* )  as xs:boolean {
   $value = $seq
} ;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
(: let $datasetname := 'LB' :)
let $definedoc := doc(concat($base,$define))
(: get the dataset location :)
let $datasetdef := $definedoc//odm:ItemGroupDef[@Name=$datasetname]
let $datasetlocation := (
	if($defineversion = '2.1') then $datasetdef/def21:leaf/@xlink:href
	else $datasetdef/def:leaf/@xlink:href
)
let $datasetdoc := (
	if($datasetlocation) then doc(concat($base,$datasetlocation))
	else ()
)
(: get the OIDs of the variables that are of type 'char', 
 : which are all the ones that are not "integer" or "float" :)
let $charvaroids := (
    for $a in $definedoc//odm:ItemDef[not(@DataType='float' or @DataType='integer')]/@OID 
        where $a = $datasetdef/odm:ItemRef/@ItemOID
        return $a
	)
(:  testing only: return <test>{data($charvaroids)}</test>  :)
(: iterate over all data points that are not of type 'integer' or 'float' :)
for $record in $datasetdoc//odm:ItemGroupData
    (: get the record number :)
    let $recnum := $record/@data:ItemGroupDataSeq
    for $datapoint in $record/odm:ItemData[functx:is-value-in-sequence(@ItemOID,$charvaroids)] 
        (: get the name from the OID :)
        let $varname := $definedoc//odm:ItemDef[@OID=$datapoint/@ItemOID]/@Name
        (: get the value :)
        let $value := $datapoint/@Value
        (: when the first character is a space, or the whole value is '.' give an warning :)
        where $value = '.' or starts-with($value,' ')
        return <warning rule="SD1021" dataset="{data($datasetname)}" variable="{data($varname)}" recordnumber="{data($recnum)}" rulelastupdate="2020-08-08">Unexpected character value in non-numeric variable value {data($varname)}. Value '{data($value)}' was found</warning>	 
]]>
</rulexquery>
</sdsrule>

<sdsrule id="SD1083" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>--DY variable must be present, when --DTC variable is present</ruledescription>
<ruledetaileddescription>Collection Study Day (--DY) variable should be included into dataset, when Collection Study Date/Time (--DTC) variable is present</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>ALL</domain>
<rulexquery><![CDATA[	
(: Rule SD1083: Missing --DY variable, when --DTC variable is present:
Collection Study Day (--DY) variable should be included into dataset, when Collection Study Date/Time (--DTC) variable is present
:)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external;  
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: iterate over all the datasets :)
for $dataset in $definedoc//odm:ItemGroupDef
    let $name := $dataset/@Name
    let $datasetname := (
		if($defineversion = '2.1') then $dataset/def21:leaf/@xlink:href
		else $dataset/def:leaf/@xlink:href
	)
    let $datasetlocation := concat($base,$datasetname)
    (: Get the OIDs of the --DY and --DTC variables  :)
    let $dyoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'DY') and string-length(@Name)=4]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
    )
    let $dyname := $definedoc//odm:ItemDef[@OID=$dyoid]/@Name
    let $dtcoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'DTC') and string-length(@Name)=5]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
    )
    let $dtcname := $definedoc//odm:ItemDef[@OID=$dtcoid]/@Name
    (: this rule is about the presence in the dataset - not about whether the records are populated :)
    where not($dyoid) and $dtcoid
    return <warning rule="SD1083" rulelastupdate="2020-08-08">Collection Study Day {data(concat($name,'DY'))} variable must be included into dataset, when Collection Study Date/Time {data($dtcname)} variable is present in dataset {data($datasetname)}</warning>
]]></rulexquery>
</sdsrule>




<sdsrule id="SD1114C" last-update="2020-08-08" originator="FDA" requiresMedDRA="Yes" standard="SDTM">
<ruledescription>Value for --DECOD must be in correct case</ruledescription>
<ruledetaileddescription>Case for the Dictionary-Derived Term (--DECOD) variable must be sentence case using a Preferred Term of the MedDRA dictionary of a version specified in the define.xml (case-sensitive)</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>AE</domain>
<domain>MH</domain>
<domain>CE</domain>
<rulexquery><![CDATA[
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
declare namespace xs="http://www.w3.org/2001/XMLSchema";
declare namespace functx = "http://www.functx.com";
(: function to find out whether a value is in a sequence (array) :)
declare function functx:is-value-in-sequence
  ( $value as xs:anyAtomicType? ,
    $seq as xs:anyAtomicType* )  as xs:boolean {
   $value = $seq
} ;
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
declare variable $meddrabase external; 
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
(: let $meddrabase := 'file:///e:/Validation_Rules_XQuery/meddra_17_1_english/MedAscii/' :)
let $meddrafile := 'pt.asc' 
let $meddralocation := concat($meddrabase,$meddrafile)
(: read the meddra file line by line :)
let $lines := unparsed-text-lines($meddralocation) 
(: get the preferred term, which is the second fied - field separator is the $ character :)
let $ptterms := (
	for $line in $lines
	return tokenize($line,'\$')[2]
)  
(: $ptterms know contains all the MedDRA PT terms in ORIGINAL CASE :)
(: we also keep a list of the uppercase ones :)
let $pttermsuppercase := (
	for $ptterm in $ptterms
	return upper-case($ptterm)
) 
(: iterate over all AE, MH and CE dataset definitions :)
let $definedoc := doc(concat($base,$define))
for $itemgroup in $definedoc//odm:ItemGroupDef[@Name='AE' or @Name='MH' or @Name='CE' or @Domain='AE' or @Domain='MH' or @Domain='CE']
    let $name := $itemgroup/@Name
    (: get the OID of the --DECOD variable :)
    let $decodoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'DECOD')]/@OID 
        where $a = $itemgroup/odm:ItemRef/@ItemOID
        return $a
    )
    let $decodname := $definedoc//odm:ItemDef[@OID=$decodoid]/@Name
    (: get the dataset location :)
    let $datasetlocation := (
		if($defineversion = '2.1') then $itemgroup/def21:leaf/@xlink:href
		else $itemgroup/def:leaf/@xlink:href
	)
    let $datasetdoc := doc(concat($base,$datasetlocation))
    (: iterate over all the records in the dataset :)
    for $record in $datasetdoc//odm:ItemGroupData
        let $recnum := $record/@data:ItemGroupDataSeq
        (: get the value of --DECOD :)
        let $decodvalue := $record/odm:ItemData[@ItemOID=$decodoid]/@Value
        (: give an error when the DECOD value is one of the PT terms of MedDRA (case-insensitive), 
        BUT not in the list of the PT terms in original case :)
        where $decodvalue and functx:is-value-in-sequence(upper-case($decodvalue),$pttermsuppercase) and not(functx:is-value-in-sequence($decodvalue,$ptterms))
        return <error rule="SD1114C" dataset="{data($name)}" variable="{data($decodname)}" recordnumber="{data($recnum)}" rulelastupdate="2020-08-08">Value '{data($decodvalue)}' for {data($decodname)} is in incorrect case from the MedDRA dictionary</error>
]]></rulexquery>
</sdsrule>

<sdsrule id="SD2008C" last-update="2020-08-08" originator="FDA" requiresMedDRA="Yes" standard="SDTM">
<ruledescription>Value for --LLT must be in correct case</ruledescription>
<ruledetaileddescription>Case for the Dictionary-Derived Term (--DECOD) variable must be sentence case using a Preferred Term of the MedDRA dictionary of a version specified in the define.xml (sase-sensitive)</ruledetaileddescription>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>AE</domain>
<domain>MH</domain>
<domain>CE</domain>
<rulexquery><![CDATA[
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
declare namespace xs="http://www.w3.org/2001/XMLSchema";
declare namespace functx = "http://www.functx.com";
(: function to find out whether a value is in a sequence (array) :)
declare function functx:is-value-in-sequence
  ( $value as xs:anyAtomicType? ,
    $seq as xs:anyAtomicType* )  as xs:boolean {
   $value = $seq
} ;
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
declare variable $meddrabase external; 
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
(: let $meddrabase := 'file:///e:/Validation_Rules_XQuery/meddra_17_1_english/MedAscii/' :)
let $meddrafile := 'llt.asc' 
let $meddralocation := concat($meddrabase,$meddrafile)
(: read the meddra file line by line :)
let $lines := unparsed-text-lines($meddralocation) 
(: get the LLT terms, which is the second fied - field separator is the $ character :)
let $lltterms := (
	for $line in $lines
	return tokenize($line,'\$')[2]
)  
(: $lltterms know contains all the MedDRA LLT terms in ORIGINAL CASE :)
(: we also keep a list of the uppercase ones :)
let $llttermsuppercase := (
	for $lltterm in $lltterms
	return upper-case($lltterm)
) 
(: iterate over all AE, MH and CE dataset definitions :)
let $definedoc := doc(concat($base,$define))
for $itemgroup in $definedoc//odm:ItemGroupDef[@Name='AE' or @Name='MH' or @Name='CE' or @Domain='AE' or @Domain='MH' or @Domain='CE']
    let $name := $itemgroup/@Name
    (: get the OID of the --LLT variable :)
    let $lltoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'LLT') and string-length(@Name)=5]/@OID 
        where $a = $itemgroup/odm:ItemRef/@ItemOID
        return $a
    )
    let $lltname := $definedoc//odm:ItemDef[@OID=$lltoid]/@Name
    (: get the dataset location :)
	let $datasetlocation := (
		if($defineversion = '2.1') then $itemgroup/def21:leaf/@xlink:href
		else $itemgroup/def:leaf/@xlink:href
	)
    let $datasetdoc := doc(concat($base,$datasetlocation))
    (: iterate over all the records in the dataset :)
    for $record in $datasetdoc//odm:ItemGroupData
        let $recnum := $record/@data:ItemGroupDataSeq
        (: get the value of --LLT :)
        let $lltvalue := $record/odm:ItemData[@ItemOID=$lltoid]/@Value
        (: give an error when the LLT value is one of the PT terms of MedDRA (case-insensitive), 
        but not in the list of the LLT terms in original case :)
        where $lltvalue and functx:is-value-in-sequence(upper-case($lltvalue),$llttermsuppercase) and not(functx:is-value-in-sequence($lltvalue,$lltterms))
        return <error rule="SD2008C" dataset="{data($name)}" variable="{data($lltname)}" recordnumber="{data($recnum)}" rulelastupdate="2020-08-08">Value '{data($lltvalue)}' for {data($lltname)} is in incorrect case from the MedDRA dictionary</error>		
]]></rulexquery>
</sdsrule>

<sdsrule id="SD2010C" last-update="2020-08-08" originator="FDA" requiresMedDRA="Yes" standard="SDTM">
<ruledescription>Value for --HLT must be in correct case</ruledescription>
<ruledetaileddescription>Case for High Level Term (--HLT) variable must be sentence case using a High Level Term of the MedDRA dictionary of a version specified in the define.xml (case-sensitive)</ruledetaileddescription>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>AE</domain>
<domain>MH</domain>
<domain>CE</domain>
<rulexquery><![CDATA[
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
declare namespace xs="http://www.w3.org/2001/XMLSchema";
declare namespace functx = "http://www.functx.com";
(: function to find out whether a value is in a sequence (array) :)
declare function functx:is-value-in-sequence
  ( $value as xs:anyAtomicType? ,
    $seq as xs:anyAtomicType* )  as xs:boolean {
   $value = $seq
} ;
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
declare variable $meddrabase external; 
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
(: let $meddrabase := 'file:///e:/Validation_Rules_XQuery/meddra_17_1_english/MedAscii/' :)
let $meddrafile := 'hlt.asc' 
let $meddralocation := concat($meddrabase,$meddrafile)
(: read the meddra file line by line :)
let $lines := unparsed-text-lines($meddralocation) 
(: get the HLT terms, which is the second fied - field separator is the $ character :)
let $hltterms := (
	for $line in $lines
	return tokenize($line,'\$')[2]
)  
(: $hltterms know contains all the MedDRA HLT terms in ORIGINAL CASE :)
(: we also keep a list of the uppercase ones :)
let $hlttermsuppercase := (
	for $hltterm in $hltterms
	return upper-case($hltterm)
) 
(: iterate over all AE, MH and CE dataset definitions :)
let $definedoc := doc(concat($base,$define))
for $itemgroup in $definedoc//odm:ItemGroupDef[@Name='AE' or @Name='MH' or @Name='CE' or @Domain='AE' or @Domain='MH' or @Domain='CE']
    let $name := $itemgroup/@Name
    (: get the OID of the --HLT variable :)
    let $hltoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'HLT') and string-length(@Name)=5]/@OID 
        where $a = $itemgroup/odm:ItemRef/@ItemOID
        return $a
    )
    let $hltname := $definedoc//odm:ItemDef[@OID=$hltoid]/@Name
    (: get the dataset location :)
	let $datasetlocation := (
		if($defineversion = '2.1') then $itemgroup/def21:leaf/@xlink:href
		else $itemgroup/def:leaf/@xlink:href
	)
    let $datasetdoc := doc(concat($base,$datasetlocation))
    (: iterate over all the records in the dataset :)
    for $record in $datasetdoc//odm:ItemGroupData
        let $recnum := $record/@data:ItemGroupDataSeq
        (: get the value of --HLT :)
        let $hltvalue := $record/odm:ItemData[@ItemOID=$hltoid]/@Value
        (: give an error when the HLT value is one of the PT terms of MedDRA (case-insensitive), 
        but not in the list of the HLT terms in original case :)
        where $hltvalue and functx:is-value-in-sequence(upper-case($hltvalue),$hlttermsuppercase) and not(functx:is-value-in-sequence($hltvalue,$hltterms))
        return <error rule="SD2010C" dataset="{data($name)}" variable="{data($hltname)}" recordnumber="{data($recnum)}" rulelastupdate="2020-08-08">Value '{data($hltvalue)}' for {data($hltname)} is in incorrect case from the MedDRA dictionary</error>			
]]></rulexquery>
</sdsrule>

<sdsrule id="SD2012C" last-update="2020-08-08" originator="FDA" requiresMedDRA="Yes" standard="SDTM">
<ruledescription>Value for --HLGT must be in correct case</ruledescription>
<ruledetaileddescription>Case for High Level Group Term (--HLGT) variable must be sentence case using a High Level Group Term of the MedDRA dictionary of a version specified in the define.xml (case-sensitive)</ruledetaileddescription>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>AE</domain>
<domain>MH</domain>
<domain>CE</domain>
<rulexquery><![CDATA[
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
declare namespace xs="http://www.w3.org/2001/XMLSchema";
declare namespace functx = "http://www.functx.com";
(: function to find out whether a value is in a sequence (array) :)
declare function functx:is-value-in-sequence
  ( $value as xs:anyAtomicType? ,
    $seq as xs:anyAtomicType* )  as xs:boolean {
   $value = $seq
} ;
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
declare variable $meddrabase external; 
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
(: let $meddrabase := 'file:///e:/Validation_Rules_XQuery/meddra_17_1_english/MedAscii/' :)
let $meddrafile := 'hlgt.asc' 
let $meddralocation := concat($meddrabase,$meddrafile)
(: read the meddra file line by line :)
let $lines := unparsed-text-lines($meddralocation) 
(: get the HLGT terms, which is the second fied - field separator is the $ character :)
let $hlgtterms := (
	for $line in $lines
	return tokenize($line,'\$')[2]
)  
(: $hltgterms know contains all the MedDRA HLT terms in ORIGINAL CASE :)
(: we also keep a list of the uppercase ones :)
let $hlgttermsuppercase := (
	for $hlgtterm in $hlgtterms
	return upper-case($hlgtterm)
) 
(: iterate over all AE, MH and CE dataset definitions :)
let $definedoc := doc(concat($base,$define))
for $itemgroup in $definedoc//odm:ItemGroupDef[@Name='AE' or @Name='MH' or @Name='CE' or @Domain='AE' or @Domain='MH' or @Domain='CE']
    let $name := $itemgroup/@Name
    (: get the OID of the --HLGT variable :)
    let $hlgtoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'HLGT') and string-length(@Name)=6]/@OID 
        where $a = $itemgroup/odm:ItemRef/@ItemOID
        return $a
    )
    let $hlgtname := $definedoc//odm:ItemDef[@OID=$hlgtoid]/@Name
    (: get the dataset location :)
	let $datasetlocation := (
		if($defineversion = '2.1') then $itemgroup/def21:leaf/@xlink:href
		else $itemgroup/def:leaf/@xlink:href
	)
    let $datasetdoc := doc(concat($base,$datasetlocation))
    (: iterate over all the records in the dataset :)
    for $record in $datasetdoc//odm:ItemGroupData
        let $recnum := $record/@data:ItemGroupDataSeq
        (: get the value of --HLGT :)
        let $hlgtvalue := $record/odm:ItemData[@ItemOID=$hlgtoid]/@Value
        (: give an error when the HLGT value is one of the HLGT terms of MedDRA (case-insensitive), 
        but not in the list of the HLGT terms in original case :)
        where $hlgtvalue and functx:is-value-in-sequence(upper-case($hlgtvalue),$hlgttermsuppercase) and not(functx:is-value-in-sequence($hlgtvalue,$hlgtterms))
        return <error rule="SD2012C" dataset="{data($name)}" variable="{data($hlgtname)}" recordnumber="{data($recnum)}" rulelastupdate="2020-08-08">Value '{data($hlgtvalue)}' for {data($hlgtname)} is in incorrect case from the MedDRA dictionary</error>		
]]></rulexquery>
</sdsrule>

<sdsrule id="SD2015C" last-update="2020-08-08" originator="FDA" requiresMedDRA="Yes" standard="SDTM">
<ruledescription>Value for --SOC must be in correct case</ruledescription>
<ruledetaileddescription>- Case for System Organ Class (--SOC) variable must be sentence case using a System Organ Class of the MedDRA dictionary of a version specified in the define.xml (case-sensitive)</ruledetaileddescription>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>AE</domain>
<domain>MH</domain>
<domain>CE</domain>
<rulexquery><![CDATA[
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
declare namespace xs="http://www.w3.org/2001/XMLSchema";
declare namespace functx = "http://www.functx.com";
(: function to find out whether a value is in a sequence (array) :)
declare function functx:is-value-in-sequence
  ( $value as xs:anyAtomicType? ,
    $seq as xs:anyAtomicType* )  as xs:boolean {
   $value = $seq
} ;
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
declare variable $meddrabase external; 
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
(: let $meddrabase := 'file:///e:/Validation_Rules_XQuery/meddra_17_1_english/MedAscii/' :)
let $meddrafile := 'soc.asc' 
let $meddralocation := concat($meddrabase,$meddrafile)
(: read the meddra file line by line :)
let $lines := unparsed-text-lines($meddralocation) 
(: get the SOC terms, which is the second fied - field separator is the $ character :)
let $socterms := (
	for $line in $lines
	return tokenize($line,'\$')[2]
)  
(: $socterms know contains all the MedDRA HLT terms in ORIGINAL CASE :)
(: we also keep a list of the uppercase ones :)
let $soctermsuppercase := (
	for $socterm in $socterms
	return upper-case($socterm)
) 
(: iterate over all AE, MH and CE dataset definitions :)
let $definedoc := doc(concat($base,$define))
for $itemgroup in $definedoc//odm:ItemGroupDef[@Name='AE' or @Name='MH' or @Name='CE' or @Domain='AE' or @Domain='MH' or @Domain='CE']
    let $name := $itemgroup/@Name
    (: get the OID of the --SOC variable :)
    let $socoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'SOC') and string-length(@Name)=5]/@OID 
        where $a = $itemgroup/odm:ItemRef/@ItemOID
        return $a
    )
    let $socname := $definedoc//odm:ItemDef[@OID=$socoid]/@Name
    (: get the dataset location :)
	let $datasetlocation := (
		if($defineversion = '2.1') then $itemgroup/def21:leaf/@xlink:href
		else $itemgroup/def:leaf/@xlink:href
	)
    let $datasetdoc := doc(concat($base,$datasetlocation))
    (: iterate over all the records in the dataset :)
    for $record in $datasetdoc//odm:ItemGroupData
        let $recnum := $record/@data:ItemGroupDataSeq
        (: get the value of --SOC :)
        let $socvalue := $record/odm:ItemData[@ItemOID=$socoid]/@Value
        (: give an error when the SOC value is one of the SOC terms of MedDRA (case-insensitive), 
        but not in the list of the SOC terms in original case :)
        where $socvalue and functx:is-value-in-sequence(upper-case($socvalue),$soctermsuppercase) and not(functx:is-value-in-sequence($socvalue,$socterms)) 
        return <error rule="SD2015C" dataset="{data($name)}" variable="{data($socname)}" recordnumber="{data($recnum)}" rulelastupdate="2020-08-08">Value '{data($socvalue)}' for {data($socname)} is in incorrect case from the MedDRA dictionary</error>	
]]></rulexquery>
</sdsrule>



<!-- Rule SD1082: "Variable length is too long for actual data" is not implemented as it only applies to SAS-XPT format -->

<!-- Rule SD1096: "High risk of truncated value for -TERM variable" is not implemented as this can never be a rule. What is "high risk" anyway -->
<!--
<sdsrule id="SD1322" last-update="2019-09-14" originator="FDA" standard="SDTM" webservice="Uses RESTcountries.eu Web Service">
<ruledescription>Value for COUNTRY (DM) must be found in ISO 3166</ruledescription>
<ruledetaileddescription>Value for the Country (COUNTRY) variable must be populated using terms found in ISO 3166</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>DM</domain>
<rulexquery><![CDATA[
xquery version "3.1";
(: Rule SD1322: Value for COUNTRY (DM) must be found in ISO 3166 :)
(: Uses http:send-request which is not supported yet by most XQuery engines :)
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
declare namespace response="http://exist-db.org/xquery/response";
declare namespace http="http://expath.org/ns/http-client";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/'  :)
(: let $define := 'define2-0-0-example-sdtm.xml'  :)
(: get the metadata for the DM dataset :)
let $definedoc := doc(concat($base,$define))
let $dmitemgroupdef := $definedoc//odm:ItemGroupDef[@Name='DM']
(: get the OID of the "COUNTRY" variable :)
let $countryoid := (
    for $a in $definedoc//odm:ItemDef[@Name='COUNTRY']/@OID 
        where $a = $dmitemgroupdef/odm:ItemRef/@ItemOID
        return $a
)
(: get the location of the DM dataset and the document itself :)
let $datasetlocation := $dmitemgroupdef/def:leaf/@xlink:href

for $record in doc(concat($base,$datasetlocation))//odm:ItemGroupData[odm:ItemData[@ItemOID=$countryoid]]
    (: get the record number for reporting  :)
    let $recnum := $record/@data:ItemGroupDataSeq
    (:  get the value of "COUNTRY" :)
    let $country := $record/odm:ItemData[@ItemOID=$countryoid]/@Value 
    (: call the restcountries RESTful web service.
    If the code is not a valid ISO 3166 code, a 400 or 404 code is returned, 
    and the response is an empty string. 
    If the code is a valid ISO 3166 code, a 200 code is returned,
    and the response is a JSON string with all kind of information about the code
    :)
    let $querystring := concat('https://restcountries.eu/rest/v2/alpha/',$country)
    (: $response contains all the headers of the response, e.g.:
    <http:response xmlns:http="http://expath.org/ns/http-client" status="200" message="" spent-millis="204">...</hc:response>
    :)
    let $response := http:send-request(<http:request method='get' href='{$querystring}' timeout='10'/>)
    let $statuscode := $response[1]/@status
    where not($statuscode='200')
    return <warning rule="SD1322" dataset="DM" variable="COUNTRY" recordnumber="{data($recnum)}" rulelastupdate="2019-09-14">Value for COUNTRY '{data($country)}' is not found in ISO 3166</warning>		
]]></rulexquery>
</sdsrule>
-->

<!-- NEW: Uses "group by" to only get the unique country codes,
so that the RESTful web service needs to be called less often, 
which makes everything much faster -->
<sdsrule id="SD1322" last-update="2020-08-08" originator="FDA" standard="SDTM" webservice="Uses RESTcountries.eu Web Service">
<ruledescription>Value for COUNTRY (DM) must be found in ISO 3166</ruledescription>
<ruledetaileddescription>Value for the Country (COUNTRY) variable must be populated using terms found in ISO 3166</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>DM</domain>
<rulexquery><![CDATA[
(: REQUIRES XQuery 3.1 ! :)
xquery version "3.1";
(: Rule SD1322: Value for COUNTRY (DM) must be found in ISO 3166 :)
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: declare variable $domain external; :)
(: let $base := '/db/fda_submissions/cdisc01/'  
let $define := 'define2-0-0-example-sdtm.xml'  :)
let $definedoc := doc(concat($base,$define))
(: get the metadata for the DM dataset :)
let $dmitemgroupdef := $definedoc//odm:ItemGroupDef[@Name='DM']
(: get the OID of the "COUNTRY" variable :)
let $countryoid := (
    for $a in $definedoc//odm:ItemDef[@Name='COUNTRY']/@OID 
        where $a = $dmitemgroupdef/odm:ItemRef/@ItemOID
        return $a
)
(: get the location of the DM dataset and the document itself :)
let $datasetlocation := (
	if($defineversion = '2.1') then $dmitemgroupdef/def21:leaf/@xlink:href
	else $dmitemgroupdef/def:leaf/@xlink:href
)
(: group the records by "COUNTRY".
 This enables to call the RESTful web service (the slowest step) only once per group/country :)
let $orderedrecords := (
        for $record in doc(concat($base,$datasetlocation))//odm:ItemGroupData[odm:ItemData[@ItemOID=$countryoid]]
            group by 
            $b:=$record/odm:ItemData[@ItemOID=$countryoid]/@Value
            return element group {  
                $record
            }
        )
(: Iterate over the groups, we only need to check the first record in the group,
 as all the records in each group have the same value for COUNTRY :)
for $group in $orderedrecords
    let $country := $group/odm:ItemGroupData[1]/odm:ItemData[@ItemOID=$countryoid]/@Value
    let $recnum :=  $group/odm:ItemGroupData[1]/@data:ItemGroupDataSeq  (: first record number in the group :)
    (: now call the RESTful web service to check the validity of the country code :)
    let $querystring := concat('https://restcountries.eu/rest/v2/alpha/',$country)
    (: submit the country code and ask up it's alpha-3 code.
    If the request fails (not a 200 code), generate the string 'error'
    :)
    let $response := 
    try {
        json-doc($querystring)?alpha3Code
    } catch * {
        'error'
    }
    (: the alpha-3 code returned from the service must map the one provided
    when not, the code was not found :)
    where not($response = $country)
    return <error rule="SD1322" dataset="DM" variable="COUNTRY" recordnumber="{data($recnum)}" rulelastupdate="2020-08-08">Value for COUNTRY '{data($country)}' is not found in ISO 3166</error> 
]]></rulexquery>
</sdsrule>



<sdsrule id="SD1020" last-update="2020-08-08" originator="FDA" standard="SDTM" isrejectioncriterion="Yes">
<ruledescription>DM dataset must be included</ruledescription>
<ruledetaileddescription>Demographics (DM) dataset must be included in every submission</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>DM</domain>
<rulexquery><![CDATA[
(: Rule SD1020 - missing DM dataset :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
declare namespace request="http://exist-db.org/xquery/request";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external;
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
(: get the location of the DM dataset :)
let $definedoc := doc(concat($base,$define))
let $dmdatasetname := (
	if($defineversion = '2.1') then $definedoc//odm:ItemGroupDef[@Name='DM']/def21:leaf/@xlink:href
	else $definedoc//odm:ItemGroupDef[@Name='DM']/def:leaf/@xlink:href
)
let $dmdatasetlocation := concat($base,$dmdatasetname)
(: we now have the location of the dataset, check whether it is available there :)
where (not(doc-available($dmdatasetlocation)))
return <error rule="SD1020" rulelastupdate="2020-08-08" dataset="DM">Document {data($dmdatasetname)} could not be found in collection {data($base)}</error>	
]]></rulexquery>
</sdsrule>


<sdsrule id="SD1115" last-update="2020-08-08" originator="FDA" standard="SDTM" isrejectioncriterion="Yes">
<ruledescription>TS dataset must be included</ruledescription>
<ruledetaileddescription>Trial Summary (TS) dataset must be included in every submission</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>TS</domain>
<rulexquery><![CDATA[
(: Rule SD1115 - Missing TS dataset :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
declare namespace request="http://exist-db.org/xquery/request";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external;
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
(: get the location of the TS dataset :)
let $definedoc := doc(concat($base,$define))
let $tsdatasetname := (
	if($defineversion = '2.1') then $definedoc//odm:ItemGroupDef[@Name='TS']/def21:leaf/@xlink:href
	else $definedoc//odm:ItemGroupDef[@Name='TS']/def:leaf/@xlink:href
)
let $tsdatasetlocation := concat($base,$tsdatasetname)
(: we now have the location of the dataset, check whether it is available there :)
where (not(doc-available($tsdatasetlocation)))
return <error rule="SD1115" rulelastupdate="2020-08-08" dataset="TS">Document {data($tsdatasetname)} could not be found in collection {data($base)}</error>
]]></rulexquery>
</sdsrule>

<!-- This rule is essentially WRONG, especially for LB, as when different tests have been done on the same sample,
all -DTC values are identical. In LB: LBDTC is datetime of sample collection
-->
<!--
<sdsrule id="SD1117" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>Duplicate records - The structure of Findings class domains should be one records per Finding Result per subject. No Finding Result with the same Test Short Name (-TESTCD) for the same Subject (USUBJID) and the same Collection Date (-DTC) are expected</ruledescription>
<domain>FINDINGS</domain>
<rulexquery><![CDATA[		
(: Rule SD1117 - Duplicate records:
The structure of Findings class domains should be one records per Finding Result per subject. No Finding Result with the same Test Short Name (-TESTCD) for the same Subject (USUBJID) and the same Collection Date (-DTC) are expected.
:)
(: Identical observations should essentially have a different -DTC - if on the same date, the time part must be different :)
(: TODO? does not yet take 'splitted' datasets into account  :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
declare namespace xs="http://www.w3.org/2001/XMLSchema";
declare namespace functx = "http://www.functx.com";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: iterate over all FINDINGS domains :)
for $findingdomain in $definedoc//odm:ItemGroupDef[upper-case(@def:Class)='FINDINGS' or upper-case(./def21:Class/@Name)='FINDINGS']
    (: get the name and domain name, we need it for getting the complete name of the -TESTCD and -DTC variables :)
    let $name := $findingdomain/@Name
    let $domainname := $findingdomain/@Domain
    
    (: get the OID of USUBJID variable :)
    let $usubjidoid := (
    for $a in $definedoc//odm:ItemDef[@Name='USUBJID']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
    )
    (: get the oid of the -TESTCD and -DTC variables :)
    let $testcdname := concat($domainname,'TESTCD')
    let $dtcname := concat($domainname,'DTC')
    let $testcdoid := (
        for $a in $definedoc//odm:ItemDef[@Name=$testcdname]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
    )
    let $dtcoid := (
       for $a in $definedoc//odm:ItemDef[@Name=$dtcname]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID 
        return $a
    )
    (: the hard part - iterate over all records in the dataset and find records with the same combination of USUBJID -TESTCD and -DTC :)
    let $datasetname := $findingdomain/def:leaf/@xlink:href
    let $dataset := doc(concat($base,$datasetname))
    (: start iterating over all records that DO have a -DTC - 
    USUBJID and TESTCD are mandatory :)
    for $record in $dataset//odm:ItemGroupData[odm:ItemData[@ItemOID=$dtcoid]]
        let $recnum := $record/@data:ItemGroupDataSeq
        (: get the value of USUBJID, -TESTCD and -DTC :)
        let $usubjid := $record/odm:ItemData[@ItemOID=$usubjidoid]/@Value
        let $testcd := $record/odm:ItemData[@ItemOID=$testcdoid]/@Value
        let $dtc := $record/odm:ItemData[@ItemOID=$dtcoid]/@Value
        (: count the number of records with the same combination of USUBJID, TESTCD and DTC
        beyond the current record :)
        let $count := count($dataset//odm:ItemGroupData[@data:ItemGroupDataSeq &gt; $recnum][odm:ItemData[@ItemOID=$usubjidoid and @Value=$usubjid]][odm:ItemData[@ItemOID=$testcdoid and @Value=$testcd]][odm:ItemData[@ItemOID=$dtcoid and @Value=$dtc]])
        where $count &gt; 0
        return <warning rule="SD1117" dataset="{data($name)}" variable="{data($dtc)}" rulelastupdate="2020-08-08" recordnuber="{data($recnum)}">Duplicate Finding Result with the same Test Short Name {data($testcdname)}={data($testcd)} for the same Subject USUBJID={data($usubjid)} and the same Collection Date {data($dtcname)}={data($dtc)} found in dataset {data($datasetname)}. Number of occurrences of this combination is {data($count+1)}</warning>
]]></rulexquery>
</sdsrule>
-->

<!-- TODO???: SD1201: what is meant with this rule?
The structure of Events class domains should be one records per Event per subject. No Events with the same Collected Term (-TERM), Decoded Term (-DECOD), Category (-CAT), Subcategory (-SCAT), Severity (-SEV), and Toxicity Grade (-TOXGR) values for the same Subject (USUBJID) and the same Start Date (-STDTC) are expected
  -->

<!-- Rule SD1077: SD1077: EPOCH should be included for clinical subject-level observations (e.g., adverse events, laboratory, concomitant medications, exposure, and vital signs).
What the hell does "e.g." mean? We will apply it to all datasets except for SUPPQUAL, RELREC, DM, Tx (trial design) and MH (where it just doesn't make sense) and for SE and SV  -->
<sdsrule id="SD1077" last-update="2020-08-08" originator="FDA" standard="SDTM" >
<ruledescription>FDA expects variable EPOCH</ruledescription>
<ruledetaileddescription>Variables requested by FDA in policy documents should be included in the dataset. E.g., EPOCH</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>ALL</domain>
<rulexquery><![CDATA[	
(: Rule SD1077:  EPOCH should be included for clinical subject-level observations (e.g., adverse events, laboratory, concomitant medications, exposure, and vital signs).
What the hell does "e.g." mean in a rule? 
We will apply it to all datasets except for SUPPQUAL, RELREC, DM, Tx (trial design) and MH and DI(where it just doesn't make sense) 
and for SE and SV :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
declare namespace functx = "http://www.functx.com";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' 
let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: iterate over all the dataset definitions in the define.xml, 
 except for DM, SUPPQUAL, RELREC, and the trial design domains :)
for $datasetdef in $definedoc//odm:ItemGroupDef[not(@Name='DM' or @Name='TS' or @Name='TA' or @Name='TE' or @Name='TV' or @Name='TD' or @Name='TI' or @Domain='MH' or @Name='DI' or @Name='SV' or @Name='SE' or @Name='RELREC' or starts-with(@Name,'SUPP'))]
    (: get the dataset name :)
    let $name := $datasetdef/@Name
    (: get whether there is an EPOCH variable :)
    let $epochoid := (
        for $a in $definedoc//odm:ItemDef[@Name='EPOCH']/@OID 
        where $a = $datasetdef/odm:ItemRef/@ItemOID
        return $a
    )
    (: if there is no EPOCH variable defined, give an error :)
    where not($epochoid)
    return <error rule="SD1077" dataset="{data($name)}" variable="EPOCH" rulelastupdate="2020-08-08">FDA expected variable EPOCH is not found in dataset '{data($name)}'</error>
]]>
</rulexquery>
</sdsrule>

<sdsrule id="SD0079" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>EX record may not be present, when subject is not assigned to an arm</ruledescription>
<ruledetaileddescription>Subjects that have withdrawn from a trial before assignment to an Arm (ARMCD='NOTASSGN') should not have any Exposure records</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>EX</domain>
<domain>DM</domain>
<rulexquery><![CDATA[
(: Rule SD0079: EX record is present, when subject is not assigned to an arm: Subjects that have withdrawn from a trial before assignment to an Arm (ARMCD='NOTASSGN') should not have any Exposure records :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external;
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: Get the DM dataset :)
let $dmdatasetname := (
	if($defineversion = '2.1') then $definedoc//odm:ItemGroupDef[@Name='DM']/def21:leaf/@xlink:href
	else $definedoc//odm:ItemGroupDef[@Name='DM']/def:leaf/@xlink:href
)
let $dmdataset := concat($base,$dmdatasetname)
(: Get the EX dataset :)
let $exdatasetname := ( 
	if($defineversion = '2.1') then $definedoc//odm:ItemGroupDef[@Name='EX']/def21:leaf/@xlink:href
	else $definedoc//odm:ItemGroupDef[@Name='EX']/def:leaf/@xlink:href
)
let $exdataset := concat($base,$exdatasetname)
(: get the OID for the ARMCD variable in the DM dataset :)
(: TODO - change this, supposing there is only one is not always correct :)
(: let $armcdoid := $definedoc//odm:ItemDef[@Name='ARMCD']/@OID :)  (: supposing there is only one :) 
let $armcdoid := (
    for $a in $definedoc//odm:ItemDef[@Name='ARMCD']/@OID 
    where $a = $definedoc//odm:ItemGroupDef[@Name='DM']/odm:ItemRef/@ItemOID
    return $a
)
(: and the OID of USUBJID :)
let $usubjidoid := (
    for $a in $definedoc//odm:ItemDef[@Name='USUBJID']/@OID 
    where $a = $definedoc//odm:ItemGroupDef[@Name='DM']/odm:ItemRef/@ItemOID
    return $a
)
(: we also need the OID of the USUBJID in the EX dataset :)
let $exusubjidoid := (
    for $a in $definedoc//odm:ItemDef[@Name='USUBJID']/@OID 
    where $a = $definedoc//odm:ItemGroupDef[@Name='EX']/odm:ItemRef/@ItemOID
    return $a
)
(: in the DM dataset, select the subjects which have ARMCD='NOTASSGN' :)
for $rec in doc($dmdataset)//odm:ItemGroupData[odm:ItemData[@ItemOID=$armcdoid and @Value='NOTASSGN']]
    let $usubjidvalue := $rec/odm:ItemData[@ItemOID=$usubjidoid]/@Value
    (: and the record number for which ARMCD='NOTASSGN' :)
    let $recnum := $rec/@data:ItemGroupDataSeq
    (: and now check whether there is a record in the EX dataset :)
    let $count := count(doc($exdataset)//odm:ItemGroupData[odm:ItemData[@ItemOID=$exusubjidoid]])
    where $count > 0  (: at least one EX record was found :)
    return <warning rule="SD0079" dataset="EX" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">{data($count)} EX records were found in EX dataset {data($exdatasetname)} for USUBJID={data($usubjidvalue)} although subject has not been assigned to an arm (ARMCD='NOTASSGN') in DM dataset {data($dmdatasetname)}</warning>
]]></rulexquery>
</sdsrule>

<sdsrule id="SD0070" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>Randomized subjects must at least have one Exposure record</ruledescription>
<ruledetaileddescription>All Demographics (DM) subjects (USUBJID) participating in a study that includes investigational product should have at least one record in the Exposure (EX) domain, except for subjects who failed screening (ARMCD = 'SCRNFAIL') or were not fully assigned to an Arm (ARMCD = 'NOTASSGN')</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>EX</domain>
<domain>DM</domain>
<rulexquery><![CDATA[
(: Rule SD0070 - No Exposure record found for subject:
All Demographics (DM) subjects (USUBJID) participating in a study that includes investigational product should have at least one record in the Exposure (EX) domain, except for subjects who failed screening (ARMCD = 'SCRNFAIL') or were not fully assigned to an Arm (ARMCD = 'NOTASSGN')
:)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
declare namespace xs="http://www.w3.org/2001/XMLSchema";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external;
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: Get the DM dataset :)
let $dmdatasetname := (
	if($defineversion = '2.1') then $definedoc//odm:ItemGroupDef[@Name='DM']/def21:leaf/@xlink:href
	else $definedoc//odm:ItemGroupDef[@Name='DM']/def:leaf/@xlink:href
)
let $dmdataset := concat($base,$dmdatasetname)
(: Get the EX dataset :)
let $exdatasetname := (
	if($defineversion = '2.1') then $definedoc//odm:ItemGroupDef[@Name='EX']/def21:leaf/@xlink:href
	else $definedoc//odm:ItemGroupDef[@Name='EX']/def:leaf/@xlink:href
)
let $exdataset := concat($base,$exdatasetname)
(: and the OID of the USUBJID and of ARMCD in the DM dataset :) 
let $usubjidoid := (
    for $a in $definedoc//odm:ItemDef[@Name='USUBJID']/@OID 
    where $a = $definedoc//odm:ItemGroupDef[@Name='DM']/odm:ItemRef/@ItemOID
    return $a
)
(: Attention, there can be more than 1 of them - although bad practice :)
(: let $armcdoid := $definedoc//odm:ItemDef[@Name='ARMCD']/@OID :)
(: better and surer :)
let $armcdoid := (
    for $a in $definedoc//odm:ItemDef[@Name='ARMCD']/@OID 
    where $a = $definedoc//odm:ItemGroupDef[@Name='DM']/odm:ItemRef/@ItemOID
    return $a
)
(: we also need the OID of USUBJID in the EX dataset :)
let $exusubjidoid := (
    for $a in $definedoc//odm:ItemDef[@Name='USUBJID']/@OID 
    where $a = $definedoc//odm:ItemGroupDef[@Name='EX']/odm:ItemRef/@ItemOID
    return $a
)
(: iterate over the DM dataset and  :)
for $record in doc($dmdataset)//odm:ItemGroupData
    let $recnum := $record/@data:ItemGroupDataSeq
    let $usubjidvalue := $record/odm:ItemData[@ItemOID=$usubjidoid]/@Value
    (: $hasarmassigned sets whether the subject has been assigned tom an arm :)
	(: REMARK that the following may/will fail in the case of SDTMIG 3.3 where ARMCD=null and AMRNRS is populated for non-randomized subjects :)
    let $hasarmassigned := not($record/odm:ItemData[@ItemOID=$armcdoid]/@Value = 'SCRNFAIL' or  $record/odm:ItemData[@ItemOID=$armcdoid]/@Value = 'NOTASSGN')
    (: now count the number of records for this subject in the DS dataset :)
    let $count := count(doc($exdataset)//odm:ItemGroupData[odm:ItemData[@ItemOID=$exusubjidoid and @Value=$usubjidvalue]])
    let $arm := $record/odm:ItemData[@ItemOID=$armcdoid]/@Value
    where $hasarmassigned and $count = 0
    (: and now check whether there are any records in the DS dataset  :)
    return <warning rule="SD0070" rulelastupdate="2020-08-08" dataset="DM" variable="USUBJID" recordnumber="{data($recnum)}">No exposure records were found in dataset {data($exdatasetname)} for subject {data($usubjidvalue)} with assigned arm {data($arm)}</warning>
]]></rulexquery>
</sdsrule>

<sdsrule id="SD1071" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>Dataset may not be greater than 5 GB in size </ruledescription>
<ruledetaileddescription>Large datasets should be split into smaller datasets no larger than 5 GB in size.</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>ALL</domain>
<rulexquery><![CDATA[
(: Rule SD1071 - Dataset is greater than 5 GB in size :)
(: see: http://rvdb.wordpress.com/2013/02/26/an-xquery-script-for-listing-the-contents-of-collections-in-exist-db/ :)
(: TODO - xmldb:get-child-resources is eXist-specific, does not work for files :)
(: suggestion: use collection('file:///a/b/c/d?select=*.xml') - see http://stackoverflow.com/questions/5601764/xquery-all-files-under-a-specific-directory :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
declare namespace request="http://exist-db.org/xquery/request";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external; 
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
for $child in xmldb:get-child-resources($base)
    let $datasetname := upper-case(substring-before($child,'.'))
    let $path := concat($base, $child)
    (: TODO: test further: file sizes obtained here are about 20% too high :)
    let $size := fn:ceiling(xmldb:size($base, $child) div (1024*1024))  (: file size in MB :)
    where $size > (5*1024)
    return
    <warning rule="SD1071" dataset="{$datasetname}" rulelastupdate="2020-08-08">Size of file {$child} is larger than 1GB - file size found = {$size} MB </warning>
]]></rulexquery>
</sdsrule>

<!-- As this is a bad rule, I assigned "info" as the severity 
- also see the discussion at: https://www.pinnacle21.com/forum/rule-sd2237-actarm-does-not-equal-arm-violates-fda-technical-conformance-guide-march-2019  -->
<sdsrule id="SD2237" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>ACTARM must be equal to ARM</ruledescription>
<ruledetaileddescription>A value for an Actual Arm (ACTARM) variable is expected to be equal to a value of an Arm (ARM) variable</ruledetaileddescription>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>DM</domain>
<rulexquery><![CDATA[	
(: Rule SD2237: A value for an Actual Arm (ACTARM) variable is expected to be equal to a value of an Arm (ARM) variable :)
(: FDA Technical Conformance Guide March 2019:
Screen failures, when provided, should be included as a record in DM with the ARM, ARMCD, ACTARM, and ACTARMCDfield left blank. 
For subjects who are randomized in treatment group but not treated, the planned arm variables (ARM and ARMCD) should be populated,
but actual treatment arm variables (ACTARM and ACTARMCD) should be left blank
:)
(: Rule violates conformance guide for non-treated but randomized subjects (March 2019) :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' 
let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: Get the DM dataset :)
for $dataset in $definedoc//odm:ItemGroupDef[@Name='DM']
    let $datasetname := (
		if($defineversion = '2.1') then $dataset/def21:leaf/@xlink:href
		else $dataset/def:leaf/@xlink:href
	)
    let $datasetlocation := concat($base,$datasetname)
    (: get the OID of the ACTARM and of ARM :)
    let $actarmoid := (
        for $a in $definedoc//odm:ItemDef[@Name='ACTARM']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name='DM']/odm:ItemRef/@ItemOID
        return $a
    )
    let $armoid := (
        for $a in $definedoc//odm:ItemDef[@Name='ARM']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name='DM']/odm:ItemRef/@ItemOID
        return $a
    )
    (: iterate over all the records in the DM dataset :)
    for $record in doc($datasetlocation)//odm:ItemGroupData
        let $recnum := $record/@data:ItemGroupDataSeq
        (: get the value of ACTARM and of ARM :)
        let $actarmvalue := $record/odm:ItemData[@ItemOID=$actarmoid]/@Value
        let $armvalue := $record/odm:ItemData[@ItemOID=$armoid]/@Value
        (: give a INFO when ACTARM != ARM :)
        where $actarmvalue and $armvalue and not($actarmvalue = $armvalue)
        return <info dataset="DM" variable="ACTARM" rule="SD2237" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">ACTARM, value='{data($actarmvalue)}' does not equal ARM, value='{data($armvalue)}'</info>	
]]></rulexquery>
</sdsrule>

<sdsrule id="SD1131" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>--STRESC value must be present for Baseline record</ruledescription>
<ruledetaileddescription>A Standard Result value (--STRESC) is expected to be populated for Baseline records (--BLFL="Y")</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>FINDINGS</domain>
<rulexquery><![CDATA[	
(: Rule SD1131 - Missing --STRESC value for Baseline record
A Standard Result value (--STRESC) is expected to be populated for Baseline records (--BLFL="Y")
FINDINGS datasets
:)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink"; 
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/'  :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: Iterate over all FINDINGS domains :)
for $dataset in $definedoc//odm:ItemGroupDef[upper-case(@def:Class)='FINDINGS' or upper-case(./def21:Class/@Name)='FINDINGS']
    let $name := $dataset/@Name
	let $datasetname := (
		if($defineversion = '2.1') then $dataset/def21:leaf/@xlink:href
		else $dataset/def:leaf/@xlink:href
	)
    let $datasetdoc := (
		if($datasetname) then doc(concat($base,$datasetname))
		else ()
	)
    (: and get the OIDs of the STRESC and BLFL variables :)
    let $strescoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'STRESC')]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a 
    )
    let $strescname := $definedoc//odm:ItemDef[@OID=$strescoid]/@Name
    let $blfloid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'BLFL')]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a     
    )
    let $blflname := $definedoc//odm:ItemDef[@OID=$blfloid]/@Name
    (: now iterate over all records in the dataset that DO have an BLFL with value "Y" :)
    for $record in $datasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$blfloid][@Value='Y']]
        let $recnum := $record/@data:ItemGroupDataSeq
        (: and get the values for STRESC and ORRES :)
        let $strescvalue := $record/odm:ItemData[@ItemOID=$strescoid]/@Value 
        let $blflvalue := $record/odm:ItemData[@ItemOID=$blfloid]/@Value
        (: we already selected on the presence of BLFL=Y,check whether STRESC is present :)
        where not($strescvalue) 
        return <warning rule="SD1131" dataset="{data($name)}" variable="{data($strescname)}" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">Missing {data($strescname)}, when {data($blflname)} value={data($blflvalue)} is provided in dataset {data($datasetname)}</warning>	
]]></rulexquery>
</sdsrule>

<sdsrule id="SD1131-SD" last-update="2020-08-08" originator="FDA" standard="SDTM" requiresDomainOrDataset="Yes">
<ruledescription>--STRESC value must be present for Baseline record</ruledescription>
<ruledetaileddescription>A Standard Result value (--STRESC) is expected to be populated for Baseline records (--BLFL="Y")</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>FINDINGS</domain>
<rulexquery><![CDATA[	
(: Rule SD1131 - Missing --STRESC value for Baseline record
A Standard Result value (--STRESC) is expected to be populated for Baseline records (--BLFL="Y")
FINDINGS datasets
:)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink"; 
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external;
declare variable $defineversion external;
declare variable $datasetname external;
(: let $base := '/db/fda_submissions/cdisc01/'  :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: Iterate over the provided dataset in the FINDINGS domains :)
for $dataset in $definedoc//odm:ItemGroupDef[@Name=$datasetname][upper-case(@def:Class)='FINDINGS' or upper-case(./def21:Class/@Name)='FINDINGS']
    let $name := $dataset/@Name
    let $dsname := (
		if($defineversion = '2.1') then $dataset/def21:leaf/@xlink:href
		else $definedoc/def:leaf/@xlink:href
	)
    let $datasetdoc := (
		if($dsname) then doc(concat($base,$dsname))
		else ()
	)
    (: and get the OIDs of the STRESC and BLFL variables :)
    let $strescoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'STRESC')]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a 
    )
    let $strescname := $definedoc//odm:ItemDef[@OID=$strescoid]/@Name
    let $blfloid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'BLFL')]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a     
    )
    let $blflname := $definedoc//odm:ItemDef[@OID=$blfloid]/@Name
    (: now iterate over all records in the dataset that DO have an BLFL with value "Y" :)
    for $record in  $datasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$blfloid][@Value='Y']]
        let $recnum := $record/@data:ItemGroupDataSeq
        (: and get the values for STRESC and ORRES :)
        let $strescvalue := $record/odm:ItemData[@ItemOID=$strescoid]/@Value 
        let $blflvalue := $record/odm:ItemData[@ItemOID=$blfloid]/@Value
        (: we already selected on the presence of BLFL=Y,check whether STRESC is present :)
        where not($strescvalue) 
        return <warning rule="SD1131" dataset="{data($name)}" variable="{data($strescname)}" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">Missing {data($strescname)}, when {data($blflname)} value={data($blflvalue)} is provided in dataset {data($datasetname)}</warning>	
]]></rulexquery>
</sdsrule>

<sdsrule id="SD1032" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>'SCRNFAIL' subject must have a record in the IE domain - </ruledescription>
<ruledetaileddescription>All subjects with Planned Arm Code (ARMCD) equals 'SCRNFAIL' should have records in the Inclusion/Exclusion Criteria Not Met (IE) domain</ruledetaileddescription>
<domain>IE</domain>
<domain>DM</domain>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<rulexquery><![CDATA[	
(: Rule SD1032 - No records for 'SCRNFAIL' subject are found in IE domain:
All subjects with Planned Arm Code (ARMCD) equals 'SCRNFAIL' should have records in the Inclusion/Exclusion Criteria Not Met (IE) domain
:)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: iterate over all records in the DM datasets :)
let $dmdataset := $definedoc//odm:ItemGroupDef[@Name='DM']
let $dmdatasetname := (
	if($defineversion = '2.1') then $dmdataset/def21:leaf/@xlink:href
	else $dmdataset/def:leaf/@xlink:href
)
let $dmdatasetlocation:= concat($base,$dmdatasetname)
(: and get the OIDs of USUBJID and ARMCD :)
let $usubjidoid := (
for $a in $definedoc//odm:ItemDef[@Name='USUBJID']/@OID 
    where $a = $definedoc//odm:ItemGroupDef[@Name='DM']/odm:ItemRef/@ItemOID
    return $a
)
let $armcdoid := (
    for $a in $definedoc//odm:ItemDef[@Name='ARMCD']/@OID 
    where $a = $definedoc//odm:ItemGroupDef[@Name='DM']/odm:ItemRef/@ItemOID
    return $a
)
(: Get the IE data :)
let $iedataset := $definedoc//odm:ItemGroupDef[@Name='IE']
let $iedatasetname := (
	if($defineversion = '2.1') then $iedataset/def21:leaf/@xlink:href
	else $iedataset/def:leaf/@xlink:href
)
let $iedatasetlocation := concat($base,$iedatasetname)
(: and the OID of the USUBJID in the IE dataset :)
let $ieusubjidoid := (
    for $a in $definedoc//odm:ItemDef[@Name='USUBJID']/@OID 
    where $a = $definedoc//odm:ItemGroupDef[@Name='IE']/odm:ItemRef/@ItemOID
    return $a
)
(: get the records for which ARMCD=SCRNFAIL :)
for $record in doc($dmdatasetlocation)//odm:ItemGroupData[odm:ItemData[@ItemOID=$armcdoid][@Value='SCRNFAIL']]
    let $recnum := $record/@data:ItemGroupDataSeq
    (: get the USUBJID of this record :)
    let $dmusubjid := $record/odm:ItemData[@ItemOID=$usubjidoid]/@Value
    (: and count the number of records for this subject in the IE dataset :)
    let $ierecordcount := count(doc($iedatasetlocation)//odm:ItemGroupData[odm:ItemData[@ItemOID=$ieusubjidoid][@Value=$dmusubjid]])
    (: there must be at least one record in IE :)
    where $ierecordcount > 1
    return <warning rule="SD1032" dataset="DM" variable="ARMCD" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">No records for in IE dataset {data($iedatasetname)} for Screen Failure subject with USUBJID {data($dmusubjid)} </warning>		
]]></rulexquery>
</sdsrule>

<sdsrule id="SD1320" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>--STRESC must be populated, when --STAT is null</ruledescription>
<ruledetaileddescription>Character Result/Finding in Std Format (--STRESC) value should not be NULL, when Status Flag (--STAT) value is null</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>FINDINGS</domain>
<rulexquery><![CDATA[
(: Rule SD1320 - Missing value for --STRESC, when --STAT is null - FINDINGS domains
Character Result/Finding in Std Format (--STRESC) value should not be NULL, when Status Flag (--STAT) value is null.
:)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/'
let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: iterate over all the FINDINGS datasets :)
for $dataset in $definedoc//odm:ItemGroupDef[upper-case(@def:Class)='FINDINGS' or upper-case(./def21:Class/@Name)='FINDINGS']
    let $name := $dataset/@Name
    let $datasetname := (
		if($defineversion = '2.1') then $dataset/def21:leaf/@xlink:href
		else $dataset/def:leaf/@xlink:href
	)
    let $datasetlocation := concat($base,$datasetname)
    (: Find the OID of the --STAT variable  :)
    let $statoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'STAT')]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
    )
    (: and the name :)
    let $statname := $definedoc//odm:ItemDef[@OID=$statoid]/@Name
    (: Find the OID and the name of the --STRESC variable :)
    let $strescoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'STRESC')]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
    )
    let $strescname := $definedoc//odm:ItemDef[@OID=$strescoid]/@Name
    (: iterate over all the records, but only if there really is a --STAT variable (as it is permissible) :)
    (: TODO: to speed things up, an "IF" is needed here :)
    for $record in doc($datasetlocation)//odm:ItemGroupData
        let $recnum := $record/@data:ItemGroupDataSeq
        (: Get the value of the DRVFL variable :)
        let $statvalue := $record/odm:ItemData[@ItemOID=$statoid]/@Value
        (: and of the STRESC variable (if any) :)
        let $strescvalue := $record/odm:ItemData[@ItemOID=$strescoid]/@Value
        (: when --STAT exists and --STAT=null ensure there is a STRESC data point :)
        where $statoid and (not($statvalue) and not($strescvalue))
        return <warning rule="SD1320" rulelastupdate="2020-08-08" dataset="{data($name)}" variable="{data($strescname)}" recordnumber="{data($recnum)}">Missing value for {data($strescname)}, when {data($statname)}=null</warning>
	]]>
</rulexquery>
</sdsrule>

<sdsrule id="SD1320-SD" last-update="2020-08-08" originator="FDA" standard="SDTM" requiresDomainOrDataset="Yes">
<ruledescription>--STRESC must be populated, when --STAT is null</ruledescription>
<ruledetaileddescription>Character Result/Finding in Std Format (--STRESC) value should not be NULL, when Status Flag (--STAT) value is null</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>FINDINGS</domain>
<rulexquery><![CDATA[
(: Rule SD1320 - Missing value for --STRESC, when --STAT is null - FINDINGS domains
Character Result/Finding in Std Format (--STRESC) value should not be NULL, when Status Flag (--STAT) value is null.
:)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
declare variable $datasetname external;
(: let $base := '/db/fda_submissions/cdisc01/'
let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: iterate over all the FINDINGS datasets :)
for $dataset in $definedoc//odm:ItemGroupDef[upper-case(@def:Class)='FINDINGS' or upper-case(./def21:Class/@Name)='FINDINGS'][@Name=$datasetname]
    let $name := $dataset/@Name
    let $dsname := (
		if($defineversion = '2.1') then $dataset/def21:leaf/@xlink:href
		else $dataset/def:leaf/@xlink:href
	)
    let $datasetlocation := concat($base,$dsname)
    (: Find the OID of the --STAT variable  :)
    let $statoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'STAT')]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
    )
    (: and the name :)
    let $statname := $definedoc//odm:ItemDef[@OID=$statoid]/@Name
    (: Find the OID and the name of the --STRESC variable :)
    let $strescoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'STRESC')]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
    )
    let $strescname := $definedoc//odm:ItemDef[@OID=$strescoid]/@Name
    (: iterate over all the records, but only if there really is a --STAT variable (as it is permissible) :)
    (: TODO: to speed things up, an "IF" is needed here :)
    for $record in doc($datasetlocation)//odm:ItemGroupData
        let $recnum := $record/@data:ItemGroupDataSeq
        (: Get the value of the DRVFL variable :)
        let $statvalue := $record/odm:ItemData[@ItemOID=$statoid]/@Value
        (: and of the STRESC variable (if any) :)
        let $strescvalue := $record/odm:ItemData[@ItemOID=$strescoid]/@Value
        (: when --STAT exists and --STAT=null ensure there is a STRESC data point :)
        where $statoid and (not($statvalue) and not($strescvalue))
        return <warning rule="SD1320" rulelastupdate="2020-08-08" dataset="{data($name)}" variable="{data($strescname)}" recordnumber="{data($recnum)}">Missing value for {data($strescname)}, when {data($statname)}=null</warning>
	]]>
</rulexquery>
</sdsrule>

<!-- Rule SD0007: Standard Units must be consistent within the same assessment (having the same -TESTCD, -CAT, -SCAT, -SPEC, -METHOD values)
 NOT IMPLEMENTED HERE: Was FDAC084 - NONSENSE RULE: there are many exceptions: test should be based on LOINC code -->
 
<sdsrule id="SD1122" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>--STRESN must be populated when standardized value is numeric</ruledescription>
<ruledetaileddescription>Standardized Result in Numeric Format (--STRESN) variable value should be populated, when Standardized Result in Character Format (--STRESC) variable value represents a numeric value</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>FINDINGS</domain>
<rulexquery><![CDATA[	
(: Rule SD1122 - Missing value for --STRESN: Standardized Result in Numeric Format (--STRESN) variable value should be populated, when Standardized Result in Character Format (--STRESC) variable value represents a numeric value
:)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink"; 
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: Iterate over all FINDINGS domains :)
for $dataset in $definedoc//odm:ItemGroupDef[upper-case(@def:Class)='FINDINGS' or upper-case(./def21:Class/@Name)='FINDINGS']
    let $name := $dataset/@Name
    let $datasetname := (
		if($defineversion = '2.1') then $dataset/def21:leaf/@xlink:href
		else $dataset/def:leaf/@xlink:href
	)
    let $datasetdoc := (
		if($datasetname) then doc(concat($base,$datasetname))
		else ()
	)
    (: and get the OIDs of the STRESC and STRESN variables :)
    let $strescoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'STRESC')]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a 
    )
    let $strescname := $definedoc//odm:ItemDef[@OID=$strescoid]/@Name
    let $stresnoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'STRESN')]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a     
    )
    let $stresnname := $definedoc//odm:ItemDef[@OID=$stresnoid]/@Name
    (: now iterate over all records in the dataset that DO have an STRESC :)
    for $record in $datasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$strescoid]]
        let $recnum := $record/@data:ItemGroupDataSeq
        (: and get the values for STRESC and ORRES :)
        let $strescvalue := $record/odm:ItemData[@ItemOID=$strescoid]/@Value 
        let $stresnvalue := $record/odm:ItemData[@ItemOID=$stresnoid]/@Value
        (: we already selected on the presence of ORRES, check whether STRESN is present AND represents a numeric value :)
         where not($stresnvalue) and $strescvalue castable as xs:double
        return <warning rule="SD1122" dataset="{data($name)}" variable="{data($stresnname)}" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">Missing value for {data($stresnname)}, when {data($strescname)} value={data($strescvalue)} is provided and represents a numeric value in dataset {data($datasetname)}</warning>	
]]></rulexquery>
</sdsrule>

<sdsrule id="SD1122-SD" last-update="2020-08-08" originator="FDA" standard="SDTM" requiresDomainOrDataset="Yes">
<ruledescription>--STRESN must be populated when standardized value is numeric</ruledescription>
<ruledetaileddescription>Standardized Result in Numeric Format (--STRESN) variable value should be populated, when Standardized Result in Character Format (--STRESC) variable value represents a numeric value</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>FINDINGS</domain>
<rulexquery><![CDATA[	
(: Rule SD1122 - Missing value for --STRESN:
Standardized Result in Numeric Format (--STRESN) variable value should be populated, when Standardized Result in Character Format (--STRESC) variable value represents a numeric value
:)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink"; 
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
declare variable $datasetname external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: Iterate over all FINDINGS domains :)
for $dataset in $definedoc//odm:ItemGroupDef[@Name=$datasetname][upper-case(@def:Class)='FINDINGS' or upper-case(./def21:Class/@Name)='FINDINGS']
    let $name := $dataset/@Name
    let $dsname := (
		if($defineversion = '2.1') then $dataset/def21:leaf/@xlink:href
		else $dataset/def:leaf/@xlink:href
	)
    let $datasetdoc := (
		if($dsname) then doc(concat($base,$dsname))
		else ()
	)
    (: and get the OIDs of the STRESC and STRESN variables :)
    let $strescoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'STRESC')]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a 
    )
    let $strescname := $definedoc//odm:ItemDef[@OID=$strescoid]/@Name
    let $stresnoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'STRESN')]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a     
    )
    let $stresnname := $definedoc//odm:ItemDef[@OID=$stresnoid]/@Name
    (: now iterate over all records in the dataset that DO have an STRESC :)
    for $record in $datasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$strescoid]]
        let $recnum := $record/@data:ItemGroupDataSeq
        (: and get the values for STRESC and ORRES :)
        let $strescvalue := $record/odm:ItemData[@ItemOID=$strescoid]/@Value 
        let $stresnvalue := $record/odm:ItemData[@ItemOID=$stresnoid]/@Value
        (: we already selected on the presence of ORRES, check whether STRESN is present AND represents a numeric value :)
         where not($stresnvalue) and $strescvalue castable as xs:double
        return <warning rule="SD1122" dataset="{data($name)}" variable="{data($stresnname)}" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">Missing value for {data($stresnname)}, when {data($strescname)} value={data($strescvalue)} is provided and represents a numeric value in dataset {data($datasetname)}</warning>
]]></rulexquery>
</sdsrule>

<!-- Rule SD0007 "Inconsistent value for Standard Units" is nonsense! -->

<sdsrule id="SD1212" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>--STRESN must be equal to --STRESC</ruledescription>
<ruledetaileddescription>Standardized Result in Numeric Format (--STRESN) variable value should be populated, when Standardized Result in Character Format (--STRESC) variable value represents a numeric value</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>FINDINGS</domain>
<rulexquery><![CDATA[
(: Rule SD1212 - --STRESN does not equal --STRESC when --STRESC represents a number - FINDINGS domains
:)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/'
let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: iterate over all the FINDINGS datasets :)
for $dataset in $definedoc//odm:ItemGroupDef[upper-case(@def:Class)='FINDINGS' or upper-case(./def21:Class/@Name)='FINDINGS']
    let $name := $dataset/@Name
    let $datasetname := (
		if($defineversion = '2.1') then $dataset/def21:leaf/@xlink:href
		else $dataset/def:leaf/@xlink:href
	)
    let $datasetdoc := (
		if($datasetname) then doc(concat($base,$datasetname))
		else ()
	)
    (: Find the OID of the --STRESN variable  :)
    let $stresnoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'STRESN')]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
    )
    (: and the name :)
    let $stresnname := $definedoc//odm:ItemDef[@OID=$stresnoid]/@Name
    (: Find the OID and the name of the --STRESC variable :)
    let $strescoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'STRESC')]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
    )
    let $strescname := $definedoc//odm:ItemDef[@OID=$strescoid]/@Name
    (: iterate over all the records in the dataset :)
    (: TODO: to speed things up, an "IF" is needed here :)
    for $record in $datasetdoc//odm:ItemGroupData
        let $recnum := $record/@data:ItemGroupDataSeq
        (: Get the value of the STRESC variable :)
        let $strescvalue := $record/odm:ItemData[@ItemOID=$strescoid]/@Value
        (: and of the STRESN variable (if any) :)
        let $stresnvalue := $record/odm:ItemData[@ItemOID=$stresnoid]/@Value
        (: check whether the value of --STRESC is representing a numeric value :)
        (: when --STAT exists and --STAT=null ensure there is a STRESC data point :)
        where $strescvalue castable as xs:double and xs:double($strescvalue) != xs:double($stresnvalue)
        return <warning rule="SD1212" rulelastupdate="2020-08-08" dataset="{data($name)}" variable="{data($stresnname)}" recordnumber="{data($recnum)}">Value for {data($stresnname)} '{data($stresnvalue)}' does not equal value for {data($strescname)} '{data($strescvalue)}'</warning>	
]]>
</rulexquery>
</sdsrule>

<sdsrule id="SD1212-SD" last-update="2020-08-08" originator="FDA" standard="SDTM" requiresDomainOrDataset="Yes">
<ruledescription>--STRESN must be equal to --STRESC</ruledescription>
<ruledetaileddescription>Standardized Result in Numeric Format (--STRESN) variable value should be populated, when Standardized Result in Character Format (--STRESC) variable value represents a numeric value</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>FINDINGS</domain>
<rulexquery><![CDATA[
(: Rule SD1212 - --STRESN does not equal --STRESC when --STRESC represents a number - FINDINGS domains
:)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
declare variable $datasetname external;
(: let $base := '/db/fda_submissions/cdisc01/'
let $define := 'define2-0-0-example-sdtm.xml' :)
(: iterate over all the FINDINGS datasets :)
let $definedoc := doc(concat($base,$define))
for $dataset in $definedoc//odm:ItemGroupDef[@Name=$datasetname][upper-case(@def:Class)='FINDINGS' or upper-case(./def21:Class/@Name)='FINDINGS']
    let $name := $dataset/@Name
    let $dsname := (
		if($defineversion = '2.1') then $dataset/def21:leaf/@xlink:href
		else $dataset/def:leaf/@xlink:href
	)
    let $datasetlocation := concat($base,$dsname)
    (: Find the OID of the --STRESN variable  :)
    let $stresnoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'STRESN')]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
    )
    (: and the name :)
    let $stresnname := $definedoc//odm:ItemDef[@OID=$stresnoid]/@Name
    (: Find the OID and the name of the --STRESC variable :)
    let $strescoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'STRESC')]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
    )
    let $strescname := $definedoc//odm:ItemDef[@OID=$strescoid]/@Name
    (: iterate over all the records in the dataset :)
    (: TODO: to speed things up, an "IF" is needed here :)
    for $record in doc($datasetlocation)//odm:ItemGroupData
        let $recnum := $record/@data:ItemGroupDataSeq
        (: Get the value of the STRESC variable :)
        let $strescvalue := $record/odm:ItemData[@ItemOID=$strescoid]/@Value
        (: and of the STRESN variable (if any) :)
        let $stresnvalue := $record/odm:ItemData[@ItemOID=$stresnoid]/@Value
        (: check whether the value of --STRESC is representing a numeric value :)
        (: when --STAT exists and --STAT=null ensure there is a STRESC data point :)
        where $strescvalue castable as xs:double and xs:double($strescvalue) != xs:double($stresnvalue)
        return <warning rule="SD1212" rulelastupdate="2020-08-08" dataset="{data($name)}" variable="{data($stresnname)}" recordnumber="{data($recnum)}">Value for {data($stresnname)} '{data($stresnvalue)}' does not equal value for {data($strescname)} '{data($strescvalue)}'</warning>	
]]>
</rulexquery>
</sdsrule>

<sdsrule id="SE0009" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>--DTC or --DY must be populated</ruledescription>
<ruledetaileddescription>Start Date/Time of Observation (--DTC) or Study Day of Observation (--DY) should be populated in the Findings observation class where data are usually collected at multiple study days.</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>FINDINGS</domain>
<rulexquery><![CDATA[
(: Rule SE0009: Missing values for both --DTC and --DY variables :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/'
let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: iterate over all the FINDINGS datasets :)
for $dataset in $definedoc//odm:ItemGroupDef[upper-case(@def:Class)='FINDINGS' or upper-case(./def21:Class/@Name)='FINDINGS']
    let $name := $dataset/@Name
    let $datasetname := (
		if($defineversion = '2.1') then $dataset/def21:leaf/@xlink:href
		else $dataset/def:leaf/@xlink:href
	)
    let $datasetdoc := (
		if($datasetname) then doc(concat($base,$datasetname))
		else ()
	)
    (: get the OIDs of the --DTC and --DY variables.
    IMPORTANT: we currently assume that this is about xxDTC and xxDY variables, 
    so NOT about --STDTC and --STDY variables :)
    (: Find the OID of the --STRESN variable  :)
    let $dtcoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'DTC') and string-length(@Name)=5]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
    )
    let $dyoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'DY') and string-length(@Name)=4]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
    )
    (: and their names (for reporting) :)
    let $dtcname := $definedoc//odm:ItemDef[@OID=$dtcoid]/@Name
    let $dyname := $definedoc//odm:ItemDef[@OID=$dyoid]/@Name
        (: now iterate over all the records in the dataset :)
    for $record in $datasetdoc//odm:ItemGroupData
        let $recnum := $record/@data:ItemGroupDataSeq
        (: and give an warning when both --DTC and -- values are absent :)
        where not($record[odm:ItemData[@ItemOID=$dtcoid]/@Value or odm:ItemData[@ItemOID=$dyoid]/@Value])
        return <warning rule="SE0009" rulelastupdate="2020-08-08" dataset="{data($name)}" variable="{data($dtcname)}" recordnumber="{data($recnum)}">Missing values for both {data($dtcname)} and {data($dyname)} variables</warning>
]]>
</rulexquery>
</sdsrule>

<sdsrule id="SE0009-SD" last-update="2020-08-08" originator="FDA" standard="SDTM" requiresDomainOrDataset="Yes">
<ruledescription>--DTC or --DY must be populated</ruledescription>
<ruledetaileddescription>Start Date/Time of Observation (--DTC) or Study Day of Observation (--DY) should be populated in the Findings observation class where data are usually collected at multiple study days.</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>FINDINGS</domain>
<rulexquery><![CDATA[
(: Rule SE0009: Missing values for both --DTC and --DY variables :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
declare variable $datasetname external;
(: let $base := '/db/fda_submissions/cdisc01/'
let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: iterate over all the FINDINGS datasets :)
for $dataset in $definedoc//odm:ItemGroupDef[@Name=$datasetname][upper-case(@def:Class)='FINDINGS' or upper-case(./def21:Class/@Name)='FINDINGS']
    let $name := $dataset/@Name
    let $dsname := (
		if($defineversion = '2.1') then $dataset/def21:leaf/@xlink:href
		else $dataset/def:leaf/@xlink:href
	)
    let $datasetdoc := (
		if($dsname) then doc(concat($base,$dsname))
		else ()
	)
    (: get the OIDs of the --DTC and --DY variables.
    IMPORTANT: we currently assume that this is about xxDTC and xxDY variables, 
    so NOT about --STDTC and --STDY variables :)
    (: Find the OID of the --STRESN variable  :)
    let $dtcoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'DTC') and string-length(@Name)=5]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
    )
    let $dyoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'DY') and string-length(@Name)=4]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
    )
    (: and their names (for reporting) :)
    let $dtcname := $definedoc//odm:ItemDef[@OID=$dtcoid]/@Name
    let $dyname := $definedoc//odm:ItemDef[@OID=$dyoid]/@Name
        (: now iterate over all the records in the dataset :)
    for $record in $datasetdoc//odm:ItemGroupData
        let $recnum := $record/@data:ItemGroupDataSeq
        (: and give an warning when both --DTC and -- values are absent :)
        where not($record[odm:ItemData[@ItemOID=$dtcoid]/@Value or odm:ItemData[@ItemOID=$dyoid]/@Value])
        return <warning rule="SE0009" rulelastupdate="2020-08-08" dataset="{data($name)}" variable="{data($dtcname)}" recordnumber="{data($recnum)}">Missing values for both {data($dtcname)} and {data($dyname)} variables</warning>
]]>
</rulexquery>
</sdsrule>

<!-- Rule SD0062: "Study data must be provided in SAS XPORT v5 (.xpt) format" is not implemented here as we use the modern Dataset-XML format -->

<sdsrule id="SD0082" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>Exposure end date may not be after the latest Disposition date</ruledescription>
<ruledetaileddescription>End Date/Time of Treatment (EXENDTC) should be less than or equal to the Start Date/Time of the latest Disposition Event (DSSTDTC)</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>EX</domain>
<domain>DS</domain>
<rulexquery><![CDATA[
(: Rule SD0082: Exposure end date is after the latest Disposition date: 
End Date/Time of Treatment (EXENDTC) should be less than or equal to the Start Date/Time of the latest Disposition Event (DSSTDTC) :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
declare namespace xs="http://www.w3.org/2001/XMLSchema";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external;
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: Get the DS dataset :)
let $dsdatasetname := (
	if($defineversion = '2.1') then $definedoc//odm:ItemGroupDef[@Name='DS']/def21:leaf/@xlink:href
	else $definedoc//odm:ItemGroupDef[@Name='DS']/def:leaf/@xlink:href
)
let $dsdataset := concat($base,$dsdatasetname)
(: and the OID of the USUBJID :) 
let $dsusubjoid := (
    for $a in $definedoc//odm:ItemDef[@Name='USUBJID']/@OID 
    where $a = $definedoc//odm:ItemGroupDef[@Name='DS']/odm:ItemRef/@ItemOID
    return $a
)
let $dsstdtcoid := (
    for $a in $definedoc//odm:ItemDef[@Name='DSSTDTC']/@OID 
    where $a = $definedoc//odm:ItemGroupDef[@Name='DS']/odm:ItemRef/@ItemOID
    return $a
)
(: Get the EX dataset :)
let $exdatasetname := (
	if($defineversion = '2.1') then $definedoc//odm:ItemGroupDef[@Name='EX']/def21:leaf/@xlink:href
	else $definedoc//odm:ItemGroupDef[@Name='EX']/def:leaf/@xlink:href
)
let $exdataset := concat($base,$exdatasetname)
let $exusubjidoid := (
    for $a in $definedoc//odm:ItemDef[@Name='USUBJID']/@OID 
    where $a = $definedoc//odm:ItemGroupDef[@Name='EX']/odm:ItemRef/@ItemOID
    return $a
)
let $exendtcoid := $definedoc//odm:ItemDef[@Name='EXENDTC']/@OID
(: iterate over all the records in the EX dataset :)
for $exrecord in doc($exdataset)//odm:ItemGroupData
    let $recnum := $exrecord/@data:ItemGroupDataSeq
    (: get als well the USUBJID as the EXENDTC :)
    let $exusubjidvalue := $exrecord/odm:ItemData[@ItemOID=$exusubjidoid]/@Value
    let $exendtcvalue := $exrecord/odm:ItemData[@ItemOID=$exendtcoid]/@Value
    (: now get the latest (maximal) value DSSTDTC in the DS dataset for this subject :)
    (: let $lastdssdtcvalue := max(doc($dsdataset)//odm:ItemGroupData[odm:ItemData[@ItemOID=$dsusubjoid and @Value=$exusubjidvalue]]/odm:ItemData[@ItemOID=$dsstdtcoid]/@Value) :)
    let $dssdtcvalues := doc($dsdataset)//odm:ItemGroupData[odm:ItemData[@ItemOID=$dsusubjoid and @Value=$exusubjidvalue]]/odm:ItemData[@ItemOID=$dsstdtcoid]/@Value
    (: get the latest DSSTDTC date :)
    let $latestdate := max(for $date in $dssdtcvalues return xs:date($date))
    (: and report an error when DSSTDTC before EXENDTC  :)
    (: remark that we need to test first whether there is an actual EXENDTC value :)
    where $exendtcvalue and xs:date($latestdate) < xs:date($exendtcvalue) 
    return <warning rule="SD0082" dataset="EX" variable="EXENDTC" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">Exposure end date EXENDTC={data($exendtcvalue)} for USUBJID={data($exusubjidvalue)} is after last disposition date {data($latestdate)}</warning>
]]></rulexquery>
</sdsrule>

<sdsrule id="SD0012" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>--STDY may not be after --ENDY</ruledescription>
<ruledetaileddescription>Study Day of Start of Event, Exposure or Observation (--STDY) must be less or equal to Study Day of End of Event, Exposure or Observation (--ENDY)</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>INTERVENTIONS</domain>
<domain>EVENTS</domain>
<domain>FINDINGS</domain>
<rulexquery><![CDATA[
(: Rule SD0012 - --STDY is after --ENDY
Study Day of Start of Event, Exposure or Observation (--STDY) must be less or equal to Study Day of End of Event, Exposure or Observation (--ENDY)
:)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: iterate over all the datasets :)
for $dataset in $definedoc//odm:ItemGroupDef[upper-case(@def:Class)='INTERVENTIONS' or upper-case(@def:Class)='EVENTS' or upper-case(@def:Class)='FINDINGS' 
	or upper-case(./def21:Class/@Name)='INTERVENTIONS' or upper-case(./def21:Class/@Name)='EVENTS' or upper-case(./def21:Class/@Name)='FINDINGS'] 
    let $name := $dataset/@Name
    let $datasetname := (
		if($defineversion = '2.1') then $dataset/def21:leaf/@xlink:href
		else $dataset/def:leaf/@xlink:href
	)
    let $datasetdoc := (
		if($datasetname) then doc(concat($base,$datasetname))
		else ()
	)
    (: Get the OIDs of the --STDY and --ENDY variables :)
    let $stdyoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'STDY')]/@OID 
        where $a = $dataset/odm:ItemRef/@ItemOID
        return $a
    )
    let $stdyname :=  $definedoc//odm:ItemDef[@OID=$stdyoid]/@Name
    let $endyoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'ENDY')]/@OID 
        where $a = $dataset/odm:ItemRef/@ItemOID
        return $a
    )
    let $endyname :=  $definedoc//odm:ItemDef[@OID=$endyoid]/@Name
	(: iterate over all the records for which STDY and ENDY are present :)
    for $record in $datasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$stdyoid] and odm:ItemData[@ItemOID=$endyoid]]
    	let $recnum := $record/@data:ItemGroupDataSeq
    	(: get the values :)
    	let $stdyvalue := $record/odm:ItemData[@ItemOID=$stdyoid]/@Value
    	let $endyvalue := $record/odm:ItemData[@ItemOID=$endyoid]/@Value
    	(: and compare the values :)
    	where $stdyvalue castable as xs:double and $endyvalue castable as xs:double and number($stdyvalue) > number($endyvalue)
    	return <error rule="SD0012" dataset="{data($name)}" variable="{data($stdyname)}" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">{data($stdyname)}={data($stdyvalue)} is after {data($endyname)}={data($endyvalue)}</error>
]]></rulexquery>
</sdsrule>

<sdsrule id="SD0012-SD" last-update="2020-08-08" originator="FDA" standard="SDTM" requiresDomainOrDataset="Yes">
<ruledescription>--STDY may not be after --ENDY</ruledescription>
<ruledetaileddescription>Study Day of Start of Event, Exposure or Observation (--STDY) must be less or equal to Study Day of End of Event, Exposure or Observation (--ENDY)</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>INTERVENTIONS</domain>
<domain>EVENTS</domain>
<domain>FINDINGS</domain>
<rulexquery><![CDATA[
(: Rule SD0012 - --STDY is after --ENDY
Study Day of Start of Event, Exposure or Observation (--STDY) must be less or equal to Study Day of End of Event, Exposure or Observation (--ENDY)
:)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
declare variable $datasetname external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: iterate over all the datasets :)
for $dataset in $definedoc//odm:ItemGroupDef[@Name=$datasetname][upper-case(@def:Class)='INTERVENTIONS' or upper-case(@def:Class)='EVENTS' or upper-case(@def:Class)='FINDINGS' 
		or upper-case(./def21:Class/@Name)='INTERVENTIONS' or upper-case(./def21:Class/@Name)='EVENTS' or upper-case(./def21:Class/@Name)='FINDINGS'] 
    let $name := $dataset/@Name
    let $datasetname := (
		if($defineversion = '2.1') then $dataset/def21:leaf/@xlink:href
		else $dataset/def:leaf/@xlink:href
	)
    let $datasetdoc := (
		if($datasetname) then doc(concat($base,$datasetname))
		else ()
	)
    (: Get the OIDs of the --STDY and --ENDY variables :)
    let $stdyoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'STDY')]/@OID 
        where $a = $dataset/odm:ItemRef/@ItemOID
        return $a
    )
    let $stdyname :=  $definedoc//odm:ItemDef[@OID=$stdyoid]/@Name
    let $endyoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'ENDY')]/@OID 
        where $a = $dataset/odm:ItemRef/@ItemOID
        return $a
    )
    let $endyname :=  $definedoc//odm:ItemDef[@OID=$endyoid]/@Name
	(: iterate over all the records for which STDY and ENDY are present :)
    for $record in $datasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$stdyoid] and odm:ItemData[@ItemOID=$endyoid]]
    	let $recnum := $record/@data:ItemGroupDataSeq
    	(: get the values :)
    	let $stdyvalue := $record/odm:ItemData[@ItemOID=$stdyoid]/@Value
    	let $endyvalue := $record/odm:ItemData[@ItemOID=$endyoid]/@Value
    	(: and compare the values :)
    	where $stdyvalue castable as xs:double and $endyvalue castable as xs:double and number($stdyvalue) > number($endyvalue)
    	return <error rule="SD0012" dataset="{data($name)}" variable="{data($stdyname)}" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">{data($stdyname)}={data($stdyvalue)} is after {data($endyname)}={data($endyvalue)}</error>
]]></rulexquery>
</sdsrule>

<sdsrule id="SD0013" last-update="2020-08-08" originator="FDA" standard="SDTM">
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<ruledescription>--STDTC may not be after --ENDTC</ruledescription>
<ruledetaileddescription>Start Date/Time of Event, Exposure or Observation (--STDTC) must be less or equal to End Date/Time of Event, Exposure or Observation (--ENDTC)</ruledetaileddescription>
<domain>ALL</domain>
<rulexquery><![CDATA[
(: Rule SD0013 - --STDTC is after --ENDTC 
Start Date/Time of Event, Exposure or Observation (--STDTC) must be less or equal to End Date/Time of Event, Exposure or Observation (--ENDTC) 
:)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: iterate over all the datasets :)
for $dataset in $definedoc//odm:ItemGroupDef
    let $name := $dataset/@Name
    let $datasetname := (
		if($defineversion = '2.1') then $dataset/def21:leaf/@xlink:href
		else $dataset/def:leaf/@xlink:href
	)
    let $datasetdoc := (
		if($datasetname) then doc(concat($base,$datasetname))
		else ()
	)
    (: get the prefix for STDTC and ENDTC from either the domain or the dataset name :)
    let $prefix := if($dataset/@Domain) then $dataset/@Domain
        else substring($name,1,2)
    (: Get the OIDs of the --STDTC and --ENDTC variables :)
    (: We must iterate over all xxSTDTC and xxxSTDTC and xxxxSTDTC :)
    let $stdtcoids := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'STDTC')]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
    )
    for $stdtcoid in $stdtcoids
        let $stdtcname :=  $definedoc//odm:ItemDef[@OID=$stdtcoid]/@Name
        (: get the corresponding ENDTC :)
        let $endtcoid := (
            for $a in $definedoc//odm:ItemDef[@Name=replace($stdtcname,'STDTC','ENDTC')]/@OID 
            where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
            return $a
        )
        let $endtcname :=  $definedoc//odm:ItemDef[@OID=$endtcoid]/@Name
        (: now iterate over all the records in the dataset that do have an SDTY AND ENDY variable :)
        for $record in $datasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$stdtcoid] and odm:ItemData[@ItemOID=$endtcoid]]
            let $recnum := $record/@data:ItemGroupDataSeq
            (: get the values :)
            let $stdtcvalue := $record/odm:ItemData[@ItemOID=$stdtcoid]/@Value
            let $endtcvalue := $record/odm:ItemData[@ItemOID=$endtcoid]/@Value
            (: take care that both are datetime, as it can be that only one of the two is of datetime 
            and that the other is of type date, which would make them incomparable :)
            (: first for --STDTC  :)
            let $stdtctestvalue :=
                        (: only the year is given :)
                        if(string-length($stdtcvalue) = 4) then concat($stdtcvalue,'-01-01T00:00:00')
                        (: only the month is given :)
                        else if(string-length($stdtcvalue) = 7) then concat($stdtcvalue,'-01T00:00:00')
                        (: complete date is given but no time :)
                         else if(string-length($stdtcvalue) = 10) then concat($stdtcvalue,'T00:00:00')
                        (: T is given but no hours - might bei invalid anyway :)
                        else if(string-length($stdtcvalue) = 11) then concat($stdtcvalue,'00:00:00')
                        (: only hour is given :)
                        else if(string-length($stdtcvalue) = 13) then concat($stdtcvalue,':00:00')
                        (: hour and minutes are given, but no seconds :)
                        else if(string-length($stdtcvalue) = 16) then concat($stdtcvalue,':00')
                        (: all ok anyway :)
                        else ($stdtcvalue)
            (: and then for --ENDTC :)
            let $endtctestvalue :=
                        (: only the year is given :)
                        if(string-length($endtcvalue) = 4) then concat($endtcvalue,'-01-01T00:00:00')
                        (: only the month is given :)
                        else if(string-length($endtcvalue) = 7) then concat($endtcvalue,'-01T00:00:00')
                        (: complete date is given but no time :)
                         else if(string-length($endtcvalue) = 10) then concat($endtcvalue,'T00:00:00')
                        (: T is given but no hours - might bei invalid anyway :)
                        else if(string-length($endtcvalue) = 11) then concat($endtcvalue,'00:00:00')
                        (: only hour is given :)
                        else if(string-length($endtcvalue) = 13) then concat($endtcvalue,':00:00')
                        (: hour and minutes are given, but no seconds :)
                        else if(string-length($endtcvalue) = 16) then concat($endtcvalue,':00')
                        (: all ok anyway :)
                        else ($endtcvalue)
            (: ENDTC value must be equal or higher than STDTY - but first check whether it really is a datetime :)
            where $stdtctestvalue castable as xs:dateTime and $endtctestvalue castable as xs:dateTime and xs:dateTime($stdtctestvalue) > xs:dateTime($endtctestvalue) 
            return <error rule="SD0013" dataset="{data($name)}" variable="{data($stdtcname)}" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">{data($stdtcname)}={data($stdtcvalue)} is after {data($endtcname)}={data($endtcvalue)} in dataset {data($datasetname)}</error>	
]]></rulexquery>
</sdsrule>

<sdsrule id="SD0013-SD" last-update="2020-08-08" originator="FDA" standard="SDTM" requiresDomainOrDataset="Yes">
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<ruledescription>--STDTC may not be after --ENDTC</ruledescription>
<ruledetaileddescription>Start Date/Time of Event, Exposure or Observation (--STDTC) must be less or equal to End Date/Time of Event, Exposure or Observation (--ENDTC)</ruledetaileddescription>
<domain>ALL</domain>
<rulexquery><![CDATA[
(: Rule SD0013 - --STDTC is after --ENDTC 
Start Date/Time of Event, Exposure or Observation (--STDTC) must be less or equal to End Date/Time of Event, Exposure or Observation (--ENDTC) 
:)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external;
declare variable $defineversion external;
declare variable $datasetname external; 
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: iterate over all the datasets :)
for $dataset in $definedoc//odm:ItemGroupDef[@Name=$datasetname]
    let $name := $dataset/@Name
    let $dsname := (
		if($defineversion = '2.1') then $dataset/def21:leaf/@xlink:href
		else $dataset/def:leaf/@xlink:href
	)
    let $datasetlocation := concat($base,$dsname)
    (: get the prefix for STDTC and ENDTC from either the domain or the dataset name :)
    let $prefix := if($dataset/@Domain) then $dataset/@Domain
        else substring($name,1,2)
    (: Get the OIDs of the --STDTC and --ENDTC variables :)
    (: We must iterate over all xxSTDTC and xxxSTDTC and xxxxSTDTC :)
    let $stdtcoids := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'STDTC')]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
    )
    for $stdtcoid in $stdtcoids
        let $stdtcname :=  $definedoc//odm:ItemDef[@OID=$stdtcoid]/@Name
        (: get the corresponding ENDTC :)
        let $endtcoid := (
            for $a in $definedoc//odm:ItemDef[@Name=replace($stdtcname,'STDTC','ENDTC')]/@OID 
            where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
            return $a
        )
        let $endtcname :=  $definedoc//odm:ItemDef[@OID=$endtcoid]/@Name
        (: now iterate over all the records in the dataset that do have an SDTY AND ENDY variable :)
        for $record in doc($datasetlocation)//odm:ItemGroupData[odm:ItemData[@ItemOID=$stdtcoid] and odm:ItemData[@ItemOID=$endtcoid]]
            let $recnum := $record/@data:ItemGroupDataSeq
            (: get the values :)
            let $stdtcvalue := $record/odm:ItemData[@ItemOID=$stdtcoid]/@Value
            let $endtcvalue := $record/odm:ItemData[@ItemOID=$endtcoid]/@Value
            (: take care that both are datetime, as it can be that only one of the two is of datetime 
            and that the other is of type date, which would make them incomparable :)
            (: first for --STDTC  :)
            let $stdtctestvalue :=
                        (: only the year is given :)
                        if(string-length($stdtcvalue) = 4) then concat($stdtcvalue,'-01-01T00:00:00')
                        (: only the month is given :)
                        else if(string-length($stdtcvalue) = 7) then concat($stdtcvalue,'-01T00:00:00')
                        (: complete date is given but no time :)
                         else if(string-length($stdtcvalue) = 10) then concat($stdtcvalue,'T00:00:00')
                        (: T is given but no hours - might bei invalid anyway :)
                        else if(string-length($stdtcvalue) = 11) then concat($stdtcvalue,'00:00:00')
                        (: only hour is given :)
                        else if(string-length($stdtcvalue) = 13) then concat($stdtcvalue,':00:00')
                        (: hour and minutes are given, but no seconds :)
                        else if(string-length($stdtcvalue) = 16) then concat($stdtcvalue,':00')
                        (: all ok anyway :)
                        else ($stdtcvalue)
            (: and then for --ENDTC :)
            let $endtctestvalue :=
                        (: only the year is given :)
                        if(string-length($endtcvalue) = 4) then concat($endtcvalue,'-01-01T00:00:00')
                        (: only the month is given :)
                        else if(string-length($endtcvalue) = 7) then concat($endtcvalue,'-01T00:00:00')
                        (: complete date is given but no time :)
                         else if(string-length($endtcvalue) = 10) then concat($endtcvalue,'T00:00:00')
                        (: T is given but no hours - might bei invalid anyway :)
                        else if(string-length($endtcvalue) = 11) then concat($endtcvalue,'00:00:00')
                        (: only hour is given :)
                        else if(string-length($endtcvalue) = 13) then concat($endtcvalue,':00:00')
                        (: hour and minutes are given, but no seconds :)
                        else if(string-length($endtcvalue) = 16) then concat($endtcvalue,':00')
                        (: all ok anyway :)
                        else ($endtcvalue)
            where $stdtctestvalue castable as xs:dateTime and $endtctestvalue castable as xs:dateTime and xs:dateTime($stdtctestvalue) > xs:dateTime($endtctestvalue) 
            return <error rule="SD0013" dataset="{data($name)}" variable="{data($stdtcname)}" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">{data($stdtcname)}={data($stdtcvalue)} is after {data($endtcname)}={data($endtcvalue)} in dataset {data($datasetname)}</error>	
]]></rulexquery>
</sdsrule>

<sdsrule id="SD0025" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>--DTC may not be after --ENDTC</ruledescription>
<ruledetaileddescription>Date/Time of Specimen Collection (--DTC) must be less or equal to End Date/Time of Specimen Collection (--ENDTC)</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>FINDINGS</domain>
<rulexquery><![CDATA[
(: Rule SD0025 - --DTC is after --ENDTC 
Date/Time of Specimen Collection (--DTC) must be less or equal to End Date/Time of Specimen Collection (--ENDTC)
:)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' 
let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: iterate over all the FINDINGS datasets :)
for $dataset in $definedoc//odm:ItemGroupDef[upper-case(@def:Class)='FINDINGS' or upper-case(./def21:Class/@Name)='FINDINGS']
    let $name := $dataset/@Name
    let $dsname := (
		if($defineversion = '2.1') then $dataset/def21:leaf/@xlink:href
		else $dataset/def:leaf/@xlink:href
	)
    let $datasetdoc := (
		if($dsname) then doc(concat($base,$dsname))
		else ()
	)
    (: get the prefix for STDTC and ENDTC from either the domain or the dataset name :)
    let $prefix := if($dataset/@Domain) then $dataset/@Domain
        else substring($name,1,2)
    (: Get the OIDs of the --DTC and --ENDTC variables :)
    (: We must iterate over all xxSTDTC and xxxSTDTC and xxxxSTDTC :)
    let $dtcoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'DTC') and string-length(@Name)=5]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
    )
    let $dtcname :=  $definedoc//odm:ItemDef[@OID=$dtcoid]/@Name
    (: get the corresponding ENDTC :)
    let $endtcoid := (
        for $a in $definedoc//odm:ItemDef[@Name=replace($dtcname,'DTC','ENDTC')]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
    )
    let $endtcname :=  $definedoc//odm:ItemDef[@OID=$endtcoid]/@Name
    (: now iterate over all the records in the dataset that do have an SDTY AND ENDY variable :)
    for $record in $datasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$dtcoid] and odm:ItemData[@ItemOID=$endtcoid]]
        let $recnum := $record/@data:ItemGroupDataSeq
        (: get the values :)
        let $dtcvalue := $record/odm:ItemData[@ItemOID=$dtcoid]/@Value
        let $endtcvalue := $record/odm:ItemData[@ItemOID=$endtcoid]/@Value
            (: take care that both are datetime, as it can be that only one of the two is of datetime 
            and that the other is of type date, which would make them incomparable :)
            (: first for --STDTC  :)
            let $dtctestvalue :=
                (: only the year is given :)
                if(string-length($dtcvalue) = 4) then concat($dtcvalue,'-01-01T00:00:00')
                (: only the month is given :)
                else if(string-length($dtcvalue) = 7) then concat($dtcvalue,'-01T00:00:00')
                (: complete date is given but no time :)
                else if(string-length($dtcvalue) = 10) then concat($dtcvalue,'T00:00:00')
                        (: T is given but no hours - might bei invalid anyway :)
                else if(string-length($dtcvalue) = 11) then concat($dtcvalue,'00:00:00')
                (: only hour is given :)
                else if(string-length($dtcvalue) = 13) then concat($dtcvalue,':00:00')
                (: hour and minutes are given, but no seconds :)
                else if(string-length($dtcvalue) = 16) then concat($dtcvalue,':00')
                (: all ok anyway :)
                else ($dtcvalue)
            (: and then for --ENDTC :)
            let $endtctestvalue :=
                (: only the year is given :)
                if(string-length($endtcvalue) = 4) then concat($endtcvalue,'-01-01T00:00:00')
                (: only the month is given :)
                else if(string-length($endtcvalue) = 7) then concat($endtcvalue,'-01T00:00:00')
                (: complete date is given but no time :)
                else if(string-length($endtcvalue) = 10) then concat($endtcvalue,'T00:00:00')
                (: T is given but no hours - might bei invalid anyway :)
                else if(string-length($endtcvalue) = 11) then concat($endtcvalue,'00:00:00')
                (: only hour is given :)
                else if(string-length($endtcvalue) = 13) then concat($endtcvalue,':00:00')
                (: hour and minutes are given, but no seconds :)
                else if(string-length($endtcvalue) = 16) then concat($endtcvalue,':00')
                (: all ok anyway :)
                else ($endtcvalue)
            (: ENDTC value must be equal or higher than DTC - but first check whether it really is a datetime :)
            where $dtctestvalue castable as xs:dateTime and $endtctestvalue castable as xs:dateTime and xs:dateTime($dtctestvalue) > xs:dateTime($endtctestvalue) 
            return <error rule="SD0025" dataset="{data($name)}" variable="{data($dtcname)}" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">{data($dtcname)}={data($dtcvalue)} is after {data($endtcname)}={data($endtcvalue)} in dataset {data($name)}</error>	
]]>
</rulexquery>
</sdsrule>

<sdsrule id="SD0025-SD" last-update="2020-08-08" originator="FDA" standard="SDTM" requiresDomainOrDataset="Yes">
<ruledescription>--DTC may not be after --ENDTC</ruledescription>
<ruledetaileddescription>Date/Time of Specimen Collection (--DTC) must be less or equal to End Date/Time of Specimen Collection (--ENDTC)</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>FINDINGS</domain>
<rulexquery><![CDATA[
(: Rule SD0025 - --DTC is after --ENDTC 
Date/Time of Specimen Collection (--DTC) must be less or equal to End Date/Time of Specimen Collection (--ENDTC)
:)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
declare variable $datasetname external;
(: let $base := '/db/fda_submissions/cdisc01/' 
let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: iterate over all the provided FINDINGS datasets :)
for $dataset in $definedoc//odm:ItemGroupDef[@Name=$datasetname][upper-case(@def:Class)='FINDINGS' or upper-case(./def21:Class/@Name)='FINDINGS']
    let $name := $dataset/@Name
    let $dsname := (
		if($defineversion = '2.1') then $dataset/def21:leaf/@xlink:href
		else $dataset/def:leaf/@xlink:href
	)
    let $datasetlocation := concat($base,$dsname)
    (: get the prefix for STDTC and ENDTC from either the domain or the dataset name :)
    let $prefix := if($dataset/@Domain) then $dataset/@Domain
        else substring($name,1,2)
    (: Get the OIDs of the --DTC and --ENDTC variables :)
    (: We must iterate over all xxSTDTC and xxxSTDTC and xxxxSTDTC :)
    let $dtcoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'DTC') and string-length(@Name)=5]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
    )
    let $dtcname :=  $definedoc//odm:ItemDef[@OID=$dtcoid]/@Name
    (: get the corresponding ENDTC :)
    let $endtcoid := (
        for $a in $definedoc//odm:ItemDef[@Name=replace($dtcname,'DTC','ENDTC')]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
    )
    let $endtcname :=  $definedoc//odm:ItemDef[@OID=$endtcoid]/@Name
    (: now iterate over all the records in the dataset that do have an SDTY AND ENDY variable :)
    for $record in doc($datasetlocation)//odm:ItemGroupData[odm:ItemData[@ItemOID=$dtcoid] and odm:ItemData[@ItemOID=$endtcoid]]
        let $recnum := $record/@data:ItemGroupDataSeq
        (: get the values :)
        let $dtcvalue := $record/odm:ItemData[@ItemOID=$dtcoid]/@Value
        let $endtcvalue := $record/odm:ItemData[@ItemOID=$endtcoid]/@Value
            (: take care that both are datetime, as it can be that only one of the two is of datetime 
            and that the other is of type date, which would make them incomparable :)
            (: first for --STDTC  :)
            let $dtctestvalue :=
                (: only the year is given :)
                if(string-length($dtcvalue) = 4) then concat($dtcvalue,'-01-01T00:00:00')
                (: only the month is given :)
                else if(string-length($dtcvalue) = 7) then concat($dtcvalue,'-01T00:00:00')
                (: complete date is given but no time :)
                else if(string-length($dtcvalue) = 10) then concat($dtcvalue,'T00:00:00')
                        (: T is given but no hours - might bei invalid anyway :)
                else if(string-length($dtcvalue) = 11) then concat($dtcvalue,'00:00:00')
                (: only hour is given :)
                else if(string-length($dtcvalue) = 13) then concat($dtcvalue,':00:00')
                (: hour and minutes are given, but no seconds :)
                else if(string-length($dtcvalue) = 16) then concat($dtcvalue,':00')
                (: all ok anyway :)
                else ($dtcvalue)
            (: and then for --ENDTC :)
            let $endtctestvalue :=
                (: only the year is given :)
                if(string-length($endtcvalue) = 4) then concat($endtcvalue,'-01-01T00:00:00')
                (: only the month is given :)
                else if(string-length($endtcvalue) = 7) then concat($endtcvalue,'-01T00:00:00')
                (: complete date is given but no time :)
                else if(string-length($endtcvalue) = 10) then concat($endtcvalue,'T00:00:00')
                (: T is given but no hours - might bei invalid anyway :)
                else if(string-length($endtcvalue) = 11) then concat($endtcvalue,'00:00:00')
                (: only hour is given :)
                else if(string-length($endtcvalue) = 13) then concat($endtcvalue,':00:00')
                (: hour and minutes are given, but no seconds :)
                else if(string-length($endtcvalue) = 16) then concat($endtcvalue,':00')
                (: all ok anyway :)
                else ($endtcvalue)
            (: ENDTC value must be equal or higher than DTC - but first check whether it really is a datetime :)
            where $dtctestvalue castable as xs:dateTime and $endtctestvalue castable as xs:dateTime and xs:dateTime($dtctestvalue) > xs:dateTime($endtctestvalue) 
            return <error rule="SD0025" dataset="{data($name)}" variable="{data($dtcname)}" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">{data($dtcname)}={data($dtcvalue)} is after {data($endtcname)}={data($endtcvalue)} in dataset {data($name)}</error>	
]]>
</rulexquery>
</sdsrule>

<sdsrule id="SD1002" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>RFSTDTC may not be after RFENDTC</ruledescription>
<ruledetaileddescription>Subject Reference Start Date/Time (RFSTDTC) must be less than or equal to Subject Reference End Date/Time (RFENDTC)</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>DM</domain>
<rulexquery><![CDATA[
(: Rule SD1002 - RFSTDTC is after RFENDTC
Subject Reference Start Date/Time (RFSTDTC) must be less than or equal to Subject Reference End Date/Time (RFENDTC)
:)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: get the DM dataset :)
let $dmdataset := doc(concat($base,$define))//odm:ItemGroupDef[@Name='DM']
let $dmdatasetname := (
	if($defineversion = '2.1') then $dmdataset/def21:leaf/@xlink:href
	else $dmdataset/def:leaf/@xlink:href
)
let $dmdatasetlocation := concat($base,$dmdatasetname)
(: and get the OIDs of USUBJID :)
let $usubjidoid := (
    for $a in $definedoc//odm:ItemDef[@Name='USUBJID']/@OID 
    where $a = $definedoc//odm:ItemGroupDef[@Name='DM']/odm:ItemRef/@ItemOID
    return $a
)
(: and the OIDs for RFSTDTC RFENDTC :)
let $rfstdtcoid := (
    for $a in $definedoc//odm:ItemDef[@Name='RFSTDTC']/@OID 
    where $a = $definedoc//odm:ItemGroupDef[@Name='DM']/odm:ItemRef/@ItemOID
    return $a
)
let $rfendtcoid := (
    for $a in $definedoc//odm:ItemDef[@Name='RFENDTC']/@OID 
    where $a = $definedoc//odm:ItemGroupDef[@Name='DM']/odm:ItemRef/@ItemOID
    return $a   
)
(: iterate over all records in the DM dataset :)
for $record in doc($dmdatasetlocation)//odm:ItemGroupData
    let $recnum := $record/@data:ItemGroupDataSeq
    (: and get the values for USUBJID and ARMCD and RFSTDTC :)
    let $rfstdtcvalue := $record/odm:ItemData[@ItemOID=$rfstdtcoid]/@Value
    let $rfendtcvalue := $record/odm:ItemData[@ItemOID=$rfendtcoid]/@Value
    let $usubjidvalue := $record/odm:ItemData[@ItemOID=$usubjidoid]/@Value
    (: except for the case that ARMCD=SCRNFAIL or NOTASSGN, there MUST be an RFSTDTC :)
    (: convert RFSTDTC and RFENDTC to a datetime for the case that one of both is a datetime 
    (or partial) and the other is not making them essentally incomparable :)
    let $rfstdtctestvalue :=
                (: only the year is given :)
                if(string-length($rfstdtcvalue) = 4) then concat($rfstdtcvalue,'-01-01T00:00:00')
                (: only the month is given :)
                else if(string-length($rfstdtcvalue) = 7) then concat($rfstdtcvalue,'-01T00:00:00')
                (: complete date is given but no time :)
                 else if(string-length($rfstdtcvalue) = 10) then concat($rfstdtcvalue,'T00:00:00')
                (: T is given but no hours - might bei invalid anyway :)
                else if(string-length($rfstdtcvalue) = 11) then concat($rfstdtcvalue,'00:00:00')
                (: only hour is given :)
                else if(string-length($rfstdtcvalue) = 13) then concat($rfstdtcvalue,':00:00')
                (: hour and minutes are given, but no seconds :)
                else if(string-length($rfstdtcvalue) = 16) then concat($rfstdtcvalue,':00')
                (: all ok anyway :)
                else ($rfstdtcvalue)
    (: and then for --ENDTC :)
    let $endtctestvalue :=
                (: only the year is given :)
                if(string-length($rfendtcvalue) = 4) then concat($rfendtcvalue,'-01-01T00:00:00')
                (: only the month is given :)
                else if(string-length($rfendtcvalue) = 7) then concat($rfendtcvalue,'-01T00:00:00')
                (: complete date is given but no time :)
                 else if(string-length($rfendtcvalue) = 10) then concat($rfendtcvalue,'T00:00:00')
                (: T is given but no hours - might bei invalid anyway :)
                else if(string-length($rfendtcvalue) = 11) then concat($rfendtcvalue,'00:00:00')
                (: only hour is given :)
                else if(string-length($rfendtcvalue) = 13) then concat($rfendtcvalue,':00:00')
                (: hour and minutes are given, but no seconds :)
                else if(string-length($rfendtcvalue) = 16) then concat($rfendtcvalue,':00')
                (: all ok anyway :)
                else ($rfendtcvalue) 
    where ($rfstdtctestvalue castable as xs:dateTime) and ($rfstdtctestvalue castable as xs:dateTime) and xs:dateTime($rfstdtctestvalue) >  xs:dateTime($endtctestvalue)  
    return <error rule="SD1002" dataset="DM" variable="RFSTDTC" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">RFSTDTC={data($rfstdtcvalue)} is after RFENDTC={data($rfendtcvalue)} for subject with USUBJID={data($usubjidvalue)}</error>
]]></rulexquery>
</sdsrule>


<sdsrule id="SD1087" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>--STDY variable must be present, when --STDTC variable is present</ruledescription>
<ruledetaileddescription>Study Day of Start (--STDY) variable should be included into dataset, when Start Study Date/Time (--STDTC) variable is present</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>INTERVENTIONS</domain>
<domain>EVENTS</domain>
<domain>FINDINGS</domain>
<domain>SV</domain>
<domain>SE</domain>
<rulexquery><![CDATA[	
(: Rule SD1087 - Missing --STDY variable, when --STDTC variable is present:
Study Day of Start (--STDY) variable should be included into dataset, when Start Study Date/Time (--STDTC) variable is present
:)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: iterate over all SE, SV, INTERVENTIONS, EVENTS and FINDINGS datasets:)
for $dataset in $definedoc//odm:ItemGroupDef[@Name='SV' or @Name='SE' or upper-case(@def:Class)='INTERVENTIONS' or upper-case(@def:Class)='EVENTS' or upper-case(@def:Class)='FINDINGS' 
		or upper-case(./def21:Class/@Name)='INTERVENTIONS' or upper-case(./def21:Class/@Name)='EVENTS' or upper-case(./def21:Class/@Name)='FINDINGS']
    let $name := $dataset/@Name
    let $datasetname := (
		if($defineversion = '2.1') then $dataset/def21:leaf/@xlink:href
		else $dataset/def:leaf/@xlink:href
	)
    let $datasetlocation := concat($base,$datasetname)
    (: Get the OIDs of the SDTY and STDTC variable :)
    let $stdyoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'STDY')]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a  
    )
    let $stdyname := concat($name,'STDY')   (: as in worst case, --SDTY may not be in dataset at all  :)
    let $stdtcoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'STDTC')]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a  
    )
    let $stdtcname := $definedoc//odm:ItemDef[@OID=$stdtcoid]/@Name
    (: STDY variable must be present when STDTC is present :)
    where $stdtcname and not($stdyoid)
    return <error rule="SD1087" variable="{data($stdyname)}" dataset="{data($name)}" rulelastupdate="2020-08-08">Missing {data($stdyname)} variable in dataset {data($datasetname)}, when {data($stdtcname)} variable is present</error>	
]]></rulexquery>
</sdsrule>

<sdsrule id="SD1091" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>--ENDY variable must be present, when --ENDTC variable is present</ruledescription>
<ruledetaileddescription>Study Day of End (--ENDY) variable should be included into dataset, when End Study Date/Time (--ENDTC) variable is present</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>INTERVENTIONS</domain>
<domain>EVENTS</domain>
<domain>FINDINGS</domain>
<domain>SV</domain>
<domain>SE</domain>
<rulexquery><![CDATA[	
(: Rule SD1091 - Missing --ENDY variable, when --ENDTC variable is present
Study Day of End (--ENDY) variable should be included into dataset, when End Study Date/Time (--ENDTC) variable is present
:)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: iterate over all SE, SV, INTERVENTIONS, EVENTS and FINDINGS datasets :)
for $dataset in $definedoc//odm:ItemGroupDef[@Name='SV' or @Name='SE' or upper-case(@def:Class)='INTERVENTIONS' or upper-case(@def:Class)='EVENTS' or upper-case(@def:Class)='FINDINGS' 
		or upper-case(./def21:Class/@Name)='INTERVENTIONS' or upper-case(./def21:Class/@Name)='EVENTS' or upper-case(./def21:Class/@Name)='FINDINGS']
    let $name := $dataset/@Name
	let $datasetname := (
		if($defineversion = '2.1') then $dataset/def21:leaf/@xlink:href
		else $dataset/def:leaf/@xlink:href
	)
    let $datasetlocation := concat($base,$datasetname)
    (: Get the OIDs of the ENTY and STDTC variable :)
    let $endyoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'ENDY')]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a  
    )
    let $endyname := concat($name,'ENDY')   (: as in worst case, --SDTY may not be in dataset at all  :)
    let $endtcoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'ENDTC')]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a  
    )
    let $endtcname := $definedoc//odm:ItemDef[@OID=$endtcoid]/@Name
    (: STDY variable must be present when STDTC is present :)
    where $endtcname and not($endyoid)
    return <error rule="SD1091" dataset="{data($name)}" variable="{data($endyname)}" rulelastupdate="2020-08-08">Missing {data($endyname)} variable in dataset {data($datasetname)}, when {data($endtcname)} variable is present</error>
]]></rulexquery>
</sdsrule>


<sdsrule id="SD0080" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>AE start date may not be after the latest Disposition date</ruledescription>
<ruledetaileddescription>Start Date/Time of Adverse Event (AESTDTC) should be less than or equal to the Start Date/Time of the latest Disposition Event (DSSTDTC)</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>AE</domain>
<rulexquery><![CDATA[	
(: Rule SD0080 - AE start date is after the latest Disposition date:
Start Date/Time of Adverse Event (AESTDTC) should be less than or equal to the Start Date/Time of the latest Disposition Event (DSSTDTC) :)
(: TODO: extensive testing - can we make this faster? DS is queried (too) many times :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
declare namespace xs="http://www.w3.org/2001/XMLSchema";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define)) 
(: get the location of the DS dataset and the OID for the DSSTDTC and USUBJID attributes :)
let $dsdataset := $definedoc//odm:ItemGroupDef[@Name='DS']
let $dsdatasetname := (
	if($defineversion = '2.1') then $dsdataset/def21:leaf/@xlink:href
	else $dsdataset/def:leaf/@xlink:href
)
let $dsdatasetlocation := concat($base,$dsdatasetname)
let $dsstdtcoid := (
    for $a in $definedoc//odm:ItemDef[@Name='DSSTDTC']/@OID 
    where $a = $definedoc//odm:ItemGroupDef[@Name='DS']/odm:ItemRef/@ItemOID
    return $a
)
let $dsusubjidoid := (
    for $a in $definedoc//odm:ItemDef[@Name='USUBJID']/@OID 
    where $a = $definedoc//odm:ItemGroupDef[@Name='DS']/odm:ItemRef/@ItemOID
    return $a
)
(: Get the AE dataset(s) :)
for $aedataset in $definedoc//odm:ItemGroupDef[@Domain='AE']
    let $aename := $aedataset/@Name
    let $aedatasetname := (
		if($defineversion = '2.1') then $aedataset/def21:leaf/@xlink:href
		else $aedataset/def:leaf/@xlink:href
	)
    let $aedatasetlocation := concat($base,$aedatasetname)
    (: and get the OID of AESTDC and USUBJID :)
    let $aestdtcoid := (
        for $a in $definedoc//odm:ItemDef[@Name='AESTDTC']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$aename]/odm:ItemRef/@ItemOID
        return $a
    )
    let $aeusubjidoid := (
        for $a in $definedoc//odm:ItemDef[@Name='USUBJID']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$aename]/odm:ItemRef/@ItemOID
        return $a
    )
    (: iterate over all records in the AE dataset(s) that have AESTDTC populated :)
    for $record in doc($aedatasetlocation)//odm:ItemGroupData[odm:ItemData[@ItemOID=$aestdtcoid]]
        let $recnum := $record/@data:ItemGroupDataSeq
        (: get the value of AESTDTC :)
        let $aestdtcvalue := $record/odm:ItemData[@ItemOID=$aestdtcoid]/@Value
        let $aeusubjidvalue := $record/odm:ItemData[@ItemOID=$aeusubjidoid]/@Value
        (: now get the latest (maximal) value DSSTDTC in the DS dataset for this subject :)
        let $dssdtcvalues := doc($dsdatasetlocation)//odm:ItemGroupData[odm:ItemData[@ItemOID=$dsusubjidoid and @Value=$aeusubjidvalue]]/odm:ItemData[@ItemOID=$dsstdtcoid]/@Value
        (: get the latest DSSTDTC date :)
        let $latestdate := max(for $date in $dssdtcvalues return xs:date($date))
        (: AESTDTC must be BEFORE the latest DSSTDTC :)
        where $aestdtcvalue and xs:date($latestdate) < xs:date($aestdtcvalue) 
        return <error rule="SD0080" dataset="AE" variable="AESTDTC" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">AE start date, value={data($aestdtcvalue)} is after the latest Disposition date, value={data($latestdate)}</error>
]]></rulexquery>
</sdsrule>

<sdsrule id="SD1202" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>--STDTC date may not be after RFPENDTC</ruledescription>
<ruledetaileddescription>Start Date/Time (--STDTC) variable value must be less than or equal to Date/Time of End of Participation (RFPENDTC)</ruledetaileddescription>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>INTERVENTIONS</domain>
<domain>EVENTS</domain>
<domain>FINDINGS</domain>
<domain>DM</domain>
<domain>SE</domain>
<domain>SV</domain>
<rulexquery><![CDATA[
(: Rule SD1202: --STDTC date is after RFPENDTC :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external;
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' 
let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: get the DM dataset and the OID of DM.RFPENDTC (datetime of end of participation) :)
let $dmdef := $definedoc//odm:ItemGroupDef[@Name='DM']
(: and get the location and the DM document itself :)
let $dmlocation := (
	if($defineversion = '2.1') then $dmdef/def21:leaf/@xlink:href
	else $dmdef/def:leaf/@xlink:href
)
let $dmdoc := doc(concat($base,$dmlocation))
(: we also need the OID of USUBJID in DM :)
let $dmusubjidoid := (
    for $a in $definedoc//odm:ItemDef[@Name='USUBJID']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name='DM']/odm:ItemRef/@ItemOID
        return $a
)
(: and of RFPENDTC :)
let $rfpendtcoid := (
    for $a in $definedoc//odm:ItemDef[@Name='RFPENDTC']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name='DM']/odm:ItemRef/@ItemOID
        return $a
)
(: create a temporary structure containing USUBJID and RFPENDTC :)
let $usubjidrfpendtcpairs := (
    for $record in $dmdoc//odm:ItemGroupData
        let $usubjid := $record/odm:ItemData[@ItemOID=$dmusubjidoid]/@Value
        let $rfpendtc := $record/odm:ItemData[@ItemOID=$rfpendtcoid]/@Value
    return <record usubjid="{$usubjid}" rfpendtc="{$rfpendtc}" />
)
(: iterate over all INTERVENTIONS, EVENTS, FINDINGS, DM, SE, SV datasets :)
for $datasetdef in $definedoc//odm:ItemGroupDef[@Name='DM' or @Name='SE' or @Name='SV' or upper-case(@def:Class)='INTERVENTIONS' or upper-case(@def:Class)='EVENTS' or upper-case(@def:Class)='FINDINGS' 
	or upper-case(./def21:Class/@Name)='INTERVENTIONS' or upper-case(./def21:Class/@Name)='EVENTS' or upper-case(./def21:Class/@Name)='FINDINGS']
    (: get the dataset name :)
    let $name := $datasetdef/@Name
	let $domain := $datasetdef/@Domain
    (: get the datasetlocation :)
    let $datasetlocation := (
		if($defineversion = '2.1') then $datasetdef/def21:leaf/@xlink:href
		else $datasetdef/def:leaf/@xlink:href
	)
	let $datasetdoc := (
		if($datasetlocation) then doc(concat($base,$datasetlocation))
		else ()
	)
    (: we need the OID of USUBJID and of the --STDTC variable :)
    let $stdtcvarname := concat($domain,'STDTC')
    let $stdtcoid := (
        for $a in $definedoc//odm:ItemDef[@Name=$stdtcvarname]/@OID 
        where $a = $datasetdef/odm:ItemRef/@ItemOID
        return $a
    )
    let $usubjidoid := (
        for $a in $definedoc//odm:ItemDef[@Name='USUBJID']/@OID 
        where $a = $datasetdef/odm:ItemRef/@ItemOID
        return $a
    )
    (: now iterate over all records that do have a value for --STDTC :)
    for $record in $datasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$stdtcoid]/@Value]
        let $recnum := $record/@data:ItemGroupDataSeq
        (: get the value of --STDTC and of USUBJID :)
        let $usubjidvalue := $record/odm:ItemData[@ItemOID=$usubjidoid]/@Value
        let $stdtcvalue := $record/odm:ItemData[@ItemOID=$stdtcoid]/@Value
        (: get the value of RFPENDTC in DM :)
        let $rfpendtcvalue := $usubjidrfpendtcpairs[@usubjid=$usubjidvalue]/@rfpendtc
        (: take care of incomplete dates :)
        let $stdtctestvalue :=
                (: only the year is given :)
                if(string-length($stdtcvalue) = 4) then concat($stdtcvalue,'-01-01T00:00:00')
                (: only the month is given :)
                else if(string-length($stdtcvalue) = 7) then concat($stdtcvalue,'-01T00:00:00')
                (: complete date is given but no time :)
                else if(string-length($stdtcvalue) = 10) then concat($stdtcvalue,'T00:00:00')
                        (: T is given but no hours - might bei invalid anyway :)
                else if(string-length($stdtcvalue) = 11) then concat($stdtcvalue,'00:00:00')
                (: only hour is given :)
                else if(string-length($stdtcvalue) = 13) then concat($stdtcvalue,':00:00')
                (: hour and minutes are given, but no seconds :)
                else if(string-length($stdtcvalue) = 16) then concat($stdtcvalue,':00')
                (: all ok anyway :)
                else ($stdtcvalue)
        (: and also for RFPENDTC :)
        let $rfpendtctestvalue := (
            (: only the year is given :)
                if(string-length($rfpendtcvalue) = 4) then concat($rfpendtcvalue,'-01-01T00:00:00')
                (: only the month is given :)
                else if(string-length($rfpendtcvalue) = 7) then concat($rfpendtcvalue,'-01T00:00:00')
                (: complete date is given but no time :)
                else if(string-length($rfpendtcvalue) = 10) then concat($rfpendtcvalue,'T00:00:00')
                        (: T is given but no hours - might bei invalid anyway :)
                else if(string-length($rfpendtcvalue) = 11) then concat($rfpendtcvalue,'00:00:00')
                (: only hour is given :)
                else if(string-length($rfpendtcvalue) = 13) then concat($rfpendtcvalue,':00:00')
                (: hour and minutes are given, but no seconds :)
                else if(string-length($rfpendtcvalue) = 16) then concat($rfpendtcvalue,':00')
                (: all ok anyway :)
                else ($rfpendtcvalue)
        )
        (: compare both values :)
        (: RFPENDTC value must be equal or higher than STDTC - but first check whether it really is a datetime :)
            where $stdtctestvalue castable as xs:dateTime and $rfpendtctestvalue castable as xs:dateTime and xs:dateTime($stdtctestvalue) > xs:dateTime($rfpendtctestvalue) 
            return <error rule="SD1202" dataset="{data($name)}" variable="{data($stdtcvarname)}" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">{data($stdtcvarname)}={data($stdtcvalue)} is after RFPENDTC={data($rfpendtcvalue)} in dataset {data($name)}</error>	
]]>
</rulexquery>
</sdsrule>

<sdsrule id="SD1202-SD" last-update="2020-08-08" originator="FDA" standard="SDTM" requiresDomainOrDataset="Yes">
<ruledescription>--STDTC date may not be after RFPENDTC</ruledescription>
<ruledetaileddescription>Start Date/Time (--STDTC) variable value must be less than or equal to Date/Time of End of Participation (RFPENDTC)</ruledetaileddescription>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>INTERVENTIONS</domain>
<domain>EVENTS</domain>
<domain>FINDINGS</domain>
<domain>DM</domain>
<domain>SE</domain>
<domain>SV</domain>
<rulexquery><![CDATA[
(: Rule SD1202: --STDTC date is after RFPENDTC :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external;
declare variable $defineversion external;
declare variable $datasetname external;
(: let $base := '/db/fda_submissions/cdisc01/' 
let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: get the DM dataset and the OID of DM.RFPENDTC (datetime of end of participation) :)
let $dmdef := $definedoc//odm:ItemGroupDef[@Name='DM']
(: and get the location and the DM document itself :)
let $dmlocation := ( 
	if($defineversion = '2.1') then $dmdef/def21:leaf/@xlink:href
	else $dmdef/def:leaf/@xlink:href
)
let $dmdoc := doc(concat($base,$dmlocation))
(: we also need the OID of USUBJID in DM :)
let $dmusubjidoid := (
    for $a in $definedoc//odm:ItemDef[@Name='USUBJID']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name='DM']/odm:ItemRef/@ItemOID
        return $a
)
(: and of RFPENDTC :)
let $rfpendtcoid := (
    for $a in $definedoc//odm:ItemDef[@Name='RFPENDTC']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name='DM']/odm:ItemRef/@ItemOID
        return $a
)
(: create a temporary structure containing USUBJID and RFPENDTC :)
let $usubjidrfpendtcpairs := (
    for $record in $dmdoc//odm:ItemGroupData
        let $usubjid := $record/odm:ItemData[@ItemOID=$dmusubjidoid]/@Value
        let $rfpendtc := $record/odm:ItemData[@ItemOID=$rfpendtcoid]/@Value
    return <record usubjid="{$usubjid}" rfpendtc="{$rfpendtc}" />
)
(: iterate over the provided dataset in classes INTERVENTIONS, EVENTS, FINDINGS, DM, SE, SV datasets :)
for $datasetdef in $definedoc//odm:ItemGroupDef[@Name=$datasetname][upper-case(@def:Class)='INTERVENTIONS' or upper-case(@def:Class)='EVENTS' or upper-case(@def:Class)='FINDINGS' 
		or upper-case(./def21:Class/@Name)='INTERVENTIONS' or upper-case(./def21:Class/@Name)='EVENTS' or upper-case(./def21:Class/@Name)='FINDINGS']
    (: get the dataset name :)
    let $name := $datasetdef/@Name
	let $domain := $datasetdef/@Domain
    (: get the datasetlocation :)
    let $datasetlocation := (
		if($defineversion = '2.1') then $datasetdef/def21:leaf/@xlink:href
		else $datasetdef/def:leaf/@xlink:href
	)
	let $datasetdoc := (
		if($datasetlocation) then doc(concat($base,$datasetlocation))
		else ()
	)
    (: we need the OID of USUBJID and of the --STDTC variable :)
    let $stdtcvarname := concat($domain,'STDTC')
    let $stdtcoid := (
        for $a in $definedoc//odm:ItemDef[@Name=$stdtcvarname]/@OID 
        where $a = $datasetdef/odm:ItemRef/@ItemOID
        return $a
    )
    let $usubjidoid := (
        for $a in $definedoc//odm:ItemDef[@Name='USUBJID']/@OID 
        where $a = $datasetdef/odm:ItemRef/@ItemOID
        return $a
    )
    (: now iterate over all records that do have a value for --STDTC :)
    for $record in $datasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$stdtcoid]/@Value]
        let $recnum := $record/@data:ItemGroupDataSeq
        (: get the value of --STDTC and of USUBJID :)
        let $usubjidvalue := $record/odm:ItemData[@ItemOID=$usubjidoid]/@Value
        let $stdtcvalue := $record/odm:ItemData[@ItemOID=$stdtcoid]/@Value
        (: get the value of RFPENDTC in DM :)
        let $rfpendtcvalue := $usubjidrfpendtcpairs[@usubjid=$usubjidvalue]/@rfpendtc
        (: take care of incomplete dates :)
        let $stdtctestvalue :=
                (: only the year is given :)
                if(string-length($stdtcvalue) = 4) then concat($stdtcvalue,'-01-01T00:00:00')
                (: only the month is given :)
                else if(string-length($stdtcvalue) = 7) then concat($stdtcvalue,'-01T00:00:00')
                (: complete date is given but no time :)
                else if(string-length($stdtcvalue) = 10) then concat($stdtcvalue,'T00:00:00')
                        (: T is given but no hours - might bei invalid anyway :)
                else if(string-length($stdtcvalue) = 11) then concat($stdtcvalue,'00:00:00')
                (: only hour is given :)
                else if(string-length($stdtcvalue) = 13) then concat($stdtcvalue,':00:00')
                (: hour and minutes are given, but no seconds :)
                else if(string-length($stdtcvalue) = 16) then concat($stdtcvalue,':00')
                (: all ok anyway :)
                else ($stdtcvalue)
        (: and also for RFPENDTC :)
        let $rfpendtctestvalue := (
            (: only the year is given :)
                if(string-length($rfpendtcvalue) = 4) then concat($rfpendtcvalue,'-01-01T00:00:00')
                (: only the month is given :)
                else if(string-length($rfpendtcvalue) = 7) then concat($rfpendtcvalue,'-01T00:00:00')
                (: complete date is given but no time :)
                else if(string-length($rfpendtcvalue) = 10) then concat($rfpendtcvalue,'T00:00:00')
                        (: T is given but no hours - might bei invalid anyway :)
                else if(string-length($rfpendtcvalue) = 11) then concat($rfpendtcvalue,'00:00:00')
                (: only hour is given :)
                else if(string-length($rfpendtcvalue) = 13) then concat($rfpendtcvalue,':00:00')
                (: hour and minutes are given, but no seconds :)
                else if(string-length($rfpendtcvalue) = 16) then concat($rfpendtcvalue,':00')
                (: all ok anyway :)
                else ($rfpendtcvalue)
        )
        (: compare both values :)
        (: RFPENDTC value must be equal or higher than STDTC - but first check whether it really is a datetime :)
            where $stdtctestvalue castable as xs:dateTime and $rfpendtctestvalue castable as xs:dateTime and xs:dateTime($stdtctestvalue) > xs:dateTime($rfpendtctestvalue) 
            return <error rule="SD1202" dataset="{data($name)}" variable="{data($stdtcvarname)}" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">{data($stdtcvarname)}={data($stdtcvalue)} is after RFPENDTC={data($rfpendtcvalue)} in dataset {data($name)}</error>	
]]>
</rulexquery>
</sdsrule>

<sdsrule id="SD1203" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>--DTC date may not be after RFPENDTC</ruledescription>
<ruledetaileddescription>Start Date/Time (--STDTC) variable value must be less than or equal to Date/Time of End of Participation (RFPENDTC)</ruledetaileddescription>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>INTERVENTIONS</domain>
<domain>EVENTS</domain>
<domain>FINDINGS</domain>
<domain>DM</domain>
<domain>SE</domain>
<domain>SV</domain>
<rulexquery><![CDATA[
(: Rule SD1203: --DTC date is after RFPENDTC :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external;
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' 
let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: get the DM dataset and the OID of DM.RFPENDTC (datetime of end of participation) :)
let $dmdef := $definedoc//odm:ItemGroupDef[@Name='DM']
(: and get the location and the DM document itself :)
let $dmlocation := (
	if($defineversion = '2.1') then $dmdef/def21:leaf/@xlink:href
	else $dmdef/def:leaf/@xlink:href
)
let $dmdoc := doc(concat($base,$dmlocation))
(: we also need the OID of USUBJID in DM :)
let $dmusubjidoid := (
    for $a in $definedoc//odm:ItemDef[@Name='USUBJID']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name='DM']/odm:ItemRef/@ItemOID
        return $a
)
(: and of RFPENDTC :)
let $rfpendtcoid := (
    for $a in $definedoc//odm:ItemDef[@Name='RFPENDTC']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name='DM']/odm:ItemRef/@ItemOID
        return $a
)
(: create a temporary structure containing USUBJID and RFPENDTC :)
let $usubjidrfpendtcpairs := (
    for $record in $dmdoc//odm:ItemGroupData
        let $usubjid := $record/odm:ItemData[@ItemOID=$dmusubjidoid]/@Value
        let $rfpendtc := $record/odm:ItemData[@ItemOID=$rfpendtcoid]/@Value
    return <record usubjid="{$usubjid}" rfpendtc="{$rfpendtc}" />
)
(: iterate over all INTERVENTIONS, EVENTS, FINDINGS, DM, SE, SV datasets :)
for $datasetdef in $definedoc//odm:ItemGroupDef[@Name='DM' or @Name='SE' or @Name='SV' or upper-case(@def:Class)='INTERVENTIONS' or upper-case(@def:Class)='EVENTS' or upper-case(@def:Class)='FINDINGS' 
		or upper-case(./def21:Class/@Name)='INTERVENTIONS' or upper-case(./def21:Class/@Name)='EVENTS' or upper-case(./def21:Class/@Name)='FINDINGS']
    (: get the dataset name :)
    let $name := $datasetdef/@Name
    let $domain := $datasetdef/@Domain
    (: get the datasetlocation :)
    let $datasetlocation := (
		if($defineversion = '2.1') then $datasetdef/def21:leaf/@xlink:href
		else $datasetdef/def:leaf/@xlink:href
	)
	let $datasetdoc := (
		if($datasetlocation) then doc(concat($base,$datasetlocation))
		else ()
	)
    (: we need the OID of USUBJID and of the --DTC variable :)
    let $dtcvarname := concat($domain,'DTC')
    let $dtcoid := (
        for $a in $definedoc//odm:ItemDef[@Name=$dtcvarname]/@OID 
        where $a = $datasetdef/odm:ItemRef/@ItemOID
        return $a
    )
    let $usubjidoid := (
        for $a in $definedoc//odm:ItemDef[@Name='USUBJID']/@OID 
        where $a = $datasetdef/odm:ItemRef/@ItemOID
        return $a
    )
    (: now iterate over all records that do have a value for --DTC :)
    for $record in $datasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$dtcoid]/@Value]
        let $recnum := $record/@data:ItemGroupDataSeq
        (: get the value of --STDTC and of USUBJID :)
        let $usubjidvalue := $record/odm:ItemData[@ItemOID=$usubjidoid]/@Value
        let $dtcvalue := $record/odm:ItemData[@ItemOID=$dtcoid]/@Value
        (: get the value of RFPENDTC in DM :)
        let $rfpendtcvalue := $usubjidrfpendtcpairs[@usubjid=$usubjidvalue]/@rfpendtc
        (: take care of incomplete dates :)
        let $dtctestvalue :=
                (: only the year is given :)
                if(string-length($dtcvalue) = 4) then concat($dtcvalue,'-01-01T00:00:00')
                (: only the month is given :)
                else if(string-length($dtcvalue) = 7) then concat($dtcvalue,'-01T00:00:00')
                (: complete date is given but no time :)
                else if(string-length($dtcvalue) = 10) then concat($dtcvalue,'T00:00:00')
                (: T is given but no hours - might bei invalid anyway :)
                else if(string-length($dtcvalue) = 11) then concat($dtcvalue,'00:00:00')
                (: only hour is given :)
                else if(string-length($dtcvalue) = 13) then concat($dtcvalue,':00:00')
                (: hour and minutes are given, but no seconds :)
                else if(string-length($dtcvalue) = 16) then concat($dtcvalue,':00')
                (: all ok anyway :)
                else ($dtcvalue)
        (: and also for RFPENDTC :)
        let $rfpendtctestvalue := (
            (: only the year is given :)
                if(string-length($rfpendtcvalue) = 4) then concat($rfpendtcvalue,'-01-01T00:00:00')
                (: only the month is given :)
                else if(string-length($rfpendtcvalue) = 7) then concat($rfpendtcvalue,'-01T00:00:00')
                (: complete date is given but no time :)
                else if(string-length($rfpendtcvalue) = 10) then concat($rfpendtcvalue,'T00:00:00')
                (: T is given but no hours - might bei invalid anyway :)
                else if(string-length($rfpendtcvalue) = 11) then concat($rfpendtcvalue,'00:00:00')
                (: only hour is given :)
                else if(string-length($rfpendtcvalue) = 13) then concat($rfpendtcvalue,':00:00')
                (: hour and minutes are given, but no seconds :)
                else if(string-length($rfpendtcvalue) = 16) then concat($rfpendtcvalue,':00')
                (: all ok anyway :)
                else ($rfpendtcvalue)
        )
        (: compare both values :)
        (: RFPENDTC value must be equal or higher than STDTC - but first check whether it really is a datetime :)
            where $dtctestvalue castable as xs:dateTime and $rfpendtctestvalue castable as xs:dateTime and xs:dateTime($dtctestvalue) > xs:dateTime($rfpendtctestvalue) 
            return <error rule="SD1203" dataset="{data($name)}" variable="{data($dtcvarname)}" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">{data($dtcvarname)}={data($dtcvalue)} is after RFPENDTC={data($rfpendtcvalue)} in dataset {data($name)}</error>	
]]>
</rulexquery>
</sdsrule>

<sdsrule id="SD1203-SD" last-update="2020-08-08" originator="FDA" standard="SDTM" requiresDomainOrDataset="Yes">
<ruledescription>--DTC date may not be after RFPENDTC</ruledescription>
<ruledetaileddescription>Start Date/Time (--STDTC) variable value must be less than or equal to Date/Time of End of Participation (RFPENDTC)</ruledetaileddescription>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>INTERVENTIONS</domain>
<domain>EVENTS</domain>
<domain>FINDINGS</domain>
<domain>DM</domain>
<domain>SE</domain>
<domain>SV</domain>
<rulexquery><![CDATA[
(: Rule SD1203: --DTC date is after RFPENDTC :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external;
declare variable $defineversion external;
declare variable $datasetname external;
(: let $base := '/db/fda_submissions/cdisc01/' 
let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: get the DM dataset and the OID of DM.RFPENDTC (datetime of end of participation) :)
let $dmdef := $definedoc//odm:ItemGroupDef[@Name='DM']
(: and get the location and the DM document itself :)
let $dmlocation := (
	if($defineversion = '2.1') then $dmdef/def21:leaf/@xlink:href
	else $dmdef/def:leaf/@xlink:href
)
let $dmdoc := doc(concat($base,$dmlocation))
(: we also need the OID of USUBJID in DM :)
let $dmusubjidoid := (
    for $a in $definedoc//odm:ItemDef[@Name='USUBJID']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name='DM']/odm:ItemRef/@ItemOID
        return $a
)
(: and of RFPENDTC :)
let $rfpendtcoid := (
    for $a in $definedoc//odm:ItemDef[@Name='RFPENDTC']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name='DM']/odm:ItemRef/@ItemOID
        return $a
)
(: create a temporary structure containing USUBJID and RFPENDTC :)
let $usubjidrfpendtcpairs := (
    for $record in $dmdoc//odm:ItemGroupData
        let $usubjid := $record/odm:ItemData[@ItemOID=$dmusubjidoid]/@Value
        let $rfpendtc := $record/odm:ItemData[@ItemOID=$rfpendtcoid]/@Value
    return <record usubjid="{$usubjid}" rfpendtc="{$rfpendtc}" />
)
(: iterate over the provided dataset within INTERVENTIONS, EVENTS, FINDINGS, DM, SE, SV datasets :)
for $datasetdef in $definedoc//odm:ItemGroupDef[@Name=$datasetname][upper-case(@def:Class)='INTERVENTIONS' or upper-case(@def:Class)='EVENTS' or upper-case(@def:Class)='FINDINGS' 
		or upper-case(./def21:Class/@Name)='INTERVENTIONS' or upper-case(./def21:Class/@Name)='EVENTS' or upper-case(./def21:Class/@Name)='FINDINGS']
    (: get the dataset name :)
    let $name := $datasetdef/@Name
    let $domain := $datasetdef/@Domain
    (: get the datasetlocation :)
    let $datasetlocation := (
		if($defineversion = '2.1') then $datasetdef/def21:leaf/@xlink:href
		else $datasetdef/def:leaf/@xlink:href
	)
    (: we need the OID of USUBJID and of the --DTC variable :)
    let $dtcvarname := concat($domain,'DTC')
    let $dtcoid := (
        for $a in $definedoc//odm:ItemDef[@Name=$dtcvarname]/@OID 
        where $a = $datasetdef/odm:ItemRef/@ItemOID
        return $a
    )
    let $usubjidoid := (
        for $a in $definedoc//odm:ItemDef[@Name='USUBJID']/@OID 
        where $a = $datasetdef/odm:ItemRef/@ItemOID
        return $a
    )
    (: now iterate over all records that do have a value for --DTC :)
    for $record in doc(concat($base,$datasetlocation))//odm:ItemGroupData[odm:ItemData[@ItemOID=$dtcoid]/@Value]
        let $recnum := $record/@data:ItemGroupDataSeq
        (: get the value of --STDTC and of USUBJID :)
        let $usubjidvalue := $record/odm:ItemData[@ItemOID=$usubjidoid]/@Value
        let $dtcvalue := $record/odm:ItemData[@ItemOID=$dtcoid]/@Value
        (: get the value of RFPENDTC in DM :)
        let $rfpendtcvalue := $usubjidrfpendtcpairs[@usubjid=$usubjidvalue]/@rfpendtc
        (: take care of incomplete dates :)
        let $dtctestvalue :=
                (: only the year is given :)
                if(string-length($dtcvalue) = 4) then concat($dtcvalue,'-01-01T00:00:00')
                (: only the month is given :)
                else if(string-length($dtcvalue) = 7) then concat($dtcvalue,'-01T00:00:00')
                (: complete date is given but no time :)
                else if(string-length($dtcvalue) = 10) then concat($dtcvalue,'T00:00:00')
                (: T is given but no hours - might bei invalid anyway :)
                else if(string-length($dtcvalue) = 11) then concat($dtcvalue,'00:00:00')
                (: only hour is given :)
                else if(string-length($dtcvalue) = 13) then concat($dtcvalue,':00:00')
                (: hour and minutes are given, but no seconds :)
                else if(string-length($dtcvalue) = 16) then concat($dtcvalue,':00')
                (: all ok anyway :)
                else ($dtcvalue)
        (: and also for RFPENDTC :)
        let $rfpendtctestvalue := (
            (: only the year is given :)
                if(string-length($rfpendtcvalue) = 4) then concat($rfpendtcvalue,'-01-01T00:00:00')
                (: only the month is given :)
                else if(string-length($rfpendtcvalue) = 7) then concat($rfpendtcvalue,'-01T00:00:00')
                (: complete date is given but no time :)
                else if(string-length($rfpendtcvalue) = 10) then concat($rfpendtcvalue,'T00:00:00')
                        (: T is given but no hours - might bei invalid anyway :)
                else if(string-length($rfpendtcvalue) = 11) then concat($rfpendtcvalue,'00:00:00')
                (: only hour is given :)
                else if(string-length($rfpendtcvalue) = 13) then concat($rfpendtcvalue,':00:00')
                (: hour and minutes are given, but no seconds :)
                else if(string-length($rfpendtcvalue) = 16) then concat($rfpendtcvalue,':00')
                (: all ok anyway :)
                else ($rfpendtcvalue)
        )
        (: compare both values :)
        (: RFPENDTC value must be equal or higher than STDTC - but first check whether it really is a datetime :)
            where $dtctestvalue castable as xs:dateTime and $rfpendtctestvalue castable as xs:dateTime and xs:dateTime($dtctestvalue) > xs:dateTime($rfpendtctestvalue) 
            return <error rule="SD1203" dataset="{data($name)}" variable="{data($dtcvarname)}" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">{data($dtcvarname)}={data($dtcvalue)} is after RFPENDTC={data($rfpendtcvalue)} in dataset {data($name)}</error>	
]]>
</rulexquery>
</sdsrule>

<sdsrule id="SD1204" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>--ENDTC date may not be after RFPENDTC</ruledescription>
<ruledetaileddescription>End Date/Time (--ENDTC) variable value must be less than or equal to Date/Time of End of Participation (RFPENDTC)</ruledetaileddescription>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>INTERVENTIONS</domain>
<domain>EVENTS</domain>
<domain>FINDINGS</domain>
<domain>DM</domain>
<domain>SE</domain>
<domain>SV</domain>
<rulexquery><![CDATA[
(: Rule SD1204: --ENDTC date is after RFPENDTC :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external;
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' 
let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: get the DM dataset and the OID of DM.RFPENDTC (datetime of end of participation) :)
let $dmdef := $definedoc//odm:ItemGroupDef[@Name='DM']
(: and get the location and the DM document itself :)
let $dmlocation := (
	if($defineversion = '2.1') then $dmdef/def21:leaf/@xlink:href
	else $dmdef/def:leaf/@xlink:href
)
let $dmdoc := doc(concat($base,$dmlocation))
(: we also need the OID of USUBJID in DM :)
let $dmusubjidoid := (
    for $a in $definedoc//odm:ItemDef[@Name='USUBJID']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name='DM']/odm:ItemRef/@ItemOID
        return $a
)
(: and of RFPENDTC :)
let $rfpendtcoid := (
    for $a in $definedoc//odm:ItemDef[@Name='RFPENDTC']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name='DM']/odm:ItemRef/@ItemOID
        return $a
)
(: create a temporary structure containing USUBJID and RFPENDTC :)
let $usubjidrfpendtcpairs := (
    for $record in $dmdoc//odm:ItemGroupData
        let $usubjid := $record/odm:ItemData[@ItemOID=$dmusubjidoid]/@Value
        let $rfpendtc := $record/odm:ItemData[@ItemOID=$rfpendtcoid]/@Value
    return <record usubjid="{$usubjid}" rfpendtc="{$rfpendtc}" />
)
(: iterate over all INTERVENTIONS, EVENTS, FINDINGS, DM, SE, SV datasets :)
for $datasetdef in $definedoc//odm:ItemGroupDef[@Name='DM' or @Name='SE' or @Name='SV' or upper-case(@def:Class)='INTERVENTIONS' or upper-case(@def:Class)='EVENTS' or upper-case(@def:Class)='FINDINGS' 
		or upper-case(./def21:Class/@Name)='INTERVENTIONS' or upper-case(./def21:Class/@Name)='EVENTS' or upper-case(./def21:Class/@Name)='FINDINGS']
    (: get the dataset name :)
    let $name := $datasetdef/@Name
    let $domain := $datasetdef/@Domain
    (: get the datasetlocation :)
    let $datasetlocation := (
		if($defineversion = '2.1') then $datasetdef/def21:leaf/@xlink:href
		else $datasetdef/def:leaf/@xlink:href
	)
	let $datasetdoc := (
		if($datasetlocation) then doc(concat($base,$datasetlocation))
		else ()
	)
    (: we need the OID of USUBJID and of the --ENDTC variable :)
    let $endtcvarname := concat($domain,'ENDTC')
    let $endtcoid := (
        for $a in $definedoc//odm:ItemDef[@Name=$endtcvarname]/@OID 
        where $a = $datasetdef/odm:ItemRef/@ItemOID
        return $a
    )
    let $usubjidoid := (
        for $a in $definedoc//odm:ItemDef[@Name='USUBJID']/@OID 
        where $a = $datasetdef/odm:ItemRef/@ItemOID
        return $a
    )
    (: now iterate over all records that do have a value for --STDTC :)
    for $record in $datasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$endtcoid]/@Value]
        let $recnum := $record/@data:ItemGroupDataSeq
        (: get the value of --ENDTC and of USUBJID :)
        let $usubjidvalue := $record/odm:ItemData[@ItemOID=$usubjidoid]/@Value
        let $endtcvalue := $record/odm:ItemData[@ItemOID=$endtcoid]/@Value
        (: get the value of RFPENDTC in DM from the temporary structure :)
        let $rfpendtcvalue := $usubjidrfpendtcpairs[@usubjid=$usubjidvalue]/@rfpendtc
        (: take care of incomplete dates :)
        let $endtctestvalue :=
                (: only the year is given :)
                if(string-length($endtcvalue) = 4) then concat($endtcvalue,'-01-01T00:00:00')
                (: only the month is given :)
                else if(string-length($endtcvalue) = 7) then concat($endtcvalue,'-01T00:00:00')
                (: complete date is given but no time :)
                else if(string-length($endtcvalue) = 10) then concat($endtcvalue,'T00:00:00')
                (: T is given but no hours - might bei invalid anyway :)
                else if(string-length($endtcvalue) = 11) then concat($endtcvalue,'00:00:00')
                (: only hour is given :)
                else if(string-length($endtcvalue) = 13) then concat($endtcvalue,':00:00')
                (: hour and minutes are given, but no seconds :)
                else if(string-length($endtcvalue) = 16) then concat($endtcvalue,':00')
                (: all ok anyway :)
                else ($endtcvalue)
        (: and also for RFPENDTC :)
        let $rfpendtctestvalue := (
            (: only the year is given :)
                if(string-length($rfpendtcvalue) = 4) then concat($rfpendtcvalue,'-01-01T00:00:00')
                (: only the month is given :)
                else if(string-length($rfpendtcvalue) = 7) then concat($rfpendtcvalue,'-01T00:00:00')
                (: complete date is given but no time :)
                else if(string-length($rfpendtcvalue) = 10) then concat($rfpendtcvalue,'T00:00:00')
                        (: T is given but no hours - might bei invalid anyway :)
                else if(string-length($rfpendtcvalue) = 11) then concat($rfpendtcvalue,'00:00:00')
                (: only hour is given :)
                else if(string-length($rfpendtcvalue) = 13) then concat($rfpendtcvalue,':00:00')
                (: hour and minutes are given, but no seconds :)
                else if(string-length($rfpendtcvalue) = 16) then concat($rfpendtcvalue,':00')
                (: all ok anyway :)
                else ($rfpendtcvalue)
        )
        (: compare both values :)
        (: RFPENDTC value must be equal or higher than STDTC - but first check whether it really is a datetime :)
            where $endtctestvalue castable as xs:dateTime and $rfpendtctestvalue castable as xs:dateTime and xs:dateTime($endtctestvalue) > xs:dateTime($rfpendtctestvalue) 
            return <error rule="SD1204" dataset="{data($name)}" variable="{data($endtcvarname)}" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">{data($endtcvarname)}={data($endtcvalue)} is after RFPENDTC={data($rfpendtcvalue)} in dataset {data($name)}</error>	
]]>
</rulexquery>
</sdsrule>

<sdsrule id="SD1204-SD" last-update="2020-08-08" originator="FDA" standard="SDTM" requiresDomainOrDataset="Yes">
<ruledescription>--ENDTC date may not be after RFPENDTC</ruledescription>
<ruledetaileddescription>End Date/Time (--ENDTC) variable value must be less than or equal to Date/Time of End of Participation (RFPENDTC)</ruledetaileddescription>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>INTERVENTIONS</domain>
<domain>EVENTS</domain>
<domain>FINDINGS</domain>
<domain>DM</domain>
<domain>SE</domain>
<domain>SV</domain>
<rulexquery><![CDATA[
(: Rule SD1204: --ENDTC date is after RFPENDTC :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external;
declare variable $defineversion external;
declare variable $datasetname external;
(: let $base := '/db/fda_submissions/cdisc01/' 
let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: get the DM dataset and the OID of DM.RFPENDTC (datetime of end of participation) :)
let $dmdef := $definedoc//odm:ItemGroupDef[@Name='DM']
(: and get the location and the DM document itself :)
let $dmlocation := (
	if($defineversion = '2.1') then $dmdef/def21:leaf/@xlink:href
	else $dmdef/def:leaf/@xlink:href
)
let $dmdoc := doc(concat($base,$dmlocation))
(: we also need the OID of USUBJID in DM :)
let $dmusubjidoid := (
    for $a in $definedoc//odm:ItemDef[@Name='USUBJID']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name='DM']/odm:ItemRef/@ItemOID
        return $a
)
(: and of RFPENDTC :)
let $rfpendtcoid := (
    for $a in $definedoc//odm:ItemDef[@Name='RFPENDTC']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name='DM']/odm:ItemRef/@ItemOID
        return $a
)
(: create a temporary structure containing USUBJID and RFPENDTC :)
let $usubjidrfpendtcpairs := (
    for $record in $dmdoc//odm:ItemGroupData
        let $usubjid := $record/odm:ItemData[@ItemOID=$dmusubjidoid]/@Value
        let $rfpendtc := $record/odm:ItemData[@ItemOID=$rfpendtcoid]/@Value
    return <record usubjid="{$usubjid}" rfpendtc="{$rfpendtc}" />
)
(: iterate over the provided dataset in INTERVENTIONS, EVENTS, FINDINGS, DM, SE, SV datasets :)
for $datasetdef in $definedoc//odm:ItemGroupDef[@Name=$datasetname][upper-case(@def:Class)='INTERVENTIONS' or upper-case(@def:Class)='EVENTS' or upper-case(@def:Class)='FINDINGS' 
		or upper-case(./def21:Class/@Name)='INTERVENTIONS' or upper-case(./def21:Class/@Name)='EVENTS' or upper-case(./def21:Class/@Name)='FINDINGS']
    (: get the dataset name :)
    let $name := $datasetdef/@Name
    let $domain := $datasetdef/@Domain
    (: get the datasetlocation :)
    let $datasetlocation := (
		if($defineversion = '2.1') then $datasetdef/def21:leaf/@xlink:href
		else $datasetdef/def:leaf/@xlink:href
	)
	let $datasetdoc := (
		if($datasetlocation) then doc(concat($base,$datasetlocation))
		else ()
	)
    (: we need the OID of USUBJID and of the --ENDTC variable :)
    let $endtcvarname := concat($domain,'ENDTC')
    let $endtcoid := (
        for $a in $definedoc//odm:ItemDef[@Name=$endtcvarname]/@OID 
        where $a = $datasetdef/odm:ItemRef/@ItemOID
        return $a
    )
    let $usubjidoid := (
        for $a in $definedoc//odm:ItemDef[@Name='USUBJID']/@OID 
        where $a = $datasetdef/odm:ItemRef/@ItemOID
        return $a
    )
    (: now iterate over all records that do have a value for --STDTC :)
    for $record in $datasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$endtcoid]/@Value]
        let $recnum := $record/@data:ItemGroupDataSeq
        (: get the value of --ENDTC and of USUBJID :)
        let $usubjidvalue := $record/odm:ItemData[@ItemOID=$usubjidoid]/@Value
        let $endtcvalue := $record/odm:ItemData[@ItemOID=$endtcoid]/@Value
        (: get the value of RFPENDTC in DM from the temporary structure :)
        let $rfpendtcvalue := $usubjidrfpendtcpairs[@usubjid=$usubjidvalue]/@rfpendtc
        (: take care of incomplete dates :)
        let $endtctestvalue :=
                (: only the year is given :)
                if(string-length($endtcvalue) = 4) then concat($endtcvalue,'-01-01T00:00:00')
                (: only the month is given :)
                else if(string-length($endtcvalue) = 7) then concat($endtcvalue,'-01T00:00:00')
                (: complete date is given but no time :)
                else if(string-length($endtcvalue) = 10) then concat($endtcvalue,'T00:00:00')
                (: T is given but no hours - might bei invalid anyway :)
                else if(string-length($endtcvalue) = 11) then concat($endtcvalue,'00:00:00')
                (: only hour is given :)
                else if(string-length($endtcvalue) = 13) then concat($endtcvalue,':00:00')
                (: hour and minutes are given, but no seconds :)
                else if(string-length($endtcvalue) = 16) then concat($endtcvalue,':00')
                (: all ok anyway :)
                else ($endtcvalue)
        (: and also for RFPENDTC :)
        let $rfpendtctestvalue := (
            (: only the year is given :)
                if(string-length($rfpendtcvalue) = 4) then concat($rfpendtcvalue,'-01-01T00:00:00')
                (: only the month is given :)
                else if(string-length($rfpendtcvalue) = 7) then concat($rfpendtcvalue,'-01T00:00:00')
                (: complete date is given but no time :)
                else if(string-length($rfpendtcvalue) = 10) then concat($rfpendtcvalue,'T00:00:00')
                        (: T is given but no hours - might bei invalid anyway :)
                else if(string-length($rfpendtcvalue) = 11) then concat($rfpendtcvalue,'00:00:00')
                (: only hour is given :)
                else if(string-length($rfpendtcvalue) = 13) then concat($rfpendtcvalue,':00:00')
                (: hour and minutes are given, but no seconds :)
                else if(string-length($rfpendtcvalue) = 16) then concat($rfpendtcvalue,':00')
                (: all ok anyway :)
                else ($rfpendtcvalue)
        )
        (: compare both values :)
        (: RFPENDTC value must be equal or higher than STDTC - but first check whether it really is a datetime :)
            where $endtctestvalue castable as xs:dateTime and $rfpendtctestvalue castable as xs:dateTime and xs:dateTime($endtctestvalue) > xs:dateTime($rfpendtctestvalue) 
            return <error rule="SD1204" dataset="{data($name)}" variable="{data($endtcvarname)}" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">{data($endtcvarname)}={data($endtcvalue)} is after RFPENDTC={data($rfpendtcvalue)} in dataset {data($name)}</error>	
]]>
</rulexquery>
</sdsrule>

<sdsrule id="SD1206" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>EXSTDTC date may not be after RFXENDTC</ruledescription>
<ruledetaileddescription>Start Date/Time of Treatment (EXSTDTC) variable value must be less than or equal to Date/Time of Last Study Treatment (RFXENDTC)</ruledetaileddescription>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>EX</domain>
<rulexquery><![CDATA[
(: Rule SD1206: EXSTDTC date is after RFXENDTC :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external;
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' 
let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: get the DM dataset and the OID of DM.RFXENDTC (Date/Time of Last Study Treatment)  :)
let $dmdef := $definedoc//odm:ItemGroupDef[@Name='DM']
(: and get the location and the DM document itself :)
let $dmlocation := (
	if($defineversion = '2.1') then $dmdef/def21:leaf/@xlink:href
	else $dmdef/def:leaf/@xlink:href
)
let $dmdoc := doc(concat($base,$dmlocation))
(: we also need the OID of USUBJID in DM :)
let $dmusubjidoid := (
    for $a in $definedoc//odm:ItemDef[@Name='USUBJID']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name='DM']/odm:ItemRef/@ItemOID
        return $a
)
(: and of RFXENDTC :)
let $rfxendtcoid := (
    for $a in $definedoc//odm:ItemDef[@Name='RFXENDTC']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name='DM']/odm:ItemRef/@ItemOID
        return $a
)
(: create a temporary structure containing USUBJID and RFPENDTC :)
let $usubjidrfxendtcpairs := (
    for $record in $dmdoc//odm:ItemGroupData
        let $usubjid := $record/odm:ItemData[@ItemOID=$dmusubjidoid]/@Value
        let $rfxendtc := $record/odm:ItemData[@ItemOID=$rfxendtcoid]/@Value
    return <record usubjid="{$usubjid}" rfxendtc="{$rfxendtc}" />
)
(: iterate over all EX datasets :)
for $datasetdef in $definedoc//odm:ItemGroupDef[@Domain='EX' or starts-with(@Name,'EX')]
    (: get the dataset name :)
    let $name := $datasetdef/@Name
    let $domain := $datasetdef/@Domain
    (: get the datasetlocation :)
    let $datasetlocation := (
		if($defineversion = '2.1') then $datasetdef/def21:leaf/@xlink:href
		else $datasetdef/def:leaf/@xlink:href
	)
    (: we need the OID of USUBJID and of the EXSTDTC variable :)
    let $exstdtcoid := (
        for $a in $definedoc//odm:ItemDef[@Name='EXSTDTC']/@OID 
        where $a = $datasetdef/odm:ItemRef/@ItemOID
        return $a
    )
    let $usubjidoid := (
        for $a in $definedoc//odm:ItemDef[@Name='USUBJID']/@OID 
        where $a = $datasetdef/odm:ItemRef/@ItemOID
        return $a
    )
    (: now iterate over all records that do have a value for --EXSTDTC :)
    for $record in doc(concat($base,$datasetlocation))//odm:ItemGroupData[odm:ItemData[@ItemOID=$exstdtcoid]/@Value]
        let $recnum := $record/@data:ItemGroupDataSeq
        (: get the value of --ENDTC and of USUBJID :)
        let $usubjidvalue := $record/odm:ItemData[@ItemOID=$usubjidoid]/@Value
        let $exstdtcvalue := $record/odm:ItemData[@ItemOID=$exstdtcoid]/@Value
        (: get the value of RFPENDTC in DM from the temporary structure :)
        let $rfxendtcvalue := $usubjidrfxendtcpairs[@usubjid=$usubjidvalue]/@rfpendtc
        (: take care of incomplete dates :)
        let $exstdtctestvalue :=
                (: only the year is given :)
                if(string-length($exstdtcvalue) = 4) then concat($exstdtcvalue,'-01-01T00:00:00')
                (: only the month is given :)
                else if(string-length($exstdtcvalue) = 7) then concat($exstdtcvalue,'-01T00:00:00')
                (: complete date is given but no time :)
                else if(string-length($exstdtcvalue) = 10) then concat($exstdtcvalue,'T00:00:00')
                (: T is given but no hours - might bei invalid anyway :)
                else if(string-length($exstdtcvalue) = 11) then concat($exstdtcvalue,'00:00:00')
                (: only hour is given :)
                else if(string-length($exstdtcvalue) = 13) then concat($exstdtcvalue,':00:00')
                (: hour and minutes are given, but no seconds :)
                else if(string-length($exstdtcvalue) = 16) then concat($exstdtcvalue,':00')
                (: all ok anyway :)
                else ($exstdtcvalue)
        (: and also for RFXENDTC :)
        let $rfxendtctestvalue := (
            (: only the year is given :)
                if(string-length($rfxendtcvalue) = 4) then concat($rfxendtcvalue,'-01-01T00:00:00')
                (: only the month is given :)
                else if(string-length($rfxendtcvalue) = 7) then concat($rfxendtcvalue,'-01T00:00:00')
                (: complete date is given but no time :)
                else if(string-length($rfxendtcvalue) = 10) then concat($rfxendtcvalue,'T00:00:00')
                (: T is given but no hours - might bei invalid anyway :)
                else if(string-length($rfxendtcvalue) = 11) then concat($rfxendtcvalue,'00:00:00')
                (: only hour is given :)
                else if(string-length($rfxendtcvalue) = 13) then concat($rfxendtcvalue,':00:00')
                (: hour and minutes are given, but no seconds :)
                else if(string-length($rfxendtcvalue) = 16) then concat($rfxendtcvalue,':00')
                (: all ok anyway :)
                else ($rfxendtcvalue)
        )
        (: compare both values :)
        (: RFXENDTC value must be equal or higher than EXSTDTC - but first check whether it really is a datetime :)
            where $exstdtctestvalue castable as xs:dateTime and $rfxendtctestvalue castable as xs:dateTime and xs:dateTime($exstdtctestvalue) > xs:dateTime($rfxendtctestvalue) 
            return <error rule="SD1206" dataset="{data($name)}" variable="EXSTDRC" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">EXSTDTC={data($exstdtcvalue)} is after RFXENDTC={data($rfxendtcvalue)} in dataset {data($name)}</error>	
]]>
</rulexquery>
</sdsrule>

<sdsrule id="SD1207" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>EXENDTC date may not be after RFXENDTC</ruledescription>
<ruledetaileddescription>End Date/Time of Treatment (EXENDTC) variable value must be less than or equal to Date/Time of Last Study Treatment (RFXENDTC)</ruledetaileddescription>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>EX</domain>
<rulexquery><![CDATA[
(: Rule SD1207: EXENDTC date is after RFXENDTC :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external;
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' 
let $define := 'define2-0-0-example-sdtm.xml' :)
(: get the DM dataset and the OID of DM.RFXENDTC (Date/Time of Last Study Treatment)  :)
let $definedoc := doc(concat($base,$define))
let $dmdef := $definedoc//odm:ItemGroupDef[@Name='DM']
(: and get the location and the DM document itself :)
let $dmlocation := (
	if($defineversion = '2.1') then $dmdef/def21:leaf/@xlink:href
	else $dmdef/def:leaf/@xlink:href
)
let $dmdoc := doc(concat($base,$dmlocation))
(: we also need the OID of USUBJID in DM :)
let $dmusubjidoid := (
    for $a in $definedoc//odm:ItemDef[@Name='USUBJID']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name='DM']/odm:ItemRef/@ItemOID
        return $a
)
(: and of RFXENDTC :)
let $rfxendtcoid := (
    for $a in $definedoc//odm:ItemDef[@Name='RFXENDTC']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name='DM']/odm:ItemRef/@ItemOID
        return $a
)
(: create a temporary structure containing USUBJID and RFPENDTC :)
let $usubjidrfxendtcpairs := (
    for $record in $dmdoc//odm:ItemGroupData
        let $usubjid := $record/odm:ItemData[@ItemOID=$dmusubjidoid]/@Value
        let $rfxendtc := $record/odm:ItemData[@ItemOID=$rfxendtcoid]/@Value
    return <record usubjid="{$usubjid}" rfxendtc="{$rfxendtc}" />
)
(: iterate over all EX datasets :)
for $datasetdef in $definedoc//odm:ItemGroupDef[@Domain='EX' or starts-with(@Name,'EX')]
    (: get the dataset name :)
    let $name := $datasetdef/@Name
    let $domain := $datasetdef/@Domain
    (: get the datasetlocation :)
    let $datasetlocation := (
		if($defineversion = '2.1') then $datasetdef/def21:leaf/@xlink:href
		else $datasetdef/def:leaf/@xlink:href
	)
    (: we need the OID of USUBJID and of the EXENDTC variable :)
    let $exendtcoid := (
        for $a in $definedoc//odm:ItemDef[@Name='EXENDTC']/@OID 
        where $a = $datasetdef/odm:ItemRef/@ItemOID
        return $a
    )
    let $usubjidoid := (
        for $a in $definedoc//odm:ItemDef[@Name='USUBJID']/@OID 
        where $a = $datasetdef/odm:ItemRef/@ItemOID
        return $a
    )
    (: now iterate over all records that do have a value for --EXSTDTC :)
    for $record in doc(concat($base,$datasetlocation))//odm:ItemGroupData[odm:ItemData[@ItemOID=$exendtcoid]/@Value]
        let $recnum := $record/@data:ItemGroupDataSeq
        (: get the value of --ENDTC and of USUBJID :)
        let $usubjidvalue := $record/odm:ItemData[@ItemOID=$usubjidoid]/@Value
        let $exendtcvalue := $record/odm:ItemData[@ItemOID=$exendtcoid]/@Value
        (: get the value of RFPENDTC in DM from the temporary structure :)
        let $rfxendtcvalue := $usubjidrfxendtcpairs[@usubjid=$usubjidvalue]/@rfpendtc
        (: take care of incomplete dates :)
        let $exendtctestvalue :=
                (: only the year is given :)
                if(string-length($exendtcvalue) = 4) then concat($exendtcvalue,'-01-01T00:00:00')
                (: only the month is given :)
                else if(string-length($exendtcvalue) = 7) then concat($exendtcvalue,'-01T00:00:00')
                (: complete date is given but no time :)
                else if(string-length($exendtcvalue) = 10) then concat($exendtcvalue,'T00:00:00')
                (: T is given but no hours - might bei invalid anyway :)
                else if(string-length($exendtcvalue) = 11) then concat($exendtcvalue,'00:00:00')
                (: only hour is given :)
                else if(string-length($exendtcvalue) = 13) then concat($exendtcvalue,':00:00')
                (: hour and minutes are given, but no seconds :)
                else if(string-length($exendtcvalue) = 16) then concat($exendtcvalue,':00')
                (: all ok anyway :)
                else ($exendtcvalue)
        (: and also for RFXENDTC :)
        let $rfxendtctestvalue := (
            (: only the year is given :)
                if(string-length($rfxendtcvalue) = 4) then concat($rfxendtcvalue,'-01-01T00:00:00')
                (: only the month is given :)
                else if(string-length($rfxendtcvalue) = 7) then concat($rfxendtcvalue,'-01T00:00:00')
                (: complete date is given but no time :)
                else if(string-length($rfxendtcvalue) = 10) then concat($rfxendtcvalue,'T00:00:00')
                (: T is given but no hours - might bei invalid anyway :)
                else if(string-length($rfxendtcvalue) = 11) then concat($rfxendtcvalue,'00:00:00')
                (: only hour is given :)
                else if(string-length($rfxendtcvalue) = 13) then concat($rfxendtcvalue,':00:00')
                (: hour and minutes are given, but no seconds :)
                else if(string-length($rfxendtcvalue) = 16) then concat($rfxendtcvalue,':00')
                (: all ok anyway :)
                else ($rfxendtcvalue)
        )
        (: compare both values :)
        (: RFXENDTC value must be equal or higher than EXENDTC - but first check whether it really is a datetime :)
            where $exendtctestvalue castable as xs:dateTime and $rfxendtctestvalue castable as xs:dateTime and xs:dateTime($exendtctestvalue) > xs:dateTime($rfxendtctestvalue) 
            return <error rule="SD1207" dataset="{data($name)}" variable="EXENDRC" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">EXENDTC={data($exendtcvalue)} is after RFXENDTC={data($rfxendtcvalue)} in dataset {data($name)}</error>	
]]>
</rulexquery>
</sdsrule>

<sdsrule id="SD1208" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>RFXSTDTC date may not be after RFXENDTC</ruledescription>
<ruledetaileddescription>Date/Time of First Study Treatment (RFXSTDTC) variable value must be less than or equal to Date/Time of Last Study Treatment (RFXENDTC)</ruledetaileddescription>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>DM</domain>
<rulexquery><![CDATA[
(: Rule SD1208: RFXSTDTC date is after RFXENDTC (DM) :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external;
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' 
let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: get the DM dataset and the OID of DM.RFXENDTC (Date/Time of Last Study Treatment)  :)
let $dmdef := $definedoc//odm:ItemGroupDef[@Name='DM']
(: and get the location and the DM document itself :)
let $dmlocation := (
	if($defineversion = '2.1') then $dmdef/def21:leaf/@xlink:href
	else $dmdef/def:leaf/@xlink:href
)
let $dmdoc := doc(concat($base,$dmlocation))
(: we need the OID of USUBJID in DM :)
let $dmusubjidoid := (
    for $a in $definedoc//odm:ItemDef[@Name='USUBJID']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name='DM']/odm:ItemRef/@ItemOID
        return $a
)
(: and of RFXENDTC :)
let $rfxendtcoid := (
    for $a in $definedoc//odm:ItemDef[@Name='RFXENDTC']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name='DM']/odm:ItemRef/@ItemOID
        return $a
)
(: and of RFXSTDTC :)
let $rfxstdtcoid := (
    for $a in $definedoc//odm:ItemDef[@Name='RFXSTDTC']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name='DM']/odm:ItemRef/@ItemOID
        return $a
) 
(: iterate over all records in DM that have both RFXSTDTC and RFXENDTC :)
for $record in $dmdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$rfxstdtcoid]/@Value and odm:ItemData[@ItemOID=$rfxendtcoid]/@Value]
        let $recnum := $record/@data:ItemGroupDataSeq
        (: get the values of RFXSTDTC and RFENDTC :)
        let $rfxstdtcvalue := $record/odm:ItemData[@ItemOID=$rfxstdtcoid]/@Value
        let $rfxendtcvalue := $record/odm:ItemData[@ItemOID=$rfxendtcoid]/@Value
        (: take care that both are comparable as dateTime :)
        let $rfxstdtctestvalue := (
            (: only the year is given :)
            if(string-length($rfxstdtcvalue) = 4) then concat($rfxstdtcvalue,'-01-01T00:00:00')
            (: only the month is given :)
            else if(string-length($rfxstdtcvalue) = 7) then concat($rfxstdtcvalue,'-01T00:00:00')
            (: complete date is given but no time :)
            else if(string-length($rfxstdtcvalue) = 10) then concat($rfxstdtcvalue,'T00:00:00')
            (: T is given but no hours - might bei invalid anyway :)
            else if(string-length($rfxstdtcvalue) = 11) then concat($rfxstdtcvalue,'00:00:00')
            (: only hour is given :)
            else if(string-length($rfxstdtcvalue) = 13) then concat($rfxstdtcvalue,':00:00')
            (: hour and minutes are given, but no seconds :)
            else if(string-length($rfxstdtcvalue) = 16) then concat($rfxstdtcvalue,':00')
            (: all ok anyway :)
            else ($rfxstdtcvalue)
        )
        let $rfxendtctestvalue := (
            (: only the year is given :)
            if(string-length($rfxendtcvalue) = 4) then concat($rfxendtcvalue,'-01-01T00:00:00')
            (: only the month is given :)
            else if(string-length($rfxendtcvalue) = 7) then concat($rfxendtcvalue,'-01T00:00:00')
            (: complete date is given but no time :)
            else if(string-length($rfxendtcvalue) = 10) then concat($rfxendtcvalue,'T00:00:00')
            (: T is given but no hours - might bei invalid anyway :)
            else if(string-length($rfxendtcvalue) = 11) then concat($rfxendtcvalue,'00:00:00')
            (: only hour is given :)
            else if(string-length($rfxendtcvalue) = 13) then concat($rfxendtcvalue,':00:00')
            (: hour and minutes are given, but no seconds :)
            else if(string-length($rfxendtcvalue) = 16) then concat($rfxendtcvalue,':00')
            (: all ok anyway :)
            else ($rfxendtcvalue)
        )
        (: compare both values :)
        (: RFXENDTC value must be equal or higher than RFXSTDTC - but first check whether it really is a datetime :)
            where $rfxstdtctestvalue castable as xs:dateTime and $rfxendtctestvalue castable as xs:dateTime and xs:dateTime($rfxstdtctestvalue) > xs:dateTime($rfxendtctestvalue) 
            return <error rule="SD1208" dataset="DM" variable="RFXSTDTC" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">RFXSTDTC={data($rfxstdtcvalue)} is after RFXENDTC={data($rfxendtcvalue)} in dataset DM</error>	
]]>
</rulexquery>
</sdsrule>

<sdsrule id="SD1063" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>Dataset must be defined in the define.xml</ruledescription>
<ruledetaileddescription>Datasets included in study data must be described in the data definition document (define.xml)</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>ALL</domain>
<rulexquery><![CDATA[
(: Rule SD1063 - Dataset is not present in define.xml :)
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
declare namespace functx = "http://www.functx.com";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external;
declare variable $defineversion external;
(: need a function substring-after-last - see e.g. http://www.xqueryfunctions.com/xq/functx_substring-after-last.html :)

declare function functx:escape-for-regex
  ( $arg as xs:string? )  as xs:string {
   replace($arg,
           '(\.|\[|\]|\\|\||\-|\^|\$|\?|\*|\+|\{|\}|\(|\))','\\$1')
 } ;
(: function "substring-after-last occurrence of a given delimiter :)
declare function functx:substring-after-last
  ( $arg as xs:string? ,
    $delim as xs:string )  as xs:string {

   replace ($arg,concat('^.*',functx:escape-for-regex($delim)),'')
 } ;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
for $doc in collection($base)
    (: get the filename :)
    let $filename := functx:substring-after-last(document-uri($doc),'/')
    (: and exclude the define.xml itself :)
    where $filename != $define and not(ends-with($filename,'.xsl')) (: exclude the define.xml doc itself, as well as any stylesheets :)
    (: now check whether it is referenced in the define.xml :)
    and not($definedoc//odm:ItemGroupDef/def:leaf[@xlink:href=$filename])
	and not($definedoc//odm:ItemGroupDef/def21:leaf[@xlink:href=$filename])
    return <warning rule="SD1063" rulelastupdate="2020-08-08">Dataset {data($filename)} is not present in define file {data($define)} </warning>    
]]></rulexquery>
</sdsrule>

<sdsrule id="SD0061" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>Domain referenced in define.xml must be included in the submission</ruledescription>
<ruledetaileddescription>Domains referenced in data definition document (define.xml) should be included in the submission</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>ALL</domain>
<rulexquery><![CDATA[
(: Rule SD0061 - Domain referenced in define.xml but dataset is missing :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external;
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: TODO: check whether "*" is fine for both define.xml 2.0 as for 2.1 :)
for $filename in $definedoc//odm:ItemGroupDef/*/@xlink:href
	let $datasetname := $filename/../../@Name  (: go up to ItemGroupDef level :)
    let $submdoc := concat($base,$filename)
    where not(doc($submdoc))
    return <warning rule="SD0061" dataset="{data($datasetname)}" rulelastupdate="2020-08-08">Dataset {data($filename)} referenced in {$define} but dataset is missing in submission</warning>
]]></rulexquery>
</sdsrule>

<!-- 2020-08-13: REMARK that this rule does NOT MAKE sense in the case of Dataset-XML format,
as in case the value was never populated ("empty column"), it will also not appear in the Dataset-XML file -->
<!--
<sdsrule id="SD0054" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>Variables defined in the define.xml must be present in the dataset</ruledescription>
<ruledetaileddescription>Variables listed in the data definition document (define.xml) should be included in the dataset</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>ALL</domain>
<rulexquery><![CDATA[
(: Rule SD0054: Variable in define.xml is not present in the dataset :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
declare namespace functx = "http://www.functx.com";
(: function to find out whether a value is in a sequence (array) :)
declare function functx:is-value-in-sequence
  ( $value as xs:anyAtomicType? ,
    $seq as xs:anyAtomicType* )  as xs:boolean {
   $value = $seq
} ;
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external;
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' 
let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: iterate over all the dataset definitions in the define.xml :)
for $datasetdef in $definedoc//odm:ItemGroupDef
    (: get the name and the dataset location and the dataset document :)
    let $datasetname := $datasetdef/@Name
    let $datasetlocation := (
		if($defineversion = '2.1') then $datasetdef/def21:leaf/@xlink:href
		else $datasetdef/def:leaf/@xlink:href
	)
    let $datasetdoc := (
		if($datasetlocation) then doc(concat($base,$datasetlocation))
		else ()
	)
    (: get all the OIDs of the variables in the dataset definition :)
    let $varoids := $datasetdef/odm:ItemRef/@ItemOID
    (: now get all unique data point OIDs in the corresponding dataset :)
    let $datasetoids := distinct-values($datasetdoc//odm:ItemGroupData/odm:ItemData/@ItemOID)
    (: iterate over all variables in the define.xml for the given dataset  :)
    for $var in $varoids
        (: this is the variable OID, get the Name :)
        let $varname := $definedoc//odm:ItemDef[@OID=$var]/@Name
        (: compare with the unique OIDs in the dataset itself :)
        where not(functx:is-value-in-sequence($var,$datasetoids))
        (: if not found in the dataset, provide a warning :)
        return <warning rule="SD0054" dataset="{$datasetname}" variable="{$varname}" rulelastupdate="2020-08-08">Variable {data($varname)} in dataset {data($datasetname)} is defined in the define.xml but cannot be found in the dataset {data($datasetname)}</warning>	
]]>
</rulexquery>
</sdsrule>
-->

<sdsrule id="SD0060" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>Variables included in the dataset must be defined in the define.xml</ruledescription>
<ruledetaileddescription>Variables included in the dataset must be described in the data definition document (define.xml)</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>ALL</domain>
<rulexquery><![CDATA[
(: Rule SD0060: Variable in dataset is not present in define.xml :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
declare namespace functx = "http://www.functx.com";
(: function to find out whether a value is in a sequence (array) :)
declare function functx:is-value-in-sequence
  ( $value as xs:anyAtomicType? ,
    $seq as xs:anyAtomicType* )  as xs:boolean {
   $value = $seq
} ;
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external;
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' 
let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: iterate over all the dataset definitions in the define.xml :)
for $datasetdef in $definedoc//odm:ItemGroupDef
    (: get the name and the dataset location and the dataset document :)
    let $datasetname := $datasetdef/@Name
    let $datasetlocation := (
		if($defineversion = '2.1') then $datasetdef/def21:leaf/@xlink:href
		else $datasetdef/def:leaf/@xlink:href
	)
    let $datasetdoc := (
		if($datasetlocation) then doc(concat($base,$datasetlocation))
		else ()
	)
    (: get all the OIDs of the variables in the dataset definition :)
    let $varoids := $datasetdef/odm:ItemRef/@ItemOID
    (: now get all unique data point OIDs in the corresponding dataset :)
    let $datasetoids := distinct-values($datasetdoc//odm:ItemGroupData/odm:ItemData/@ItemOID)
    (: iterate over all variables in the given dataset  :)
    for $var in $datasetoids
        (: P.S. we will not have a name for it when it is not defined in the define.xml :)
        (: compare with the OIDs in the define.xml for the given dataset :)
        where not(functx:is-value-in-sequence($var,$varoids))
        (: if not found in the dataset, provide a warning :)
        return <warning rule="SD0060" dataset="{$datasetname}" variable="{$var}" rulelastupdate="2020-08-08">Variable with OID {data($var)} in dataset {data($datasetname)} in the dataset {data($datasetname)} is not defined in the define.xml</warning>
]]>
</rulexquery>
</sdsrule>

<sdsrule id="SD0059-SD" last-update="2020-08-08" originator="FDA" standard="SDTM" requiresDomainOrDataset="Yes" timeintensive="Yes">
<ruledescription>Variable datatype must match datatype in the define.xml</ruledescription>
<ruledetaileddescription>Variable Data Types in the dataset must match the variable data types described in the data definition document (define.xml)</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>ALL</domain>
<rulexquery><![CDATA[
(: Rule SD0059 - Define.xml/dataset variable type mismatch: 
Variable Data Types in the dataset must match the variable data types described in the data definition document (define.xml)
:)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
declare namespace functx = "http://www.functx.com";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external;
declare variable $defineversion external;
declare variable $datasetname external;
(: some own local functions :)
declare function functx:isPartialDatetime
  ( $arg as xs:string? ) as xs:boolean {
   let $testvalue := 
        (: only the year is given :)
        if(string-length($arg) = 4) then concat($arg,'-01-01T00:00:00')
        (: only the year and month is given :)
        else if(string-length($arg) = 7) then concat($arg,'-01T00:00:00')
        (: date is complete but time part is missing :)
        else if(string-length($arg) = 10) then concat($arg,'T00:00:00')
        (: data is complete but only hour is given :)
        else if(string-length($arg) = 14) then concat($arg,':00:00')
        (: date, hour and minutes are given, but seconds is missing :)
        else if(string-length($arg) = 17) then concat($arg,':00')
        (: date seems to be complete :)
        else $arg 
    return $testvalue castable as xs:date
 } ;
declare function functx:isPartialDate ( $arg as xs:string? ) as xs:boolean {
    let $testvalue := 
        (: only the year is given :)
        if(string-length($arg) = 4) then concat($arg,'-01-01')
        (: only year and month is given :)
        else if(string-length($arg) = 7) then concat($arg,'-01')
        (: date seems to be complete :)
        else $arg 
    return $testvalue castable as xs:date
 } ;
declare function functx:isPartialTime ( $arg as xs:string? ) as xs:boolean {
    let $testvalue := 
        (: only the hour is given :)
        if(string-length($arg) = 4) then concat($arg,':00:00')
        (: only hour and minutes are given :)
        else if(string-length($arg) = 7) then concat($arg,':00')
        (: time seems to be complete :)
        else $arg 
    return $testvalue castable as xs:time
 } ;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: let $datasetname := 'ALL' :)
(: EITHER provide $datasetname := 'ALL', meaning: validate for all domains referenced from the define.xml OR:
$datasetname:='XX' where XX is a specific dataset name, MEANING validate for a single dataset only :)
(:  get the definitions for the domains (ItemGroupDefs in define.xml) :)
let $datasets := (
    if($datasetname != 'ALL') then $definedoc//odm:ItemGroupDef[@Name=$datasetname]
    else $definedoc//odm:ItemGroupDef
)
(: iterate over all datasets :)
for $dataset in $datasets
	let $dsname := $dataset/@Name
    let $datasetlocation := (
		if($defineversion = '2.1') then concat($base,$dataset/def21:leaf/@xlink:href)
		else concat($base,$dataset/def:leaf/@xlink:href)
	)
    for $record in doc($datasetlocation)//odm:ItemGroupData
        let $recnum := $record/@data:ItemGroupDataSeq
        (: iterate over all datapoints in the record, get OID and value :)
        for $datapoint in $record/odm:ItemData
            let $itemoid := $datapoint/@ItemOID
            let $itemvalue := $datapoint/@Value
            (: get the datatype in the corresponding ItemDef :)
            let $datatype := $definedoc//odm:ItemDef[@OID=$itemoid]/@DataType
            let $varname := $definedoc//odm:ItemDef[@OID=$itemoid]/@Name
            (: define.xml allows following datatypes:
            text, integer, float, datetime, date, time, partialDate, partialTime, partialDatetime, 
            incompleteDatetime, durationDatetime
            :)
            (: $isValidDataTypeValue is boolean :)
			(: P.S. in case 'datetime' is expected but a correct date is provided, that is also ok :)
            let $isValidDataTypeValue := 
                if ($datatype = 'text') then 'true'   (: everything is text ... :)
                else if($datatype = 'integer' and $itemvalue castable as xs:integer) then 'true'
                else if($datatype = 'float' and $itemvalue castable as xs:double) then 'true'
                else if($datatype = 'datetime' and $itemvalue castable as xs:dateTime) then 'true' 
                else if($datatype = 'date' and $itemvalue castable as xs:date or $itemvalue castable as xs:date) then 'true'
                else if($datatype = 'time' and $itemvalue castable as xs:time) then 'true'
                (: use function for partialDate :)
                else if($datatype = 'partialDate' and functx:isPartialDate($itemvalue)) then 'true'
                (: use function for partialTime :)
                else if($datatype = 'partialTime' and functx:isPartialTime($itemvalue)) then 'true'
                (: use function for partialDateTime :)
                else if($datatype = 'partialDateTime' and functx:isPartialDatetime($itemvalue)) then 'true' 
                (: TODO: write function for incompleteDatetime :)
                else if($datatype = 'durationDatetime' and $itemvalue castable as xs:duration) then 'true'
                (: TODO: write function for other form of durationDatetime ("from-to" format) :)
                else 'false'
            where $isValidDataTypeValue = 'false'
            return <error rule="SD0059" dataset="{data($dsname)}" variable="{data($varname)}" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">Define.xml/dataset variable type mismatch - Data point with OID = {data($itemoid)} and name = {data($varname)} and value {data($itemvalue)} is expected to be of type {data($datatype)}</error>
]]></rulexquery>
</sdsrule>

<sdsrule id="SD0037" last-update="2020-08-08" originator="FDA" standard="SDTM" timeintensive="Yes">
<ruledescription>Variable values must be found in the corresponding user-defined codelist</ruledescription>
<ruledetaileddescription>Variable values should be populated with terms found in the user-defined codelist associated with the variable in define.xml</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>ALL</domain>
<rulexquery><![CDATA[
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
declare namespace functx = "http://www.functx.com";
declare function functx:is-value-in-sequence
  ( $value as xs:anyAtomicType? ,
    $seq as xs:anyAtomicType* )  as xs:boolean {
   $value = $seq
 } ;
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external;
declare variable $defineversion external;
(: find all dataset definitions  :)
(: let $base := '/db/fda_submissions/cdisc01/'  :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: iterate over all dataset definitions :)
for $itemgroupdef in $definedoc//odm:ItemGroupDef
    let $name := $itemgroupdef/@Name
    (: get the location of the dataset and document :)
    let $datasetlocation := (
		if($defineversion = '2.1') then $itemgroupdef/def21:leaf/@xlink:href
		else $itemgroupdef/def:leaf/@xlink:href
	)
    let $datasetdoc := (
		if($datasetlocation) then doc(concat($base,$datasetlocation))
		else ()
	)
    (: get the OIDs of the coded variables for this dataset. 
    We need to exclude "External" CodeLists as long as they do not have a RESTful WS available,
    and as long as ODM/Dataset-XML does not support RESTful web services :)
    let $codedoids := (
        for $a in $definedoc//odm:ItemDef[odm:CodeListRef]/@OID 
        where $a = $itemgroupdef/odm:ItemRef/@ItemOID 
        return $a 
    )
    (: iterate over all records in the dataset, but only when they contain coded values :)
    for $record in $datasetdoc[count($codedoids)>0]//odm:ItemGroupData
        let $recnum := $record/@data:ItemGroupDataSeq
        (: iterate over the coded variables :)
        for $codeditem in $record/odm:ItemData[functx:is-value-in-sequence(@ItemOID,$codedoids)]
            (: get the OID and value :)
            let $oid := $codeditem/@ItemOID
            let $value := $codeditem/@Value
            (: also get the variable name for reporting :)
            let $varname := $definedoc//odm:ItemDef[@OID=$oid]/@Name
            (: look up the allowed values for this coded item in the define.xml :)
            let $codelistoid := $definedoc//odm:ItemDef[@OID=$oid]/odm:CodeListRef/@CodeListOID
            (: P.S. exclude "ExternalCodeList"  :)
            let $allowedvalues := $definedoc//odm:CodeList[not(odm:ExternalCodeList)][@OID=$codelistoid]/odm:*/@CodedValue
            (: the value must be one of the coded values :)
            where count($allowedvalues) > 0 and not(functx:is-value-in-sequence($value,$allowedvalues))
            return <error rule="SD0037" dataset="{data($name)}" variable="{data($varname)}" recordnumber="{data($recnum)}" rulelastupdate="2020-08-08">Value for variable {data($varname)}: value '{data($value)}' not found in not found in user-defined codelist {data($codelistoid)}</error>								
]]></rulexquery>
</sdsrule>

<sdsrule id="SD0037-SD" last-update="2020-08-08" originator="FDA" requiresDomainOrDataset="Yes" standard="SDTM">
<ruledescription>Variable values must be found in the corresponding user-defined codelist</ruledescription>
<ruledetaileddescription>Variable values should be populated with terms found in the user-defined codelist associated with the variable in define.xml</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>ALL</domain>
<rulexquery><![CDATA[
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
declare namespace functx = "http://www.functx.com";
declare function functx:is-value-in-sequence
  ( $value as xs:anyAtomicType? ,
    $seq as xs:anyAtomicType* )  as xs:boolean {
   $value = $seq
 } ;
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external;
declare variable $defineversion external;
declare variable $datasetname external;
(: find all dataset definitions  :)
(: let $base := '/db/fda_submissions/cdisc01/'  :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: iterate over all dataset definitions :)
for $itemgroupdef in $definedoc//odm:ItemGroupDef[@Name=$datasetname]
    let $name := $itemgroupdef/@Name
    (: get the location of the dataset and document :)
    let $datasetlocation := (
		if($defineversion = '2.1') then $itemgroupdef/def21:leaf/@xlink:href
		else $itemgroupdef/def:leaf/@xlink:href
	)
    let $datasetdoc := (
		if($datasetlocation) then doc(concat($base,$datasetlocation))
		else ()
	)
    (: get the OIDs of the coded variables for this dataset.
    We currently still need to exclude "External" CodeLists as long as they do not have a RESTful WS available,
    and as long as ODM/Dataset-XML does not support RESTful web services :)
    let $codedoids := (
        for $a in $definedoc//odm:ItemDef[odm:CodeListRef]/@OID 
        where $a = $itemgroupdef/odm:ItemRef/@ItemOID 
        return $a 
    )
    (: iterate over all records in the dataset, but only when they contain coded values :)
    for $record in $datasetdoc[count($codedoids)>0]//odm:ItemGroupData
        let $recnum := $record/@data:ItemGroupDataSeq
        (: iterate over the coded variables :)
        for $codeditem in $record/odm:ItemData[functx:is-value-in-sequence(@ItemOID,$codedoids)]
            (: get the OID and value :)
            let $oid := $codeditem/@ItemOID
            let $value := $codeditem/@Value
            (: also get the variable name for reporting :)
            let $varname := $definedoc//odm:ItemDef[@OID=$oid]/@Name
            (: look up the allowed values for this coded item in the define.xml :)
            let $codelistoid := $definedoc//odm:ItemDef[@OID=$oid]/odm:CodeListRef/@CodeListOID
            (: P.S. exclude "ExternalCodeList"  :)
            let $allowedvalues := $definedoc//odm:CodeList[not(odm:ExternalCodeList)][@OID=$codelistoid]/odm:*/@CodedValue
            (: the value must be one of the coded values :)
            where count($allowedvalues) > 0 and not(functx:is-value-in-sequence($value,$allowedvalues))
            return <error rule="SD0037" dataset="{data($name)}" variable="{data($varname)}" recordnumber="{data($recnum)}" rulelastupdate="2020-08-08">Value for variable {data($varname)}: value '{data($value)}' not found in not found in user-defined codelist {data($codelistoid)}</error>								
]]></rulexquery>
</sdsrule>

<sdsrule id="SD1228-SD" last-update="2020-08-08" originator="FDA" requiresDomainOrDataset="Yes" standard="SDTM" timeintensive="Yes">
<ruledescription>Variable value must be found in user-defined codelist when value-level condition occurs</ruledescription>
<ruledetaileddescription>Variable value must be populated with terms from user-defined value level codelist as specified in define.xml file</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>ALL</domain>
<rulexquery><![CDATA[
(: Rule SD1228: Variable value not found in user-defined codelist when value-level condition occurs :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
declare namespace functx = "http://www.functx.com";
declare function functx:is-value-in-sequence
  ( $value as xs:anyAtomicType? ,
    $seq as xs:anyAtomicType* )  as xs:boolean {
   $value = $seq
 } ;
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external;
declare variable $defineversion external;
declare variable $datasetname external; 
(: find all dataset definitions  :)
(: let $base := '/db/fda_submissions/cdisc01/'  
let $define := 'define2-0-0-example-sdtm.xml'
let $datasetname := 'EG' :)
let $definedoc := doc(concat($base,$define))
(: iterate over all dataset definitions :)
for $itemgroupdef in $definedoc//odm:ItemGroupDef[@Name=$datasetname]
    let $name := $itemgroupdef/@Name
    (: get the location of the dataset and document :)
    let $datasetlocation := (
		if($defineversion = '2.1') then $itemgroupdef/def21:leaf/@xlink:href
		else $itemgroupdef/def:leaf/@xlink:href
	)
    let $datasetdoc := doc(concat($base,$datasetlocation))
    (: get the OIDs of the coded ValueList variables for this dataset
    We currently still need to exclude "External" CodeLists as long as they do not have a RESTful WS available,
    and as long as ODM/Dataset-XML does not support RESTful web services :)
    (: get the variables (OIDs) that do have an attached ValueList for this dataset :)
    let $valuelistvars := (
		if($defineversion = '2.1') then 
			for $a in $definedoc//odm:ItemDef[def21:ValueListRef/@ValueListOID]/@OID 
			where $a = $itemgroupdef/odm:ItemRef/@ItemOID 
			return $a 
		else 
			for $a in $definedoc//odm:ItemDef[def:ValueListRef/@ValueListOID]/@OID 
			where $a = $itemgroupdef/odm:ItemRef/@ItemOID 
			return $a 
    )
    (: return <test>{data($valuelistvars)}</test> :)
    (: iterate over the variables (OIDs) that do have an attached ValueList :)
    for $valuelistvar in $valuelistvars
        (: get the Name of the variable :)
        let $valuelistvarname := $definedoc//odm:ItemDef[@OID=$valuelistvar]/@Name
        (: get the reference valuelist itself :)
        let $valuelistoid := (
			if($defineversion = '2.1') then $definedoc//odm:ItemDef[@OID=$valuelistvar]/def21:ValueListRef/@ValueListOID
			else $definedoc//odm:ItemDef[@OID=$valuelistvar]/def:ValueListRef/@ValueListOID
		)
        let $valuelist := (
			if($defineversion = '2.1') then $definedoc//def21:ValueListDef[@OID=$valuelistoid]
			else $definedoc//def:ValueListDef[@OID=$valuelistoid]
		)
        (: iterate over the ValueList ItemRefs :)
        for $valuelistitemref in $valuelist/odm:ItemRef
            (: get the OID :)
            let $valuelistitemoid := $valuelistitemref/@ItemOID
            (: get the WhereClause :)
            let $whereclauserefoid := (
				if($defineversion = '2.1') then $valuelistitemref/def21:WhereClauseRef/@WhereClauseOID
				else $valuelistitemref/def:WhereClauseRef/@WhereClauseOID
			)
            (: and the corresponding WhereClauseDef :)
            let $whereclausedef := (
				if($defineversion = '2.1') then $definedoc//def21:WhereClauseDef[@OID=$whereclauserefoid]
				else $definedoc//def:WhereClauseDef[@OID=$whereclauserefoid]
			)
            (: get the item it is about - we currently only support 1 item and only the "EQ" comparator,
            and only the case that it is about data in the same dataset (e.g. STRESU depends of TESTCD value)
            This covers 90% of the cases :)
            let $whereclauseaboutitemoid := (
				if($defineversion = '2.1') then $whereclausedef/odm:RangeCheck/@def21:ItemOID
				else $whereclausedef/odm:RangeCheck/@def:ItemOID
			)
            let $checkvalue := $whereclausedef/odm:RangeCheck/odm:CheckValue/text()
            (: return <test>{data($whereclauseaboutitemoid)} - {data($checkvalue)}</test> :)
            (: pick up the corresponding ItemDef - but only when it does have a connected codelist :)
            for $valuelistitemdefcoded in $definedoc//odm:ItemDef[@OID=$valuelistitemoid and odm:CodeListRef/@CodeListOID]
                (: get the codelist itself and its coded values :)
                let $valuelistcodelistoid := $valuelistitemdefcoded/odm:CodeListRef/@CodeListOID
                (: exclude ExternalCodeList :)
                let $valuelistcodelist := $definedoc//odm:CodeList[@OID=$valuelistcodelistoid and not(odm:ExternalCodeList)]
                let $valuelistcodelistname := $valuelistcodelist/@Name
                (: get the coded values :)
                let $valuelistcodedvalues := $valuelistcodelist/odm:*/@CodedValue
                (: get all the records in the dataset that are about the given valuelist variable :)
                (: return <test>{data($valuelistvar)} - {data($whereclauseaboutitemoid)} - {data($checkvalue)} - {data($valuelistcodedvalues)}</test> :)
                (: iterate over all the records where the WhereClause applies to, 
                i.e. $whereclauseaboutitemoid = $checkvalue :)
                for $record in $datasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$whereclauseaboutitemoid and @Value=$checkvalue]]
                    let $recnum := $record/@data:ItemGroupDataSeq
                    (: get the value of the dependent variable :)                  
                    let $dependentvarvalue := $record/odm:ItemData[@ItemOID=$valuelistvar]/@Value
                    (: the dependent variable value must be a value in the codelist :)
                    where not(functx:is-value-in-sequence($dependentvarvalue,$valuelistcodedvalues))
                    return <error rule="SD1228" dataset="{$datasetname}" variable="{$valuelistvarname}" recordnumber="{$recnum}" rulelastupdate="2020-08-08">Variable {data($valuelistvarname)} valuelist value '{data($dependentvarvalue)}' is not found in user-defined codelist '{data($valuelistcodelistname)}'. One of [{data($valuelistcodedvalues)}] is expected</error>
]]>
</rulexquery>
</sdsrule>

<sdsrule id="SD1229-SD" last-update="2020-08-08" originator="FDA" requiresDomainOrDataset="Yes" standard="SDTM">
<ruledescription>Variable value may not be null when value-level mandatory condition occurs</ruledescription>
<ruledetaileddescription>Variable value must be populated when value level attribute Mandatory='Yes' as specified in define.xml file</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>ALL</domain>
<rulexquery><![CDATA[
(: Rule SD1229: Variable value is null when value-level condition occurs - 
 : Variable value must be populated when value level attribute Mandatory='Yes' as specified in define.xml file :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
declare namespace functx = "http://www.functx.com";
declare function functx:is-value-in-sequence
  ( $value as xs:anyAtomicType? ,
    $seq as xs:anyAtomicType* )  as xs:boolean {
   $value = $seq
 } ;
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external;
declare variable $defineversion external;
declare variable $datasetname external; 
(: find all dataset definitions  :)
(: let $base := '/db/fda_submissions/cdisc01/'  
let $define := 'define2-0-0-example-sdtm.xml'
let $datasetname := 'EG' :)
let $definedoc := doc(concat($base,$define)) 
(: iterate over all dataset definitions :)
for $itemgroupdef in $definedoc//odm:ItemGroupDef[@Name=$datasetname]
    let $name := $itemgroupdef/@Name
    (: get the location of the dataset and document :)
    let $datasetlocation := (
		if($defineversion = '2.1') then $itemgroupdef/def21:leaf/@xlink:href
		else $itemgroupdef/def:leaf/@xlink:href
	)
    let $datasetdoc := doc(concat($base,$datasetlocation))
    (: get the OIDs of the coded ValueList variables for this dataset
    We currently still need to exclude "External" CodeLists as long as they do not have a RESTful WS available,
    and as long as ODM/Dataset-XML does not support RESTful web services :)
    (: get the variables (OIDs) that do have an attached ValueList for this dataset :)
    let $valuelistvars := (
		if($defineversion = '2.1') then 
			for $a in $definedoc//odm:ItemDef[def21:ValueListRef/@ValueListOID]/@OID 
			where $a = $itemgroupdef/odm:ItemRef/@ItemOID 
			return $a 
		else 
			for $a in $definedoc//odm:ItemDef[def:ValueListRef/@ValueListOID]/@OID 
			where $a = $itemgroupdef/odm:ItemRef/@ItemOID 
			return $a 
    )
    (: return <test>{data($valuelistvars)}</test> :)
    (: iterate over the variables (OIDs) that do have an attached ValueList :)
    for $valuelistvar in $valuelistvars
        (: get the Name of the variable :)
        let $valuelistvarname := $definedoc//odm:ItemDef[@OID=$valuelistvar]/@Name
        (: get the reference valuelist itself :)
        let $valuelistoid := (
			if($defineversion = '2.1') then $definedoc//odm:ItemDef[@OID=$valuelistvar]/def21:ValueListRef/@ValueListOID 
			else $definedoc//odm:ItemDef[@OID=$valuelistvar]/def:ValueListRef/@ValueListOID
		)
        let $valuelist := (
			if($defineversion = '2.1') then $definedoc//def21:ValueListDef[@OID=$valuelistoid]
			else $definedoc//def:ValueListDef[@OID=$valuelistoid]
		)
        (: iterate over the ValueList ItemRefs that are "Mandatory" :)
        for $valuelistitemref in $valuelist/odm:ItemRef[@Mandatory='Yes']
            (: get the OID :)
            let $valuelistitemoid := $valuelistitemref/@ItemOID
            (: get the WhereClause :)
            let $whereclauserefoid := (
				if($defineversion = '2.1') then $valuelistitemref/def21:WhereClauseRef/@WhereClauseOID
				else $valuelistitemref/def:WhereClauseRef/@WhereClauseOID
			)
            (: and the corresponding WhereClauseDef :)
            let $whereclausedef := (
				if($defineversion = '2.1') then $definedoc//def21:WhereClauseDef[@OID=$whereclauserefoid]
				else $definedoc//def:WhereClauseDef[@OID=$whereclauserefoid]
			)
            (: get the item it is about - we currently only support 1 item and only the "EQ" comparator,
            and only the case that it is about data in the same dataset (e.g. STRESU depends of TESTCD value)
            This covers 90% of the cases :)
            let $whereclauseaboutitemoid := (
				if($defineversion = '2.1') then $whereclausedef/odm:RangeCheck/@def21:ItemOID
				else $whereclausedef/odm:RangeCheck/@def:ItemOID
			)
            let $whereclauseaboutitemname := $definedoc//odm:ItemDef[@OID=$whereclauseaboutitemoid]/@Name
            let $checkvalue := $whereclausedef/odm:RangeCheck/odm:CheckValue/text()
            (: return <test>{data($whereclauseaboutitemoid)} - {data($checkvalue)}</test> :)
            (: pick up the corresponding ItemDef :)
            for $valuelistitemdefmandatory in $definedoc//odm:ItemDef[@OID=$valuelistitemoid]
                (: get all the records in the dataset that are about the given valuelist variable :)
                (: return <test>{data($valuelistvar)} - {data($whereclauseaboutitemoid)} - {data($checkvalue)} - {data($valuelistcodedvalues)}</test> :)
                (: iterate over all the records where the WhereClause applies to, 
                i.e. $whereclauseaboutitemoid = $checkvalue :)
                for $record in $datasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$whereclauseaboutitemoid and @Value=$checkvalue]]
                    let $recnum := $record/@data:ItemGroupDataSeq
                    (: get the value of the dependent variable :)                  
                    let $dependentvarvalue := $record/odm:ItemData[@ItemOID=$valuelistvar]/@Value
                    (: the dependent variable value may not be null as "Mandatory='Yes'" :)
                    where not($dependentvarvalue)
                    return <error rule="SD1229" dataset="{$datasetname}" variable="{$valuelistvarname}" recordnumber="{$recnum}" rulelastupdate="2020-08-08">Variable {data($valuelistvarname)} valuelist value '{data($dependentvarvalue)}' is found to be null although the item is valuelist-mandatory for {data($whereclauseaboutitemname)}='{data($checkvalue)}'</error>
]]>
</rulexquery>
</sdsrule>

<sdsrule id="SD1230-SD" last-update="2020-08-08" originator="FDA" standard="SDTM" requiresDomainOrDataset="Yes">
<ruledescription>Value level datatype must match the datatype described in define.xml - Variable datatype is not %Variable.@Clause.DataType% when value-level condition occurs </ruledescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>ALL</domain>
<rulexquery><![CDATA[
(: Rule SD1230: Variable datatype is not %Variable.@Clause.DataType% when value-level condition occurs - 
 Value level datatype must match the datatype described in define.xml :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
declare namespace functx = "http://www.functx.com";
declare function functx:is-value-in-sequence
  ( $value as xs:anyAtomicType? ,
    $seq as xs:anyAtomicType* )  as xs:boolean {
   $value = $seq
 } ;
 (: some own local functions :)
declare function functx:isPartialDatetime
  ( $arg as xs:string? ) as xs:boolean {
   let $testvalue := 
        (: only the year is given :)
        if(string-length($arg) = 4) then concat($arg,'-01-01T00:00:00')
        (: only the year and month is given :)
        else if(string-length($arg) = 7) then concat($arg,'-01T00:00:00')
        (: date is complete but time part is missing :)
        else if(string-length($arg) = 10) then concat($arg,'T00:00:00')
        (: data is complete but only hour is given :)
        else if(string-length($arg) = 14) then concat($arg,':00:00')
        (: date, hour and minutes are given, but seconds is missing :)
        else if(string-length($arg) = 17) then concat($arg,':00')
        (: date seems to be complete :)
        else $arg 
    return $testvalue castable as xs:date
 } ;
declare function functx:isPartialDate ( $arg as xs:string? ) as xs:boolean {
    let $testvalue := 
        (: only the year is given :)
        if(string-length($arg) = 4) then concat($arg,'-01-01')
        (: only year and month is given :)
        else if(string-length($arg) = 7) then concat($arg,'-01')
        (: date seems to be complete :)
        else $arg 
    return $testvalue castable as xs:date
 } ;
declare function functx:isPartialTime ( $arg as xs:string? ) as xs:boolean {
    let $testvalue := 
        (: only the hour is given :)
        if(string-length($arg) = 4) then concat($arg,':00:00')
        (: only hour and minutes are given :)
        else if(string-length($arg) = 7) then concat($arg,':00')
        (: time seems to be complete :)
        else $arg 
    return $testvalue castable as xs:time
 } ;
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external;
declare variable $defineversion external;
declare variable $datasetname external; 
(: find all dataset definitions  :)
(: let $base := '/db/fda_submissions/cdisc01/'  
let $define := 'define2-0-0-example-sdtm.xml'
let $datasetname := 'EG' :)
let $definedoc := doc(concat($base,$define))
(: iterate over all dataset definitions :)
for $itemgroupdef in $definedoc//odm:ItemGroupDef[@Name=$datasetname]
    let $name := $itemgroupdef/@Name
    (: get the location of the dataset and document :)
    let $datasetlocation := (
		if($defineversion = '2.1') then $itemgroupdef/def21:leaf/@xlink:href
		else $itemgroupdef/def:leaf/@xlink:href
	)
    let $datasetdoc := doc(concat($base,$datasetlocation))
    (: get the OIDs of the coded ValueList variables for this dataset
    We currently still need to exclude "External" CodeLists as long as they do not have a RESTful WS available,
    and as long as ODM/Dataset-XML does not support RESTful web services :)
    (: get the variables (OIDs) that do have an attached ValueList for this dataset :)
    let $valuelistvars := (
		if($defineversion = '2.1') then 
			for $a in $definedoc//odm:ItemDef[def21:ValueListRef/@ValueListOID]/@OID 
			where $a = $itemgroupdef/odm:ItemRef/@ItemOID 
			return $a 
		else 
			for $a in $definedoc//odm:ItemDef[def:ValueListRef/@ValueListOID]/@OID 
			where $a = $itemgroupdef/odm:ItemRef/@ItemOID 
			return $a 
    )
    (: return <test>{data($valuelistvars)}</test> :)
    (: iterate over the variables (OIDs) that do have an attached ValueList :)
    for $valuelistvar in $valuelistvars
        (: get the Name of the variable :)
        let $valuelistvarname := $definedoc//odm:ItemDef[@OID=$valuelistvar]/@Name
        (: get the reference valuelist itself :)
        let $valuelistoid := (
			if($defineversion = '2.1') then $definedoc//odm:ItemDef[@OID=$valuelistvar]/def21:ValueListRef/@ValueListOID
			else $definedoc//odm:ItemDef[@OID=$valuelistvar]/def:ValueListRef/@ValueListOID
		)
        let $valuelist := (
			if($defineversion = '2.1') then $definedoc//def21:ValueListDef[@OID=$valuelistoid]
			else $definedoc//def:ValueListDef[@OID=$valuelistoid]
		)
        (: iterate over the ValueList ItemRefs :)
        for $valuelistitemref in $valuelist/odm:ItemRef[@Mandatory='Yes']
            (: get the OID :)
            let $valuelistitemoid := $valuelistitemref/@ItemOID
            (: get the WhereClause :)
            let $whereclauserefoid := (
				if($defineversion = '2.1') then $valuelistitemref/def21:WhereClauseRef/@WhereClauseOID
				else $valuelistitemref/def:WhereClauseRef/@WhereClauseOID
			)
            (: and the corresponding WhereClauseDef :)
            let $whereclausedef := (
				if($defineversion = '2.1') then $definedoc//def21:WhereClauseDef[@OID=$whereclauserefoid]
				else $definedoc//def:WhereClauseDef[@OID=$whereclauserefoid]
			)
            (: get the item it is about - we currently only support 1 item and only the "EQ" comparator,
            and only the case that it is about data in the same dataset (e.g. STRESU depends of TESTCD value)
            This covers 90% of the cases :)
            let $whereclauseaboutitemoid := (
				if($defineversion = '2.1') then $whereclausedef/odm:RangeCheck/@def21:ItemOID
				else $whereclausedef/odm:RangeCheck/@def:ItemOID
			)
            let $whereclauseaboutitemname := $definedoc//odm:ItemDef[@OID=$whereclauseaboutitemoid]/@Name
            let $checkvalue := $whereclausedef/odm:RangeCheck/odm:CheckValue/text()
            (: return <test>{data($whereclauseaboutitemoid)} - {data($checkvalue)}</test> :)
            (: pick up the corresponding ItemDef :)
            for $valuelistitemdef in $definedoc//odm:ItemDef[@OID=$valuelistitemoid]
                (: get the datatype :)
                let $datatype := $valuelistitemdef/@DataType
                (: get all the records in the dataset that are about the given valuelist variable :)
                (: return <test>{data($valuelistvar)} - {data($whereclauseaboutitemoid)} - {data($checkvalue)} - {data($valuelistcodedvalues)}</test> :)
                (: iterate over all the records where the WhereClause applies to, 
                i.e. $whereclauseaboutitemoid = $checkvalue :)
                for $record in $datasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$whereclauseaboutitemoid and @Value=$checkvalue]]
                    let $recnum := $record/@data:ItemGroupDataSeq
                    (: get the value of the dependent variable :)                  
                    let $itemvalue := $record/odm:ItemData[@ItemOID=$valuelistvar]/@Value
                    (: the value must be according to the datatype :)
                    (: define.xml allows following datatypes:
                    text, integer, float, datetime, date, time, partialDate, partialTime, partialDatetime, 
                    incompleteDatetime, durationDatetime
                    :)
                    (: P.S. in case 'datetime' is expected but a correct date is provided, that is also ok :)
                    let $isValidDataTypeValue := 
                        if ($datatype = 'text') then 'true'   (: everything is text ... :)
                        else if($datatype = 'integer' and $itemvalue castable as xs:integer) then 'true'
                        else if($datatype = 'float' and $itemvalue castable as xs:double) then 'true'
                        else if($datatype = 'datetime' and $itemvalue castable as xs:dateTime) then 'true' 
                        else if($datatype = 'date' and $itemvalue castable as xs:date or $itemvalue castable as xs:date) then 'true'
                        else if($datatype = 'time' and $itemvalue castable as xs:time) then 'true'
                        (: use function for partialDate :)
                        else if($datatype = 'partialDate' and functx:isPartialDate($itemvalue)) then 'true'
                        (: use function for partialTime :)
                        else if($datatype = 'partialTime' and functx:isPartialTime($itemvalue)) then 'true'
                        (: use function for partialDateTime :)
                        else if($datatype = 'partialDateTime' and functx:isPartialDatetime($itemvalue)) then 'true' 
                        (: TODO: write function for incompleteDatetime :)
                        else if($datatype = 'durationDatetime' and $itemvalue castable as xs:duration) then 'true'
                        (: TODO: write function for other form of durationDatetime ("from-to" format) :)
                        else 'false'
                    (: when the value does not match the datatype, give an error  :)
                    where $isValidDataTypeValue = 'false'
                    return <error rule="SD1230" dataset="{$datasetname}" variable="{$valuelistvarname}" recordnumber="{$recnum}" rulelastupdate="2020-08-08">Variable {data($valuelistvarname)} valuelist value '{data($itemvalue)}' is compatible with its data type '{data($datatype)}'</error>
]]>
</rulexquery>
</sdsrule>

<sdsrule id="SD1231-SD" last-update="2020-08-08" originator="FDA" requiresDomainOrDataset="Yes" standard="SDTM">
<ruledescription>Value level length must not exceed the length as specified define.xml</ruledescription>
<ruledetaileddescription>Variable value is longer than defined max length %Variable.@Clause.Length% when value-level condition occurs</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>ALL</domain>
<rulexquery><![CDATA[
(: Rule SD1231: Variable value is longer than defined max length %Variable.@Clause.Length% when value-level condition occurs -
  Value level length must not exceed the length as specified define.xml :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
declare namespace functx = "http://www.functx.com";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external;
declare variable $defineversion external;
declare variable $datasetname external;  
(: find all dataset definitions  :)
(: let $base := '/db/fda_submissions/cdisc01/'  
let $define := 'define2-0-0-example-sdtm.xml'
let $datasetname := 'DA' :)
let $definedoc := doc(concat($base,$define))
(: iterate over all dataset definitions :)
for $itemgroupdef in $definedoc//odm:ItemGroupDef[@Name=$datasetname]
    let $name := $itemgroupdef/@Name
    (: get the location of the dataset and document :)
    let $datasetlocation := (
		if($defineversion = '2.1') then $itemgroupdef/def21:leaf/@xlink:href
		else $itemgroupdef/def:leaf/@xlink:href
	)
    let $datasetdoc := doc(concat($base,$datasetlocation))
    (: get the OIDs of the coded ValueList variables for this dataset
    We currently still need to exclude "External" CodeLists as long as they do not have a RESTful WS available,
    and as long as ODM/Dataset-XML does not support RESTful web services :)
    (: get the variables (OIDs) that do have an attached ValueList for this dataset :)
    let $valuelistvars := (
		if($defineversion = '2.1') then 
			for $a in $definedoc//odm:ItemDef[def21:ValueListRef/@ValueListOID]/@OID 
			where $a = $itemgroupdef/odm:ItemRef/@ItemOID 
			return $a 
		else 
			for $a in $definedoc//odm:ItemDef[def:ValueListRef/@ValueListOID]/@OID 
			where $a = $itemgroupdef/odm:ItemRef/@ItemOID 
			return $a 
    )
    (: return <test>{data($valuelistvars)}</test> :)
    (: iterate over the variables (OIDs) that do have an attached ValueList :)
    for $valuelistvar in $valuelistvars
        (: get the Name of the variable :)
        let $valuelistvarname := $definedoc//odm:ItemDef[@OID=$valuelistvar]/@Name
        (: get the reference valuelist itself :)
        let $valuelistoid := (
			if($defineversion = '2.1') then $definedoc//odm:ItemDef[@OID=$valuelistvar]/def21:ValueListRef/@ValueListOID
			else $definedoc//odm:ItemDef[@OID=$valuelistvar]/def:ValueListRef/@ValueListOID
		)
        let $valuelist := (
			if($defineversion = '2.1') then $definedoc//def21:ValueListDef[@OID=$valuelistoid]
			else $definedoc//def:ValueListDef[@OID=$valuelistoid]
		)
        (: iterate over the ValueList ItemRefs :)
        for $valuelistitemref in $valuelist/odm:ItemRef[@Mandatory='Yes']
            (: get the OID :)
            let $valuelistitemoid := $valuelistitemref/@ItemOID
            (: get the WhereClause :)
            let $whereclauserefoid := (
				if($defineversion = '2.1') then $valuelistitemref/def21:WhereClauseRef/@WhereClauseOID
				else $valuelistitemref/def:WhereClauseRef/@WhereClauseOID
			)
            (: and the corresponding WhereClauseDef :)
            let $whereclausedef := (
				if($defineversion = '2.1') then $definedoc//def21:WhereClauseDef[@OID=$whereclauserefoid]
				else $definedoc//def:WhereClauseDef[@OID=$whereclauserefoid]
			)
            (: get the item it is about - we currently only support 1 item and only the "EQ" comparator,
            and only the case that it is about data in the same dataset (e.g. STRESU depends of TESTCD value)
            This covers 90% of the cases :)
            let $whereclauseaboutitemoid := (
				if($defineversion = '2.1') then $whereclausedef/odm:RangeCheck/@def21:ItemOID
				else $whereclausedef/odm:RangeCheck/@def:ItemOID
			)
            let $whereclauseaboutitemname := $definedoc//odm:ItemDef[@OID=$whereclauseaboutitemoid]/@Name
            let $checkvalue := $whereclausedef/odm:RangeCheck/odm:CheckValue/text()
            (: return <test>{data($whereclauseaboutitemoid)} - {data($checkvalue)}</test> :)
            (: pick up the corresponding ItemDef :)
            for $valuelistitemdef in $definedoc//odm:ItemDef[@OID=$valuelistitemoid]
                (: get the maximal length :)
                let $maxlength := $valuelistitemdef/@Length
                (: get all the records in the dataset that are about the given valuelist variable :)
                (: return <test>{data($valuelistvar)} - {data($whereclauseaboutitemoid)} - {data($checkvalue)} - {data($valuelistcodedvalues)}</test> :)
                (: iterate over all the records where the WhereClause applies to, 
                i.e. $whereclauseaboutitemoid = $checkvalue :)
                for $record in $datasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$whereclauseaboutitemoid and @Value=$checkvalue]]
                    let $recnum := $record/@data:ItemGroupDataSeq
                    (: get the value of the dependent variable :)                  
                    let $itemvalue := $record/odm:ItemData[@ItemOID=$valuelistvar]/@Value
                    (: and get its length :)
                    let $valuelength := string-length($itemvalue)
                    (: real length should be less or equal than the maximum lenth :)
                    where $valuelength > $maxlength 
                    return <error rule="SD1231" dataset="{$datasetname}" variable="{$valuelistvarname}" recordnumber="{$recnum}" rulelastupdate="2020-08-08">Variable {data($valuelistvarname)} valuelist value '{data($itemvalue)}' is longer than the allowed maximal length {data($maxlength)}</error>
]]>
</rulexquery>
</sdsrule>

<!-- Rule SD1324 "Define.xml/dataset variable label mismatch" is not implemented, as in Dataset-XML format, 
the dataset does not contain any labels, only identifier references  -->

<!-- Rule SD1325 "Define.xml/dataset description mismatch" is not implemented, 
as it is unclear what the rule means, and is probably not applicable to Dataset-XML format anyway -->

<sdsrule id="SD1135" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>The value for Study Day must not be negative for Exposure treatments - </ruledescription>
<ruledetaileddescription>Study Day variables (*DY) value should not be negative in Exposure (EX) datasets</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>EX</domain>
<rulexquery><![CDATA[
(: Rule SD1135: Study Day variables (*DY) value should not be negative in Exposure (EX) datasets :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external;
declare variable $defineversion external;
(: find all dataset definitions  :)
(: let $base := '/db/fda_submissions/cdisc01/'  
let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: iterate over all dataset definitions (for the case there is more than one) :)
for $datasetdef in $definedoc//odm:ItemGroupDef[starts-with(@Name,'EX') or @Domain='EX']
    (: get the name location of the dataset :)
    let $name := $datasetdef/@Name
    let $datasetlocation := (
		if($defineversion = '2.1') then $datasetdef/def21:leaf/@xlink:href
		else $datasetdef/def:leaf/@xlink:href
	)
    (: and the document itself :)
    let $datasetdoc := doc(concat($base,$datasetlocation))
    (: iterate over all *DY (includes --DY, --STDY, --ENDY) variables :)
    let $dyvaroids := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'DY')]/@OID 
        where $a = $datasetdef/odm:ItemRef/@ItemOID 
        return $a 
    )
    for $dyvaroid in $dyvaroids
        (: get the name of the variable :)
        let $varname := $definedoc//odm:ItemDef[@OID=$dyvaroid]/@Name
        (: iterate over all the records in the dataset for this variable, 
        and get the value :)
        for $record in $datasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$dyvaroid]]
            let $recnum := $record/@data:ItemGroupDataSeq
            let $dyvalue := $record/odm:ItemData[@ItemOID=$dyvaroid]/@Value
            (: the value may not be negative :)
            where $dyvalue castable as xs:integer and xs:integer($dyvalue) < 0
            return <warning rule="SD1135" dataset="{$name}" variable="{$varname}" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">Value of {data($varname)} is not allowed to be negative in dataset {data($name)}. A value of {data($dyvalue)} was found</warning>	
]]>
</rulexquery>
</sdsrule>

<!-- TODO: more extensive testing -->
<sdsrule id="SE0011-SD" last-update="2020-08-08" originator="FDA" standard="SDTM" requiresDomainOrDataset="Yes">
<ruledescription>Time Point Reference must be provided, when Reference Time Point is used</ruledescription>
<ruledetaileddescription>Time Point Reference (--TPTREF) variable values should be provided, when Date/Time of Reference Time Point (--RFTDTC) variable values are populated</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>EX</domain>
<domain>BG</domain>
<domain>LB</domain>
<domain>CL</domain>
<domain>PC</domain>
<domain>EG</domain>
<domain>VS</domain>
<rulexquery><![CDATA[
(: Rule SE0011: Time Point Reference should be provided, when Reference Time Point is used.
 Time Point Reference (--TPTREF) variable values should be provided, when Date/Time of Reference Time Point (--RFTDTC) variable values are populated.
 Applicable to: EX, BG, LB, CL, PC, EG, VS
 IMPORTANT REMARK: this also applies to SDTM, not only to SEND :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external;
declare variable $defineversion external;
declare variable $datasetname external;
(: find all dataset definitions  :)
(: let $base := '/db/fda_submissions/cdisc01/'  
let $define := 'define2-0-0-example-sdtm.xml'
let $datasetname := 'LB' :)
(: get the define.xml dataset definition :)
let $definedoc := doc(concat($base,$define))
for $datasetdef in $definedoc//odm:ItemGroupDef[@Name=$datasetname][@Domain='EX' or @Domain='BG' or @Domain='LB' or @Domain='CL' or @Domain='PC' or @domain='EG' or @Domain='VS']
    (: get the name location of the dataset :)
    let $name := $datasetdef/@Name
	let $datasetlocation := (
		if($defineversion = '2.1') then $datasetdef/def21:leaf/@xlink:href
		else $datasetdef/def:leaf/@xlink:href
	)
    (: and the document itself :)
    let $datasetdoc := doc(concat($base,$datasetlocation))
    (: get the OIDs of --TPTREF and --RFTDTC, and their name :)
    let $tptrefoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'TPTREF')]/@OID 
        where $a = $datasetdef/odm:ItemRef/@ItemOID 
        return $a 
    ) 
    let $tptrefname := $definedoc//odm:ItemDef[@OID=$tptrefoid]/@Name
    let $rftdtcoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'RFTDTC')]/@OID 
        where $a = $datasetdef/odm:ItemRef/@ItemOID 
        return $a 
    )
    let $rftdtcname := $definedoc//odm:ItemDef[@OID=$rftdtcoid]/@Name
    (: iterate over all records that have --RFTDTC :)
    for $record in $datasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$rftdtcoid]/@Value]
        let $recnum := $record/@data:ItemGroupDataSeq
        (: get the value of --TPTREF, if any :)
        let $tptrefvalue := $record/odm:ItemData[@ItemOID=$tptrefoid]/@Value
        (: the value of -TPTREF is not allowed to be null or empty :)
        (: where not($tptrefvalue) or $tptrefvalue='' :)
        return <warning rule="SE0011" dataset="{$name}" variable="{$rftdtcname}" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">{data($tptrefname)} is not populated although {data($rftdtcname)} is populated</warning>	
]]>
</rulexquery>
</sdsrule>

<sdsrule id="SD0021" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>An End Time-Point value must be populated</ruledescription>
<ruledetaileddescription>One End Time-Point variable is expected to be populated when an event or an intervention occurred. 
(E.g., End Date/Time of Event or Intervention (--ENDTC), or End Relative to Reference Period (--ENRF), or End Relative to Reference Period (--ENRTPT) should not be missing, 
or Occurrence (--OCCUR) = 'N' or Completion Status (--STAT) != '') or Duration (--DUR) != '')</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>EVENTS</domain>
<domain>INTERVENTIONS</domain>
<rulexquery><![CDATA[
(: Rule SD0021: One End Time-Point variable is expected to be populated when an event or an intervention occurred. 
 e.g./i.e., End Date/Time of Event or Intervention (--ENDTC), or End Relative to Reference Period (--ENRF), or End Relative to Reference Period (--ENRTPT) should not be missing,
 OR Occurrence (--OCCUR) = 'N' or Completion Status (--STAT) != '') or Duration (--DUR) != ''). :)
(: Applicable to INTERVENTIONS, EVENTS :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external;
declare variable $defineversion external;
(:  declare variable $datasetname external; :)
(: find all dataset definitions  :)
(:  let $base := '/db/fda_submissions/cdisc01/'  
let $define := 'define2-0-0-example-sdtm.xml' :)
(: let $datasetname := 'AE' :)
(: get the define.xml dataset definition :)
let $definedoc := doc(concat($base,$define))
(: iterate over all dataset definitions in EVENTS and INTERVENTIONS :)
for $datasetdef in $definedoc//odm:ItemGroupDef[upper-case(@def:Class)='INTERVENTIONS' or upper-case(@def:Class)='EVENTS' 
		or upper-case(./def21:Class/@Name)='INTERVENTIONS' or upper-case(./def21:Class/@Name)='EVENTS']
    (: get the name of the dataset :)
    let $name := $datasetdef/@Name
    let $domain := $datasetdef/@Domain
    (: get the dataset location :)
	let $datasetlocation := (
		if($defineversion = '2.1') then $datasetdef/def21:leaf/@xlink:href
		else $datasetdef/def:leaf/@xlink:href
	)
    (: and the dataset document itself :)
    let $datasetdoc := doc(concat($base,$datasetlocation))
    (: get the OIDs of --ENDTC, --ENRF, --ENRTPT, and their name:)
    let $endtcoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'ENDTC')]/@OID 
        where $a = $datasetdef/odm:ItemRef/@ItemOID 
        return $a 
    ) 
    let $endtcname := concat($domain,'ENDTC') (: we use 'concat' for the case ENDTC is not defined at all :)
    let $enrfoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'ENRF')]/@OID 
        where $a = $datasetdef/odm:ItemRef/@ItemOID 
        return $a 
    ) 
    let $enrfname := concat($domain,'ENRF')  (: we use 'concat' for the case ENRF is not defined at all :)
    let $enrtptoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'ENRTPT')]/@OID 
        where $a = $datasetdef/odm:ItemRef/@ItemOID 
        return $a 
    ) 
    let $enrtptname := concat($domain,'ENRTPT')  (: we use 'concat' for the case ENRTPT is not defined at all :)
    (: return <test>{data($endtcoid)}  - {data($enrfoid)} - {data($enrtptoid)}</test> :)
    (: and the OIDs of --STAT and --OCCUR and --DUR :)  
    let $statoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'STAT')]/@OID 
        where $a = $datasetdef/odm:ItemRef/@ItemOID 
        return $a 
    )
    let $occuroid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'OCCUR')]/@OID 
        where $a = $datasetdef/odm:ItemRef/@ItemOID 
        return $a 
    )
	let $duroid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'DUR')]/@OID 
        where $a = $datasetdef/odm:ItemRef/@ItemOID 
        return $a 
    )
    (: return <test>{data($statoid)}  - {data($occuroid)}</test> :)
    (: now iterate over all the records in the dataset 
    for which --OCCUR is NOT 'N' or --STAT is null or empty or --DUR is null or empty :)
	for $record in $datasetdoc//odm:ItemGroupData[not(odm:ItemData[@ItemOID=$statoid]) or not(odm:ItemData[@ItemOID=$duroid]) or odm:ItemData[@ItemOID=$occuroid]/@Value='N' ]
	let $recnum := $record/@data:ItemGroupDataSeq
	(: one of --ENDTC, --ENRF or --ENRTPT must be populated - if none of them is, give warning :)
	where not($record/odm:ItemData[@ItemOID=$endtcoid]/@Value) and not($record/odm:ItemData[@ItemOID=$enrfoid]/@Value) and not($record/odm:ItemData[@ItemOID=$enrtptoid]/@Value) 
	return <warning rule="SD0021" dataset="{data($name)}" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">One of the End Time-Point variables {data($endtcname)}, {data($enrfname)} or {data($enrtptname)} must be populated</warning>
]]>
</rulexquery>
</sdsrule>

<sdsrule id="SD0021-SD" last-update="2020-08-08" originator="FDA" standard="SDTM" requiresDomainOrDataset="Yes">
<ruledescription>An End Time-Point value must be populated</ruledescription>
<ruledetaileddescription>One End Time-Point variable is expected to be populated when an event or an intervention occurred. 
(E.g., End Date/Time of Event or Intervention (--ENDTC), or End Relative to Reference Period (--ENRF), or End Relative to Reference Period (--ENRTPT) should not be missing, 
or Occurrence (--OCCUR) = 'N' or Completion Status (--STAT) != '') or Duration (--DUR) != '')</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>EVENTS</domain>
<domain>INTERVENTIONS</domain>
<rulexquery><![CDATA[
(: Rule SD0021: One End Time-Point variable is expected to be populated when an event or an intervention occurred. 
 e.g./i.e., End Date/Time of Event or Intervention (--ENDTC), or End Relative to Reference Period (--ENRF), or End Relative to Reference Period (--ENRTPT) should not be missing,
 OR Occurrence (--OCCUR) = 'N' or Completion Status (--STAT) != '') or Duration (--DUR) != ''). :)
(: Applicable to INTERVENTIONS, EVENTS :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external;
declare variable $defineversion external;
declare variable $datasetname external;
(: find all dataset definitions  :)
(:  let $base := '/db/fda_submissions/cdisc01/'  
let $define := 'define2-0-0-example-sdtm.xml' :)
(: let $datasetname := 'AE' :)
(: get the define.xml dataset definition :)
let $definedoc := doc(concat($base,$define))
(: iterate over all the provided dataset definitions in EVENTS and INTERVENTIONS :)
for $datasetdef in $definedoc//odm:ItemGroupDef[@Name=$datasetname][upper-case(@def:Class)='INTERVENTIONS' or upper-case(@def:Class)='EVENTS' 
		or upper-case(./def21:Class/@Name)='INTERVENTIONS' or upper-case(./def21:Class/@Name)='EVENTS' ]
    (: get the name of the dataset :)
    let $name := $datasetdef/@Name
    let $domain := $datasetdef/@Domain
    (: get the dataset location :)
	let $datasetlocation := (
		if($defineversion = '2.1') then $datasetdef/def21:leaf/@xlink:href
		else $datasetdef/def:leaf/@xlink:href
	)
    (: and the dataset document itself :)
    let $datasetdoc := doc(concat($base,$datasetlocation))
    (: get the OIDs of --ENDTC, --ENRF, --ENRTPT, and their name:)
    let $endtcoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'ENDTC')]/@OID 
        where $a = $datasetdef/odm:ItemRef/@ItemOID 
        return $a 
    ) 
    let $endtcname := concat($domain,'ENDTC') (: we use 'concat' for the case ENDTC is not defined at all :)
    let $enrfoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'ENRF')]/@OID 
        where $a = $datasetdef/odm:ItemRef/@ItemOID 
        return $a 
    ) 
    let $enrfname := concat($domain,'ENRF')  (: we use 'concat' for the case ENRF is not defined at all :)
    let $enrtptoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'ENRTPT')]/@OID 
        where $a = $datasetdef/odm:ItemRef/@ItemOID 
        return $a 
    ) 
    let $enrtptname := concat($domain,'ENRTPT')  (: we use 'concat' for the case ENRTPT is not defined at all :)
    (: return <test>{data($endtcoid)}  - {data($enrfoid)} - {data($enrtptoid)}</test> :)
    (: and the OIDs of --STAT and --OCCUR and --DUR :)  
    let $statoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'STAT')]/@OID 
        where $a = $datasetdef/odm:ItemRef/@ItemOID 
        return $a 
    )
    let $occuroid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'OCCUR')]/@OID 
        where $a = $datasetdef/odm:ItemRef/@ItemOID 
        return $a 
    )
	let $duroid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'DUR')]/@OID 
        where $a = $datasetdef/odm:ItemRef/@ItemOID 
        return $a 
    )
    (: return <test>{data($statoid)}  - {data($occuroid)}</test> :)
    (: now iterate over all the records in the dataset 
    for which --OCCUR is NOT 'N' or --STAT is null or empty or --DUR is null or empty :)
	for $record in $datasetdoc//odm:ItemGroupData[not(odm:ItemData[@ItemOID=$statoid]) or not(odm:ItemData[@ItemOID=$duroid]) or odm:ItemData[@ItemOID=$occuroid]/@Value='N' ]
	let $recnum := $record/@data:ItemGroupDataSeq
	(: one of --ENDTC, --ENRF or --ENRTPT must be populated - if none of them is, give warning :)
	where not($record/odm:ItemData[@ItemOID=$endtcoid]/@Value) and not($record/odm:ItemData[@ItemOID=$enrfoid]/@Value) and not($record/odm:ItemData[@ItemOID=$enrtptoid]/@Value) 
	return <warning rule="SD0021" dataset="{data($name)}" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">One of the End Time-Point variables {data($endtcname)}, {data($enrfname)} or {data($enrtptname)} must be populated</warning>
]]>
</rulexquery>
</sdsrule>

<sdsrule id="SD0022" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>Start Time-Point value must be populated</ruledescription>
<ruledetaileddescription>One of Start Time-Point variables values is expected to be populated when an event an intervention occurred. (E.g., one of Start Date/Time of Event or Intervention (--STDTC), Start Relative to Reference Period (--STRF), and Start Relative to Reference Period (--STRTPT) variables values should not be missing, or Occurrence (--OCCUR) variable value should be 'N')</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>EVENTS</domain>
<domain>INTERVENTIONS</domain>
<rulexquery><![CDATA[
(: Rule SD0022: One of Start Time-Point variables values is expected to be populated when an event an intervention occurred. 
 E.g./i.e., one of Start Date/Time of Event or Intervention (--STDTC), Start Relative to Reference Period (--STRF), and Start Relative to Reference Period (--STRTPT) variables values should not be missing, or Occurrence (--OCCUR) variable value should be 'N') :)
(: Applicable to INTERVENTIONS, EVENTS :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external;
declare variable $defineversion external;
(:  declare variable $datasetname external; :)
(: find all dataset definitions  :)
(: let $base := '/db/fda_submissions/cdisc01/'  
let $define := 'define2-0-0-example-sdtm.xml' :)
(: let $datasetname := 'AE' :)
(: get the define.xml dataset definition :)
let $definedoc := doc(concat($base,$define))
(: iterate over all dataset definitions in EVENTS and INTERVENTIONS :)
for $datasetdef in $definedoc//odm:ItemGroupDef[upper-case(@def:Class)='INTERVENTIONS' or upper-case(@def:Class)='EVENTS' 
		or upper-case(./def21:Class/@Name)='INTERVENTIONS' or upper-case(./def21:Class/@Name)='EVENTS']
    (: get the name of the dataset :)
    let $name := $datasetdef/@Name
    let $domain := $datasetdef/@Domain
    (: get the dataset location :)
	let $datasetlocation := (
		if($defineversion = '2.1') then $datasetdef/def21:leaf/@xlink:href
		else $datasetdef/def:leaf/@xlink:href
	)
    (: and the dataset document itself :)
    let $datasetdoc := doc(concat($base,$datasetlocation))
    (: get the OIDs of --STDTC, --STRF, --STRTPT, and their name:)
    let $stdtcoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'STDTC')]/@OID 
        where $a = $datasetdef/odm:ItemRef/@ItemOID 
        return $a 
    ) 
    let $stdtcname := concat($domain,'STDTC') (: we use 'concat' for the case STDTC is not defined at all :)
    let $strfoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'STRF')]/@OID 
        where $a = $datasetdef/odm:ItemRef/@ItemOID 
        return $a 
    ) 
    let $strfname := concat($domain,'STRF')  (: we use 'concat' for the case STRF is not defined at all :)
    let $strtptoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'STRTPT')]/@OID 
        where $a = $datasetdef/odm:ItemRef/@ItemOID 
        return $a 
    ) 
    let $strtptname := concat($domain,'STRTPT')  (: we use 'concat' for the case STRTPT is not defined at all :)
    (: return <test>{data($stdtcoid)}  - {data($strfoid)} - {data($strtptoid)}</test> :)
    (: and the OIDs of --STAT and --OCCUR :)  
    let $statoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'STAT')]/@OID 
        where $a = $datasetdef/odm:ItemRef/@ItemOID 
        return $a 
    )
    let $occuroid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'OCCUR')]/@OID 
        where $a = $datasetdef/odm:ItemRef/@ItemOID 
        return $a 
    )
    (: return <test>{data($statoid)}  - {data($occuroid)}</test> :)
    (: now iterate over all the records in the dataset 
    for which --OCCUR is NOT 'N' :)
    for $record in $datasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$occuroid]/@Value='N']
        let $recnum := $record/@data:ItemGroupDataSeq
        (: one of --STDTC, --STENRF or -STRTPT must be populated - if none of them is, give warning :)
        where not($record/odm:ItemData[@ItemOID=$stdtcoid]/@Value) and not($record/odm:ItemData[@ItemOID=$strfoid]/@Value) and not($record/odm:ItemData[@ItemOID=$strtptoid]/@Value)
        return <warning rule="SD0022" dataset="{$name}" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">One of the End Time-Point variables {data($stdtcname)}, {data($strfname)} or {data($strtptname)} must be populated</warning>
]]>
</rulexquery>
</sdsrule>

<sdsrule id="SD0022-SD" last-update="2020-08-08" originator="FDA" standard="SDTM" requiresDomainOrDataset="Yes">
<ruledescription>A Start Time-Point must be populated</ruledescription>
<ruledetaileddescription>One of Start Time-Point variables values is expected to be populated when an event an intervention occurred. (E.g., one of Start Date/Time of Event or Intervention (--STDTC), Start Relative to Reference Period (--STRF), and Start Relative to Reference Period (--STRTPT) variables values should not be missing, or Occurrence (--OCCUR) variable value should be 'N')</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>EVENTS</domain>
<domain>INTERVENTIONS</domain>
<rulexquery><![CDATA[
(: Rule SD0022: One of Start Time-Point variables values is expected to be populated when an event an intervention occurred. 
 E.g./i.e., one of Start Date/Time of Event or Intervention (--STDTC), Start Relative to Reference Period (--STRF), and Start Relative to Reference Period (--STRTPT) variables values should not be missing, or Occurrence (--OCCUR) variable value should be 'N') :)
(: Applicable to INTERVENTIONS, EVENTS :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external;
declare variable $defineversion external;
declare variable $datasetname external; 
(: find all dataset definitions  :)
(: let $base := '/db/fda_submissions/cdisc01/'  
let $define := 'define2-0-0-example-sdtm.xml' :)
(: let $datasetname := 'AE' :)
(: get the define.xml dataset definition :)
let $definedoc := doc(concat($base,$define))
(: iterate over all the provided dataset definitions in EVENTS and INTERVENTIONS :)
for $datasetdef in $definedoc//odm:ItemGroupDef[@Name=$datasetname][upper-case(@def:Class)='INTERVENTIONS' or upper-case(@def:Class)='EVENTS' 
		or upper-case(./def21:Class/@Name)='INTERVENTIONS' or upper-case(./def21:Class/@Name)='EVENTS']
    (: get the name of the dataset :)
    let $name := $datasetdef/@Name
    let $domain := $datasetdef/@Domain
    (: get the dataset location :)
	let $datasetlocation := (
		if($defineversion = '2.1') then $datasetdef/def21:leaf/@xlink:href
		else $datasetdef/def:leaf/@xlink:href
	)
    (: and the dataset document itself :)
    let $datasetdoc := doc(concat($base,$datasetlocation))
    (: get the OIDs of --STDTC, --STRF, --STRTPT, and their name:)
    let $stdtcoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'STDTC')]/@OID 
        where $a = $datasetdef/odm:ItemRef/@ItemOID 
        return $a 
    ) 
    let $stdtcname := concat($domain,'STDTC') (: we use 'concat' for the case STDTC is not defined at all :)
    let $strfoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'STRF')]/@OID 
        where $a = $datasetdef/odm:ItemRef/@ItemOID 
        return $a 
    ) 
    let $strfname := concat($domain,'STRF')  (: we use 'concat' for the case STRF is not defined at all :)
    let $strtptoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'STRTPT')]/@OID 
        where $a = $datasetdef/odm:ItemRef/@ItemOID 
        return $a 
    ) 
    let $strtptname := concat($domain,'STRTPT')  (: we use 'concat' for the case STRTPT is not defined at all :)
    (: return <test>{data($stdtcoid)}  - {data($strfoid)} - {data($strtptoid)}</test> :)
    (: and the OIDs of --STAT and --OCCUR :)  
    let $statoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'STAT')]/@OID 
        where $a = $datasetdef/odm:ItemRef/@ItemOID 
        return $a 
    )
    let $occuroid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'OCCUR')]/@OID 
        where $a = $datasetdef/odm:ItemRef/@ItemOID 
        return $a 
    )
    (: return <test>{data($statoid)}  - {data($occuroid)}</test> :)
    (: now iterate over all the records in the dataset 
    for which --OCCUR is NOT 'N' :)
    for $record in $datasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$occuroid]/@Value='N']
        let $recnum := $record/@data:ItemGroupDataSeq
        (: one of --STDTC, --STENRF or -STRTPT must be populated - if none of them is, give warning :)
        where not($record/odm:ItemData[@ItemOID=$stdtcoid]/@Value) and not($record/odm:ItemData[@ItemOID=$strfoid]/@Value) and not($record/odm:ItemData[@ItemOID=$strtptoid]/@Value)
        return <warning rule="SD0022" dataset="{$name}" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">One of the Start Time-Point variables {data($stdtcname)}, {data($strfname)} or {data($strtptname)} must be populated</warning>
]]>
</rulexquery>
</sdsrule>

<sdsrule id="SD0024" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>--DTC must be provided, when --ENDTC is provided</ruledescription>
<ruledetaileddescription>Date/Time of Collection (--DTC) should not be NULL, when End Date/Time of Observation (--ENDTC) is not NULL</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>FINDINGS</domain>
<rulexquery><![CDATA[	
(: TODO - not well tested yet :)
(: Rule SD0024 - Missing value for --DTC, when --ENDTC is provided:
Date/Time of Collection (--DTC) should not be NULL, when End Date/Time of Observation (--ENDTC) is not NULL
Findings domains
:)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: iterate over all the FINDINGS datasets :)
for $dataset in $definedoc//odm:ItemGroupDef[upper-case(@def:Class)='FINDINGS' or upper-case(./def21:Class/@Name)='FINDINGS']
    let $name := $dataset/@Name
    let $datasetname := (
		if($defineversion = '2.1') then $dataset/def21:leaf/@xlink:href
		else $dataset/def:leaf/@xlink:href
	)
    let $datasetdoc := (
		if($datasetname) then doc(concat($base,$datasetname))
		else ()
	)
    (: get the OID of the ENDTC variable :)
    let $endtcoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'ENDTC')]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
    )
    let $endtcname := $definedoc//odm:ItemDef[@OID=$endtcoid]/@Name
    (: and the OID of the DTC variable (5 characters) :)
    let $dtcoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'DTC') and string-length(@Name)=5]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
    )
    let $dtcname := $definedoc//odm:ItemDef[@OID=$dtcoid]/@Name
    (: now iterate over all records in the dataset that have an ENDTC that is not null :)
    for $record in $datasetdoc//odm:ItemGroupData[odm:ItemData/@ItemOID=$endtcoid]
        let $recnum := $record/@data:ItemGroupDataSeq
        (: get the values of the ENDTC and DTC variables (if any) :)
        let $endtcvalue := $record/odm:ItemData[@ItemOID=$endtcoid]/@Value
        let $dtcvalue := $record/odm:ItemData[@ItemOID=$dtcoid]/@Value
        (: check whether there is a DTC value when an ENDTC value is present  :)
        (: where $endtcvalue and not($dtcvalue) :)
        return <warning rule="SD0024" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">Missing value for {data($dtcname)}, when {data($endtcname)} is provided, value={data($endtcvalue)}</warning>
]]></rulexquery>
</sdsrule>

<sdsrule id="SD0024-SD" last-update="2020-08-08" originator="FDA" standard="SDTM" requiresDomainOrDataset="Yes">
<ruledescription>--DTC must be provided, when --ENDTC is provided</ruledescription>
<ruledetaileddescription>Date/Time of Collection (--DTC) should not be NULL, when End Date/Time of Observation (--ENDTC) is not NULL</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>FINDINGS</domain>
<rulexquery><![CDATA[	
(: TODO - not well tested yet :)
(: Rule SD0024 - Missing value for --DTC, when --ENDTC is provided:
Date/Time of Collection (--DTC) should not be NULL, when End Date/Time of Observation (--ENDTC) is not NULL
Findings domains
:)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
declare variable $datasetname external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
(: let $datasetname := 'LB' :)
let $definedoc := doc(concat($base,$define))
(: iterate over all the FINDINGS datasets :)
for $dataset in $definedoc//odm:ItemGroupDef[@Name=$datasetname][upper-case(@def:Class)='FINDINGS' or upper-case(./def21:Class/@Name)='FINDINGS']
    let $name := $dataset/@Name
    let $dsname := (
		if($defineversion = '2.1') then $dataset/def21:leaf/@xlink:href
		else $dataset/def:leaf/@xlink:href
	)
    let $datasetdoc := (
		if($dsname) then doc(concat($base,$dsname))
		else ()
	)
    (: get the OID of the ENDTC variable :)
    let $endtcoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'ENDTC')]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
    )
    let $endtcname := $definedoc//odm:ItemDef[@OID=$endtcoid]/@Name
    (: and the OID of the DTC variable (5 characters) :)
    let $dtcoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'DTC') and string-length(@Name)=5]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
    )
    let $dtcname := $definedoc//odm:ItemDef[@OID=$dtcoid]/@Name
    (: now iterate over all records in the dataset that have an ENDTC that is not null :)
    for $record in $datasetdoc//odm:ItemGroupData[odm:ItemData/@ItemOID=$endtcoid]
        let $recnum := $record/@data:ItemGroupDataSeq
        (: get the values of the ENDTC and DTC variables (if any) :)
        let $endtcvalue := $record/odm:ItemData[@ItemOID=$endtcoid]/@Value
        let $dtcvalue := $record/odm:ItemData[@ItemOID=$dtcoid]/@Value
        (: check whether there is a DTC value when an ENDTC value is present  :)
        (: where $endtcvalue and not($dtcvalue) :)
        return <warning rule="SD0024" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">Missing value for {data($dtcname)}, when {data($endtcname)} is provided, value={data($endtcvalue)}</warning>	
]]></rulexquery>
</sdsrule>

<sdsrule id="SD1118" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>One of --STDTC, --DTC or --STDY must be populated in DS</ruledescription>
<ruledetaileddescription>At least one value of three variables Start Date/Time (--STDTC), Collection Date/Time (--DTC) or Start Study Day (--STDY) should be populated</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>DS</domain>
<rulexquery><![CDATA[	
(: Rule SD1118 - Neither --STDTC, --DTC nor  --STDY are populated in DS:
At least one value of two variables Start Date/Time (--STDTC) and Start Study Day (--STDY) should be populated
:)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink"; 
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/'  :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: get the location of the DS dataset :)
let $dsdataset := $definedoc//odm:ItemGroupDef[@Name='DS']
let $dsdatasetname := (
	if($defineversion = '2.1') then $dsdataset/def21:leaf/@xlink:href
	else $dsdataset/def:leaf/@xlink:href
)
let $dsdatasetlocation := concat($base,$dsdatasetname)
(: Get the OID of the DSSTDTC, DSDTC and DSSTDY and DS variables :)
let $dssdttcoid := (
    for $a in $definedoc//odm:ItemDef[@Name='DSSTDTC']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name='DS']/odm:ItemRef/@ItemOID
        return $a  
)
let $dsstdyoid := (
    for $a in $definedoc//odm:ItemDef[@Name='DSSTDY']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name='DS']/odm:ItemRef/@ItemOID
        return $a    
)
let $dsdtcoid := (
	for $a in $definedoc//odm:ItemDef[@Name='DSDTC']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name='DS']/odm:ItemRef/@ItemOID
        return $a 
)
(: iterate over all the records in the dataset :)
for $record in doc($dsdatasetlocation)//odm:ItemGroupData
    let $recnum := $record/@data:ItemGroupDataSeq
    (: and the values of the DSSDTDTC and DSSTDY and DSDTC data points (when present) :)
    let $dsstdtcvalue := $record/odm:ItemData[@ItemOID=$dssdttcoid]/@Value
    let $dsstdyvalue := $record/odm:ItemData[@ItemOID=$dsstdyoid]/@Value
	let $dsdtcvalue := $record/odm:ItemData[@ItemOID=$dsdtcoid]/@Value
    (: at least one of the three must be present :)
    where not($dsstdtcvalue) and not($dsstdyvalue) and not($dsdtcvalue)
    return <warning rule="SD1118" dataset="DS" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">Neither DSSDTDTC nor DSSTDY nor DSDTC are populated in dataset {data($dsdatasetname)}</warning>	
]]></rulexquery>
</sdsrule>

<sdsrule id="SD1209" last-update="2020-08-08" originator="FDA" standard="SDTM" requiresDomainOrDataset="Yes">
<ruledescription>RFXENDTC must be provided, when RFXSTDTC is provided</ruledescription>
<ruledetaileddescription>Value for Date/Time of Last Study Treatment (RFXENDTC) variable must be populated, when Date/Time of First Study Treatment (RFXSTDTC) variable value is provided</ruledetaileddescription>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>DM</domain>
<rulexquery><![CDATA[
(: Rule SD1209: Missing value for RFXENDTC, when RFXSTDTC is provided - Value for Date/Time of Last Study Treatment (RFXENDTC) variable must be populated, when Date/Time of First Study Treatment (RFXSTDTC) variable value is provided  :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink"; 
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/'  
let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: get the location of the DM dataset :)
let $dmdatasetdef := $definedoc//odm:ItemGroupDef[@Name='DM']
let $dmdatasetlocation := (
	if($defineversion = '2.1') then $dmdatasetdef/def21:leaf/@xlink:href
	else $dmdatasetdef/def:leaf/@xlink:href
)
(: Get the OID of the RFXENDTC and RFXSTDTC variables :)
let $rfxendtcoid := (
    for $a in $definedoc//odm:ItemDef[@Name='RFXENDTC']/@OID 
        where $a = $dmdatasetdef/odm:ItemRef/@ItemOID
        return $a  
)
let $rfxstdtcoid := (
    for $a in $definedoc//odm:ItemDef[@Name='RFXSTDTC']/@OID 
        where $a = $dmdatasetdef/odm:ItemRef/@ItemOID
        return $a    
)
(: iterate over all the records in the DM dataset that have RFXSTDTC populated :)
for $record in doc(concat($base,$dmdatasetlocation))//odm:ItemGroupData[odm:ItemData[@ItemOID=$rfxstdtcoid]/@Value]
    let $recnum := $record/@data:ItemGroupDataSeq
    (: give an warning when RFXENDTC is not populated :)
    where not($record/odm:ItemData[@ItemOID=$rfxendtcoid])
    return <warning rule="SD1209" dataset="DM" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}" variable="RFXENDTC">RFXENDTC is not populated although RFXSTDTC is populated</warning>	
]]>
</rulexquery>
</sdsrule>


<sdsrule id="SD1213" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>RFSTDTC must be provided for a randomized subject</ruledescription>
<ruledetaileddescription>Subject Reference Start Date/Time (RFSTDTC) should be populated for all randomized subjects, those where Planned Arm Code (ARMCD) is not equal to 'SCRNFAIL' or 'NOTASSGN'</ruledetaileddescription>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>DM</domain>
<rulexquery><![CDATA[	
(: Rule SD1213 - RFSTDTC is not provided for a randomized subject
Subject Reference Start Date/Time (RFSTDTC) should be populated for all randomized subjects, those where Planned Arm Code (ARMCD) is not equal to 'SCRNFAIL' or 'NOTASSGN'
:)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: get the DM dataset :)
let $dmdataset := $definedoc//odm:ItemGroupDef[@Name='DM']
let $dmdatasetname := (
	if($defineversion = '2.1') then $dmdataset/def21:leaf/@xlink:href
	else $dmdataset/def:leaf/@xlink:href
)
let $dmdatasetlocation := concat($base,$dmdatasetname)
(: and get the OIDs of USUBJID and ARMCD :)
let $usubjidoid := (
    for $a in $definedoc//odm:ItemDef[@Name='USUBJID']/@OID 
    where $a = $definedoc//odm:ItemGroupDef[@Name='DM']/odm:ItemRef/@ItemOID
    return $a
)
let $armcdoid := (
    for $a in $definedoc//odm:ItemDef[@Name='ARMCD']/@OID 
    where $a = $definedoc//odm:ItemGroupDef[@Name='DM']/odm:ItemRef/@ItemOID
    return $a
)
(: and the OID for RFSTDTC :)
let $rfstdtcoid := (
    for $a in $definedoc//odm:ItemDef[@Name='RFSTDTC']/@OID 
    where $a = $definedoc//odm:ItemGroupDef[@Name='DM']/odm:ItemRef/@ItemOID
    return $a   
)
(: iterate over all records in the DM dataset :)
for $record in doc($dmdatasetlocation)//odm:ItemGroupData
    let $recnum := $record/@data:ItemGroupDataSeq
    (: and get the values for USUBJID and ARMCD and RFSTDTC :)
    let $armcdvalue := $record/odm:ItemData[@ItemOID=$armcdoid]/@Value
    let $rfstdtcvalue := $record/odm:ItemData[@ItemOID=$rfstdtcoid]/@Value
    let $usubjidvalue := $record/odm:ItemData[@ItemOID=$usubjidoid]/@Value
    (: except for the case that ARMCD=SCRNFAIL or NOTASSGN, there MUST be an RFSTDTC :)
    where not($armcdvalue = 'SCRNFAIL') and not($armcdvalue='NOTASSGN') and not($rfstdtcvalue) 
    return <warning rule="SD1213" dataset="DM" variable="RFSTDTC" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">RFSTDTC is not provided for a randomized subject with USUBJID={data($usubjidvalue)}</warning>
]]></rulexquery>
</sdsrule>

<!-- 2020-08-08 evening Rule SD1011 "Invalid ISO 8601 value for variable":
Value of Duration, Elapsed Time, and Interval variables (-DUR, -ELTM, -EVLINT) must conform to the ISO 8601 international standard
 -->
<sdsrule id="SD1011" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>Value of --DUR, --ELTM, --EVLINT must conform to the ISO 8601 'duration' international standard</ruledescription>
<ruledetaileddescription>Value of Duration, Elapsed Time, and Interval variables (--DUR, --ELTM, --EVLINT) must conform to the ISO 8601 international standard</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>ALL</domain>
<rulexquery><![CDATA[
(: Rule SD1011 - Value --DUR, --ELTM, --EVLINT must conform to the ISO 8601 "duration" international standard
Value of Duration, Elapsed Time, and Interval variables (--DUR, --ELTM, --EVLINT) must conform to the ISO 8601 international standard
:)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
declare namespace xs="http://www.w3.org/2001/XMLSchema";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' 
let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: iterate over ALL datasets :)
for $dataset in $definedoc//odm:ItemGroupDef
let $datasetname := (
	if($defineversion = '2.1') then $dataset/def21:leaf/@xlink:href
	else $dataset/def:leaf/@xlink:href
)
let $datasetlocation := concat($base,$datasetname)
(: get the OIDs of the --DUR, --ELTM and --EVLINT variables.
 Also get the variable name :)
let $duroid := (
    for $a in $definedoc//odm:ItemDef[ends-with(@Name,'DUR')]/@OID 
    where $a = $dataset/odm:ItemRef/@ItemOID
    return $a
)
let $durname := $definedoc//odm:ItemDef[@OID=$duroid]/@Name
let $eltmoid := (
    for $a in $definedoc//odm:ItemDef[ends-with(@Name,'ELTM')]/@OID 
    where $a = $dataset/odm:ItemRef/@ItemOID
    return $a
)
let $eltmname := $definedoc//odm:ItemDef[@OID=$eltmoid]/@Name
let $evlintoid := (
    for $a in $definedoc//odm:ItemDef[ends-with(@Name,'EVLINT')]/@OID 
    where $a = $dataset/odm:ItemRef/@ItemOID
    return $a
)
let $evlintname := $definedoc//odm:ItemDef[@OID=$evlintoid]/@Name
(:  return <test>{data($durname)} - {data($eltmname)} - {data($evlintname)}</test> :)
(: iterate over all records that have either a value for --DUR, --ELTM or --EVLINT :)
for $record in doc($datasetlocation)[$duroid or $eltmoid or $evlintoid]//odm:ItemGroupData
    (: get the record number :)
    let $recnum := $record/@data:ItemGroupDataSeq
    (: iterate over all the datapoints --DUR, --ELTM, --EVLINT :)
    let $durdatapoints := $record/odm:ItemData[@ItemOID=$duroid or @ItemOID=$eltmoid or @ItemOID=$evlintoid]
    for $datapoint in $durdatapoints
        (: get the value :)
        let $value := $datapoint/@Value
        (: we need the name of the datapoint in the define.xml for reporting :)
        let $itemoid := $datapoint/@ItemOID
        let $itemname := $definedoc//odm:ItemDef[@OID=$itemoid]/@Name
        (: the value must be a valid ISO-8601 "duration" :)
        (: IMPORTANT: PnW and -PnW are rejected as valid ISO-8601 durations, so we need to take care of that :)
        (: TODO: check the "n" is an integer :)
        where not($value castable as xs:duration) and not((starts-with($value,'P') or starts-with($value,'-P')) and ends-with($value,'W'))
        return <error rule="SD1011" dataset="{data($datasetname)}" variable="{$itemname}" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">Invalid (non-ISO8601 duration) value='{data($value)}'</error>
]]>
</rulexquery>
</sdsrule>

<sdsrule id="SD0028" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>Value for --STNRHI may not be less than value for --STNRLO</ruledescription>
<ruledetaileddescription>Reference Range Upper Limit-Std Units (--STNRHI) value must be greater than or equal to Reference Range Lower Limit-Std Units (--STNRLO) value</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>FINDINGS</domain>
<rulexquery><![CDATA[
(: Rule SD0028 - Value for --STNRHI is less than value for --STNRLO:
Reference Range Upper Limit-Std Units (--STNRHI) value must be greater than or equal to Reference Range Lower Limit-Std Units (--STNRLO) value
:)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: iterate over all FINDINGS datasets :)
for $dataset in $definedoc//odm:ItemGroupDef[upper-case(@def:Class)='FINDINGS' or upper-case(./def21:Class/@Name)='FINDINGS']
    let $name := $dataset/@Name
	let $datasetname := (
		if($defineversion = '2.1') then $dataset/def21:leaf/@xlink:href
		else $dataset/def:leaf/@xlink:href
	)
    let $datasetdoc := (
		if($datasetname) then doc(concat($base,$datasetname))
		else ()
	)
    (: get the OID of the STNRHI and STNRLO variables :)
    let $stnrhioid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'STNRHI')]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
    )
    let $stnrihname := $definedoc//odm:ItemDef[@OID=$stnrhioid]/@Name
    let $stnrlooid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'STNRLO')]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
    )
    let $stnrloname := $definedoc//odm:ItemDef[@OID=$stnrlooid]/@Name
    (: iterate over all records for which there is as well a populated STNRHI as STNTLO data point :)
    for $record in $datasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$stnrlooid] and odm:ItemData[@ItemOID=$stnrhioid]][@data:ItemGroupDataSeq<10]
        let $recnum := $record/@data:ItemGroupDataSeq
        (: get both the values :)
        let $stnrlovalue := $record/odm:ItemData[@ItemOID=$stnrlooid]/@Value
        let $stnrhivalue := $record/odm:ItemData[@ItemOID=$stnrhioid]/@Value
        (: convert to numeric and compare both values :)
        (: --STNRHI must not be smaller than STNRLO :)
        where number($stnrhivalue) < number($stnrlovalue)
        return <error rule="SD0028" dataset="{data($name)}" variable="{data(stnrihname)}" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">Value for {data($stnrihname)}, value={data($stnrhivalue)} is less than value for {data($stnrloname)}, value={data($stnrlovalue)}</error>
]]></rulexquery>
</sdsrule>

<sdsrule id="SD0028-SD" last-update="2020-08-08" originator="FDA" standard="SDTM" requiresDomainOrDataset="Yes">
<ruledescription>Value for --STNRHI may not be less than value for --STNRLO</ruledescription>
<ruledetaileddescription>Reference Range Upper Limit-Std Units (--STNRHI) value must be greater than or equal to Reference Range Lower Limit-Std Units (--STNRLO) value</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>FINDINGS</domain>
<rulexquery><![CDATA[
(: Rule SD0028 - Value for --STNRHI is less than value for --STNRLO:
Reference Range Upper Limit-Std Units (--STNRHI) value must be greater than or equal to Reference Range Lower Limit-Std Units (--STNRLO) value
:)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
declare variable $datasetname external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: iterate over all FINDINGS datasets :)
for $dataset in $definedoc//odm:ItemGroupDef[@Name=$datasetname][upper-case(@def:Class)='FINDINGS' or upper-case(./def21:Class/@Name)='FINDINGS']
    let $name := $dataset/@Name
    let $dsname := (
		if($defineversion = '2.1') then $dataset/def21:leaf/@xlink:href
		else $dataset/def:leaf/@xlink:href
	)
    let $datasetdoc := (
		if($dsname) then doc(concat($base,$dsname))
		else ()
	)
    (: get the OID of the STNRHI and STNRLO variables :)
    let $stnrhioid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'STNRHI')]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
    )
    let $stnrihname := $definedoc//odm:ItemDef[@OID=$stnrhioid]/@Name
    let $stnrlooid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'STNRLO')]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
    )
    let $stnrloname := $definedoc//odm:ItemDef[@OID=$stnrlooid]/@Name
    (: iterate over all records for which there is as well a populated STNRHI as STNTLO data point :)
    for $record in $datasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$stnrlooid] and odm:ItemData[@ItemOID=$stnrhioid]][@data:ItemGroupDataSeq<10]
        let $recnum := $record/@data:ItemGroupDataSeq
        (: get both the values :)
        let $stnrlovalue := $record/odm:ItemData[@ItemOID=$stnrlooid]/@Value
        let $stnrhivalue := $record/odm:ItemData[@ItemOID=$stnrhioid]/@Value
        (: convert to numeric and compare both values :)
        (: --STNRHI must not be smaller than STNRLO :)
        where number($stnrhivalue) < number($stnrlovalue)
        return <error rule="SD0028" dataset="{data($name)}" variable="{data(stnrihname)}" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">Value for {data($stnrihname)}, value={data($stnrhivalue)} is less than value for {data($stnrloname)}, value={data($stnrlovalue)}</error>
]]></rulexquery>
</sdsrule>


<sdsrule id="SD0014" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>Value for –DOSE may not be negative</ruledescription>
<ruledetaileddescription>Non-missing Dose (--DOSE) value must be greater than or equal to 0</ruledetaileddescription>
<domain>INTERVENTIONS</domain>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<rulexquery><![CDATA[	
(: Rule SD0014 - Negative value for --DOSE (Intervention domains)
Non-missing Dose (--DOSE) value must be greater than or equal to 0
:)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: iterate over all datasets that have a --DOSE variable - Limit to @def:Class=INTERVENTIONS ? :)
for $dataset in $definedoc//odm:ItemGroupDef[upper-case(@def:Class)='INTERVENTIONS' or upper-case(./def21:Class/@Name)='INTERVENTIONS']
    let $name := $dataset/@Name
    let $datasetname := (
		if($defineversion = '2.1') then $dataset/def21:leaf/@xlink:href
		else $dataset/def:leaf/@xlink:href
	)
    let $datasetlocation := concat($base,$datasetname)
    (: find the OID of the --DOSE variable :)
    let $doseoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'DOSE')]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a 
    )
    let $dosename := $definedoc//odm:ItemDef[@OID=$doseoid]/@Name
    (: now iterate over all in the dataset itself :)
    for $record in doc($datasetlocation)//odm:ItemGroupData
        let $recnum := $record/@data:ItemGroupDataSeq
        (: and get the value :)
        let $dosevalue := $record/odm:ItemData[@ItemOID=$doseoid]/@Value
        (: and check whether >= 0 :)
        where $dosevalue and number($dosevalue) < 0
        return <error rule="SD0014" dataset="{data($name)}" variable="{data($dosename)}" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">Negative value for {data($dosename)} in dataset {data($datasetname)}: {data($dosevalue)} was found</error>	
]]></rulexquery>
</sdsrule>

<sdsrule id="SD0014-SD" last-update="2020-08-08" originator="FDA" standard="SDTM" requiresDomainOrDataset="Yes">
<ruledescription>Value for –DOSE may not be negative</ruledescription>
<ruledetaileddescription>Non-missing Dose (--DOSE) value must be greater than or equal to 0</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>INTERVENTIONS</domain>
<rulexquery><![CDATA[	
(: Rule SD0014 - Negative value for --DOSE (Intervention domains)
Non-missing Dose (--DOSE) value must be greater than or equal to 0
:)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
declare variable $datasetname external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: iterate over all datasets that have a --DOSE variable - Limit to @def:Class=INTERVENTIONS ? :)
for $dataset in $definedoc//odm:ItemGroupDef[@Name=$datasetname][upper-case(@def:Class)='INTERVENTIONS' 
		or upper-case(./def21:Class/@Name)='INTERVENTIONS']
    let $name := $dataset/@Name
    let $dsname := (
		if($defineversion = '2.1') then $dataset/def21:leaf/@xlink:href
		else $dataset/def:leaf/@xlink:href
	)
    let $datasetlocation := concat($base,$dsname)
    (: find the OID of the --DOSE variable :)
    let $doseoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'DOSE')]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a 
    )
    let $dosename := $definedoc//odm:ItemDef[@OID=$doseoid]/@Name
    (: now iterate over all in the dataset itself :)
    for $record in doc($datasetlocation)//odm:ItemGroupData
        let $recnum := $record/@data:ItemGroupDataSeq
        (: and get the value :)
        let $dosevalue := $record/odm:ItemData[@ItemOID=$doseoid]/@Value
        (: and check whether >= 0 :)
        where $dosevalue and number($dosevalue) < 0
        return <error rule="SD0014" dataset="{data($name)}" variable="{data($dosename)}" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">Negative value for {data($dosename)} in dataset {data($datasetname)}: {data($dosevalue)} was found</error>
]]></rulexquery>
</sdsrule>

<!-- Remark: this rule has been made more precise relative to old rule FDAC082:
a) applies to: INTERVENTIONS, EVENTS, FINDINGS, TE, SE, SV  
b) Values for the following variables cannot be negative: Age, Dose, and Duration of Event, Exposure or Observation
-->
<sdsrule id="SD0015" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>Value for --DUR may not be negative</ruledescription>
<ruledetaileddescription>Non-missing Duration of Event, Exposure or Observation (--DUR) value must be greater than or equal to 0</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>INTERVENTIONS</domain>
<domain>EVENTS</domain>
<domain>FINDINGS</domain>
<domain>TE</domain>
<domain>SE</domain>
<domain>SV</domain>
<rulexquery><![CDATA[	
(: Rule SD0015 - Negative value for --DUR (all domains)
Non-missing Duration of Event, Exposure or Observation (--DUR) value must be greater than or equal to 0
:)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: iterate over all datasets that have a --DOSE variable :)
for $dataset in $definedoc//odm:ItemGroupDef[upper-case(@def:Class)='INTERVENTIONS' or upper-case(@def:Class)='EVENTS' or upper-case(@def:Class)='FINDINGS' 
		or upper-case(./def21:Class/@Name)='INTERVENTIONS' or upper-case(./def21:Class/@Name)='EVENTS' or upper-case(./def21:Class/@Name)='FINDINGS'
		or @Name='TE' or @Name='TV' or @Name='SV'] 
    let $name := $dataset/@Name
    let $datasetname := (
		if($defineversion = '2.1') then $dataset/def21:leaf/@xlink:href
		else $dataset/def:leaf/@xlink:href
	)
    let $datasetdoc := (
		if($datasetname) then doc(concat($base,$datasetname))
		else ()
	)
    (: find the OID of the --DOSE variable :)
    let $duroid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'DUR') or @Name='AGE']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a 
    )
    let $durname := $definedoc//odm:ItemDef[@OID=$duroid]/@Name
    (: now iterate over all in the dataset itself :)
    for $record in $datasetdoc//odm:ItemGroupData
        let $recnum := $record/@data:ItemGroupDataSeq
        (: and get the value :)
        let $durvalue := $record/odm:ItemData[@ItemOID=$duroid]/@Value
        (: and check whether >= 0 :)
        where $durvalue and starts-with($durvalue,'-') (: a --DUR value is present and it is a -P so negative value :)
        return <error rule="SD0015" dataset="{data($name)}" variable="data{data($durname)}" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">Negative value for {data($durname)} in dataset {data($datasetname)}: {data($durvalue)} was found</error>	
]]></rulexquery>
</sdsrule>

<!-- Remark: this rule has been made more precise relative to old rule FDAC082:
a) applies to: INTERVENTIONS, EVENTS, FINDINGS, TE, SE, SV  
b) Values for the following variables cannot be negative: Age, Dose, and Duration of Event, Exposure or Observation
-->
<sdsrule id="SD0015-SD" last-update="2020-08-08" originator="FDA" standard="SDTM" requiresDomainOrDataset="Yes">
<ruledescription>Value for --DUR may not be negative</ruledescription>
<ruledetaileddescription>Non-missing Duration of Event, Exposure or Observation (--DUR) value must be greater than or equal to 0</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>INTERVENTIONS</domain>
<domain>EVENTS</domain>
<domain>FINDINGS</domain>
<domain>TE</domain>
<domain>SE</domain>
<domain>SV</domain>
<rulexquery><![CDATA[	
(: Rule SD0015 - Negative value for --DUR (all domains)
Non-missing Duration of Event, Exposure or Observation (--DUR) value must be greater than or equal to 0
:)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
declare variable $datasetname external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: iterate over all datasets that have a --DOSE variable :)
for $dataset in $definedoc//odm:ItemGroupDef[@Name=$datasetname][upper-case(@def:Class)='INTERVENTIONS' or upper-case(@def:Class)='EVENTS' or upper-case(@def:Class)='FINDINGS' 
		or upper-case(./def21:Class/@Name)='INTERVENTIONS' or upper-case(./def21:Class/@Name)='EVENTS' or upper-case(./def21:Class/@Name)='FINDINGS'
		or @Name='TE' or @Name='TV' or @Name='SV'] 
    let $name := $dataset/@Name
    let $dsname := (
		if($defineversion = '2.1') then $dataset/def21:leaf/@xlink:href
		else $dataset/def:leaf/@xlink:href
	)
    let $datasetdoc := (
		if($dsname) then doc(concat($base,$dsname))
		else ()
	)
    (: find the OID of the --DOSE variable :)
    let $duroid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'DUR') or @Name='AGE']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a 
    )
    let $durname := $definedoc//odm:ItemDef[@OID=$duroid]/@Name
    (: now iterate over all in the dataset itself :)
    for $record in $datasetdoc//odm:ItemGroupData
        let $recnum := $record/@data:ItemGroupDataSeq
        (: and get the value :)
        let $durvalue := $record/odm:ItemData[@ItemOID=$duroid]/@Value
        (: and check whether >= 0 :)
        where $durvalue and starts-with($durvalue,'-') (: a --DUR value is present and it is a -P so negative value :)
        return <error rule="SD0015" dataset="{data($name)}" variable="data{data($durname)}" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">Negative value for {data($durname)} in dataset {data($datasetname)}: {data($durvalue)} was found</error>
]]></rulexquery>
</sdsrule>

<sdsrule id="SD0084" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>The value for AGE may not be negative</ruledescription>
<ruledetaileddescription>The value of Age (AGE) cannot be less than 0</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>DM</domain>
<rulexquery><![CDATA[	
(: Rule SD0084 - Negative value for AGE (DM)
The value of Age (AGE) cannot be less than 0
:)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: Get the DM dataset :)
let $dmdataset := $definedoc//odm:ItemGroupDef[@Name='DM']
    let $dmdatasetname := ( 
		if($defineversion = '2.1') then $dmdataset/def21:leaf/@xlink:href
		else $dmdataset/def:leaf/@xlink:href
	)
    let $dmdatasetlocation := concat($base,$dmdatasetname)
    (: find the OID of the AGE variable :)
    let $ageoid := (
        for $a in $definedoc//odm:ItemDef[@Name='AGE']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name='DM']/odm:ItemRef/@ItemOID
        return $a 
    )
    (: now iterate over all in the dataset itself :)
    for $record in doc($dmdatasetlocation)//odm:ItemGroupData
        let $recnum := $record/@data:ItemGroupDataSeq
        (: and get the value :)
        let $agevalue := $record/odm:ItemData[@ItemOID=$ageoid]/@Value
        (: and check whether >= 0 :)
        where $agevalue and number($agevalue) < 0
        return <error rule="SD0084" dataset="DM" variable="AGE" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">Negative value for AGE in DM dataset {data($dmdatasetname)}: {data($agevalue)} was found</error>
]]></rulexquery>
</sdsrule>

<!-- Rule SD1029: Non-ASCII or non-printable characters in variable
BUT only check on whether all the value characters are US-ASCII -->
<sdsrule id="SD1029-SD" last-update="2020-08-08" originator="FDA" standard="SDTM" requiresDomainOrDataset="Yes">
<ruledescription>Only ASCII characters in variables are allowed</ruledescription>
<ruledetaileddescription>Variables value must not include non-ASCII or non-printable characters (outside of 32-126 ASCII code range), limited to variables which values may be converted into new variable name or label (--TEST, --TESTCD, --PARM, --PARMCD, QLABEL, QNAM)</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>ALL</domain>
<rulexquery><![CDATA[
(: Rule SD1029: Non-ASCII or non-printable characters in variable.
 We only check on non-ascii, as every character is printable - depends on the printer ... :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink"; 
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
declare variable $datasetname external;
(: let $base := '/db/fda_submissions/cdisc01/'  
let $define := 'define2-0-0-example-sdtm.xml' 
let $datasetname := 'LB' :)
(: the define.xml document :)
let $definedoc := doc(concat($base,$define))
(: get the dataset definition :)
for $datasetdef in $definedoc//odm:ItemGroupDef[@Name=$datasetname]
    let $name := $datasetdef/@Name
    (: get the dataset location :)
    let $datasetlocation := (
		if($defineversion = '2.1') then $datasetdef/def21:leaf/@xlink:href
		else $datasetdef/def:leaf/@xlink:href
	)
    (: and the document itself :)
    let $datasetdoc := doc(concat($base,$datasetlocation))
    (: iterate over all the records in the dataset :)
    for $record in $datasetdoc//odm:ItemGroupData
        let $recnum := $record/@data:ItemGroupDataSeq
        (: iterate over all the data points :)
        for $datapoint in $record/odm:ItemData
            (: get the OID and Name of the datapoint :)
            let $varoid := $datapoint/@ItemOID
            let $varname := $definedoc//odm:ItemDef[@OID=$varoid]/@Name
            (: get the value :)
            let $varvalue := $datapoint/@Value
            (: if the string-length > 0, the all the characters must be ASCII - 
            see e.g. https://stackoverflow.com/questions/51521676/use-xpath-to-check-if-a-string-has-only-ascii-characters :)
            where string-length($varvalue) > 0 and not(matches($varvalue,'^[ -~\n\t]+$'))  
            return <error rule="SD1029" dataset="{$datasetname}" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}" variable="{$varname}">Variable value '{data($varvalue)}' contains non-ASCII characters</error>	
]]>
</rulexquery>
</sdsrule>

<!-- Rule SE2310 is applicable to as well SDTM and SEND for following domains: SC, BW, BG, FW, LB, DD, CL, MI, MA, OM, PM, PC, PP, TF, EG, VS -->
<sdsrule id="SE2310" last-update="2020-08-08" originator="FDA" standard="SDTM" requiresDomainOrDataset="Yes">
<ruledescription>Value for --STRESC may not be populated, when --STAT is 'NOT DONE'</ruledescription>
<ruledetaileddescription>Status (--STAT) should be NULL, when Standardized Result in Character Format (--STRESC ) is provided</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>SC</domain>
<domain>BW</domain>
<domain>BG</domain>
<domain>FW</domain>
<domain>LB</domain>
<domain>DD</domain>
<domain>CL</domain>
<domain>MI</domain>
<domain>MA</domain>
<domain>OM</domain>
<domain>PM</domain>
<domain>PC</domain>
<domain>PP</domain>
<domain>TF</domain>
<domain>EG</domain>
<domain>VS</domain>
<rulexquery><![CDATA[	
(: Rule SE2310 - Value for --STRESC is populated, when --STAT is 'NOT DONE'
Status (--STAT) should be NULL, Standardized Result in Character Format (--STRESC ) is provided
:) 
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
declare variable $datasetname external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: let $datasetname := 'LB' :)
(: iterate over all provided datasets within SC, BW, BG, FW, LB, DD, CL, MI, MA, OM, PM, PC, PP, TF, EG, VS :)
for $dataset in $definedoc//odm:ItemGroupDef[@Name=$datasetname][@Domain='SC' or @Domain='BW' or @Domain='BG' or @Domain='FW' or @Domain='LB' or @Domain='DD' or @Domain='CL' or @Domain='MI' or @Domain='MA' or @Domain='OM' or @Domain='PM' or @Domain='PC' or @Domain='PP' or @Domain='TF' or @Domain='EG' or @Domain='VS']
    let $name := $dataset/@Name
    let $dsname := ( 
		if($defineversion = '2.1') then $dataset/def21:leaf/@xlink:href
		else $dataset/def:leaf/@xlink:href
	)
    let $datasetlocation := concat($base,$dsname)
    (: get the OIDs of the --STAT and --STRESC variable :)
    let $statoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'STAT')]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
    )
    let $statname := $definedoc//odm:ItemDef[@OID=$statoid]/@Name
    let $strescoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'STRESC')]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
    )
    let $strescname := $definedoc//odm:ItemDef[@OID=$strescoid]/@Name
    (: iterate over all records for which STRESC is populated :)
    for $record in doc($datasetlocation)//odm:ItemGroupData[odm:ItemData[@ItemOID=$strescoid]/@Value]
        let $recnum := $record/@data:ItemGroupDataSeq
        (: get the value of STAT  :)
        let $statvalue := $record/odm:ItemData[@ItemOID=$statoid]/@Value
        (: STAT must be null :)
        where $statvalue
        return <warning rule="SE2310" dataset="{data($name)}" variable="{data($statname)}" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">Status {data($statname)} should be NULL, when Result or Finding in Original Units ({data($strescname)}) is provided
        </warning>
]]></rulexquery>
</sdsrule>

<sdsrule id="SD1123" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>Value for --ORRES may not be populated, when --STAT is 'NOT DONE' </ruledescription>
<ruledetaileddescription>Value of Original Result (--ORRES) variable is expected to be missing, when observation was not performed (Status (--STAT) is 'NOT DONE')</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>FINDINGS</domain>
<rulexquery><![CDATA[	
(: Rule SD1123 - Value for --ORRES is populated, when --STAT is 'NOT DONE'
Value of Original Result (--ORRES) variable is expected to be missing, when observation was not performed (Status (--STAT) is 'NOT DONE')
:) 
(: ATTENTION: Message and Description do not match :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: iterate over all FINDINGS datasets :)
for $dataset in $definedoc//odm:ItemGroupDef[upper-case(@def:Class)='FINDINGS' or upper-case(./def21:Class/@Name)='FINDINGS']
    let $name := $dataset/@Name
    let $datasetname := (
		if($defineversion = '2.1') then $dataset/def21:leaf/@xlink:href
		else $dataset/def:leaf/@xlink:href
	)
    let $datasetdoc := (
		if($datasetname) then doc(concat($base,$datasetname))
		else ()
	)
    (: get the OIDs of the --STAT, --DRVFL and --ORRES variable :)
    let $statoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'STAT')]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
    )
    let $statname := $definedoc//odm:ItemDef[@OID=$statoid]/@Name
    let $orresoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'ORRES')]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
    )
    let $orresname := $definedoc//odm:ItemDef[@OID=$orresoid]/@Name
    (: iterate over all records for which STAT has the value 'NOT DONE' :)
    for $record in $datasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$statoid][@Value='NOT DONE']]
        let $recnum := $record/@data:ItemGroupDataSeq
        (: get the value of ORRES  :)
        let $orresvalue := $record/odm:ItemData[@ItemOID=$orresoid]/@Value
        (: ORRES must be null :)
        where $orresvalue
        return <warning rule="SD1123" dataset="{data($name)}" variable="{data($orresname)}" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">{data($orresname)} value is populated (value={data($orresvalue)}), when {data($orresname)} is 'NOT DONE'  </warning>
]]></rulexquery>
</sdsrule>

<sdsrule id="SD1123-SD" last-update="2020-08-08" originator="FDA" standard="SDTM" requiresDomainOrDataset="Yes">
<ruledescription>Value for --ORRES may not be populated, when --STAT is 'NOT DONE' </ruledescription>
<ruledetaileddescription>Value of Original Result (--ORRES) variable is expected to be missing, when observation was not performed (Status (--STAT) is 'NOT DONE')</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>FINDINGS</domain>
<rulexquery><![CDATA[	
(: Rule SD1123 - Value for --ORRES is populated, when --STAT is 'NOT DONE'
Value of Original Result (--ORRES) variable is expected to be missing, when observation was not performed (Status (--STAT) is 'NOT DONE')
:) 
(: ATTENTION: Message and Description do not match :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
declare variable $datasetname external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: iterate over all FINDINGS datasets :)
for $dataset in $definedoc//odm:ItemGroupDef[@Name=$datasetname][upper-case(@def:Class)='FINDINGS' or upper-case(./def21:Class/@Name)='FINDINGS']
    let $name := $dataset/@Name
    let $dsname := (
		if($defineversion = '2.1') then $dataset/def21:leaf/@xlink:href
		else $dataset/def:leaf/@xlink:href
	)
    let $datasetdoc := (
		if($dsname) then doc(concat($base,$dsname))
		else ()
	)
    (: get the OIDs of the --STAT, --DRVFL and --ORRES variable :)
    let $statoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'STAT')]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
    )
    let $statname := $definedoc//odm:ItemDef[@OID=$statoid]/@Name
    let $orresoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'ORRES')]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
    )
    let $orresname := $definedoc//odm:ItemDef[@OID=$orresoid]/@Name
    (: iterate over all records for which STAT has the value 'NOT DONE' :)
    for $record in $datasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$statoid][@Value='NOT DONE']]
        let $recnum := $record/@data:ItemGroupDataSeq
        (: get the value of ORRES  :)
        let $orresvalue := $record/odm:ItemData[@ItemOID=$orresoid]/@Value
        (: ORRES must be null :)
        where $orresvalue
        return <warning rule="SD1123" dataset="{data($name)}" variable="{data($orresname)}" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">{data($orresname)} value is populated (value={data($orresvalue)}), when {data($orresname)} is 'NOT DONE'  </warning>
]]></rulexquery>
</sdsrule>

<sdsrule id="SD1124" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>Value for --REASND must be present, when --STAT is 'NOT DONE'</ruledescription>
<ruledetaileddescription>Value of Reason Not Done (--REASND) variable should be populated, when Status (--STAT) variable value is 'NOT DONE'</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>INTERVENTIONS</domain>
<domain>EVENTS</domain>
<domain>FINDINGS</domain>
<rulexquery><![CDATA[	
(: Rule SD1124 - Missing value for --REASND, when --STAT is 'NOT DONE':
Value of Reason Not Done (--REASND) variable should be populated, when Status (--STAT) variable value is 'NOT DONE'.
:)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/'  :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: Find all "Findings", "Interventions" and "Events" datasets  :)
for $dataset in $definedoc//odm:ItemGroupDef[upper-case(@def:Class)='FINDINGS' or upper-case(@def:Class)='INTERVENTIONS' or upper-case(@def:Class)='EVENTS' 
		or upper-case(./def21:Class/@Name)='FINDINGS' or upper-case(./def21:Class/@Name)='INTERVENTIONS' or upper-case(./def21:Class/@Name)='EVENTS']
    let $name := $dataset/@Name
    let $datasetname := (
		if($defineversion = '2.1') then $dataset/def21:leaf/@xlink:href
		else $dataset/def:leaf/@xlink:href
	)
    let $datasetdoc := (
		if($datasetname) then doc(concat($base,$datasetname))
		else ()
	)
    (: Get the OIDs of the STAT and REASND variables :)
    let $statoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'STAT')]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
    )
    let $statname := $definedoc//odm:ItemDef[@OID=$statoid]/@Name
    let $reasndoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'REASND')]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
    )
    let $reasndname := $definedoc//odm:ItemDef[@OID=$reasndoid]/@Name
    (: iterate over each record in the dataset for which there is a STAT data point that has 'NOT DONE' as value :)
    for $record in $datasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$statoid]/@Value='NOT DONE']
        (: get the record number and the value for REASND :)
        let $recnum := $record/@data:ItemGroupDataSeq
        let $reasndvalue := $record/odm:ItemData[@ItemOID=$reasndoid]/@Value
        (: and check whether there is a corresponding STAT :)
        let $statvalue := $record/odm:ItemData[@ItemOID=$statoid]/@Value  
        (: check that REASND is populated :)
        where not($reasndvalue)
        return <warning rule="SD1124" dataset="{data($name)}" variable="{data($reasndname)}" rulelastupdate="2020-08-08" recordnumber="{$recnum}">Missing value for {data($reasndname)} when {data($statname)}={data($statvalue)} in dataset {data($datasetname)}</warning>	
]]></rulexquery>
</sdsrule>

<sdsrule id="SD1124-SD" last-update="2020-08-08" originator="FDA" standard="SDTM" requiresDomainOrDataset="Yes">
<ruledescription>Value for --REASND must be present, when --STAT is 'NOT DONE'</ruledescription>
<ruledetaileddescription>Value of Reason Not Done (--REASND) variable should be populated, when Status (--STAT) variable value is 'NOT DONE'</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>INTERVENTIONS</domain>
<domain>EVENTS</domain>
<domain>FINDINGS</domain>
<rulexquery><![CDATA[
(: Rule SD1124 - Missing value for --REASND, when --STAT is 'NOT DONE':
Value of Reason Not Done (--REASND) variable should be populated, when Status (--STAT) variable value is 'NOT DONE'.
:)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
declare variable $datasetname external;
(: let $base := '/db/fda_submissions/cdisc01/'  :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: Find all provided datasets in "Findings", "Interventions" and "Events" :)
for $dataset in $definedoc//odm:ItemGroupDef[@Name=$datasetname][upper-case(@def:Class)='FINDINGS' or upper-case(@def:Class)='INTERVENTIONS' or upper-case(@def:Class)='EVENTS' 
		or upper-case(./def21:Class/@Name)='FINDINGS' or upper-case(./def21:Class/@Name)='INTERVENTIONS' or upper-case(./def21:Class/@Name)='EVENTS']
    let $name := $dataset/@Name
    let $dsname := (
		if($defineversion = '2.1') then $dataset/def21:leaf/@xlink:href
		else $dataset/def:leaf/@xlink:href
	)
    let $datasetdoc := (
		if($dsname) then doc(concat($base,$dsname))
		else ()
	)
    (: Get the OIDs of the STAT and REASND variables :)
    let $statoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'STAT')]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
    )
    let $statname := $definedoc//odm:ItemDef[@OID=$statoid]/@Name
    let $reasndoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'REASND')]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
    )
    let $reasndname := $definedoc//odm:ItemDef[@OID=$reasndoid]/@Name
    (: iterate over each record in the dataset for which there is a STAT data point that has 'NOT DONE' as value :)
    for $record in $datasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$statoid]/@Value='NOT DONE']
        (: get the record number and the value for REASND :)
        let $recnum := $record/@data:ItemGroupDataSeq
        let $reasndvalue := $record/odm:ItemData[@ItemOID=$reasndoid]/@Value
        (: and check whether there is a corresponding STAT :)
        let $statvalue := $record/odm:ItemData[@ItemOID=$statoid]/@Value  
        (: check that REASND is populated :)
        where not($reasndvalue)
        return <warning rule="SD1124" dataset="{data($name)}" variable="{data($reasndname)}" rulelastupdate="2020-08-08" recordnumber="{$recnum}">Missing value for {data($reasndname)} when {data($statname)}={data($statvalue)} in dataset {data($datasetname)}</warning>
]]></rulexquery>
</sdsrule>

<!-- Rule SD0031 applies to: INTERVENTIONS, EVENTS, SE, SV -->
<sdsrule id="SD0031" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>One of --STDTC, --STRF or --STRTPT must be populated, when --ENDTC, --ENRF or --ENRTPT is provided</ruledescription>
<ruledetaileddescription>Start Date/Time of Observation (--STDTC), Start Relative to Reference Period (--STRF) or Start Relative to Reference Time Point (--STRTPT) should not be NULL, when End Date/Time of Observation (--ENDTC), End Relative to Reference Period (--ENRF) or End Relative to Reference Time Period (--ENRTPT) is not NULL</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>INTERVENTIONS</domain>
<domain>EVENTS</domain>
<domain>SE</domain>
<domain>SV</domain>
<rulexquery><![CDATA[
(: TODO: requires further testing :)
(: Rule SD0031 - Missing values for --STDTC, --STRF and --STRTPT, when --ENDTC, --ENRF or --ENRTPT is provided
Start Date/Time of Observation (--STDTC), Start Relative to Reference Period (--STRF) or Start Relative to Reference Time Point (--STRTPT) should not be NULL, 
when End Date/Time of Observation (--ENDTC), End Relative to Reference Period (--ENRF) or End Relative to Reference Time Period (--ENRTPT) is not NULL
:)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :) 
let $definedoc := doc(concat($base,$define)) 
(: iterate over all the datasets :)
for $dataset in $definedoc//odm:ItemGroupDef[upper-case(@def:Class)='INTERVENTIONS' or upper-case(@def:Class)='EVENTS' 
		or upper-case(./def21:Class/@Name)='INTERVENTIONS' or upper-case(./def21:Class/@Name)='EVENTS'
		or @Domain='SE' or @Domain='SV']
    let $name := $dataset/@Name
    let $datasetname := (
		if($defineversion = '2.1') then $dataset/def21:leaf/@xlink:href
		else $dataset/def:leaf/@xlink:href
	)
    let $datasetlocation := concat($base,$datasetname)
    (: Get the OID and Name of the STDTC, STRF, STRTPT variables :)
    let $stdtcoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'STDTC')]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
    )
    let $stdtcname := $definedoc//odm:ItemDef[@OID=$stdtcoid]/@Name
    let $strfoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'STRF')]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
    )
    let $strfname := $definedoc//odm:ItemDef[@OID=$strfoid]/@Name
    let $strtptoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'STRTPT')]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
    )
    let $strtptname := $definedoc//odm:ItemDef[@OID=$strtptoid]/@Name
    (: and of the ENDTC, ENRF and ENRTPT variables :) 
    let $endtcoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'ENDTC')]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
    )
    let $endtcname := $definedoc//odm:ItemDef[@OID=$endtcoid]/@Name
    let $enrfoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'ENDTC')]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
    )
    let $enrfname := $definedoc//odm:ItemDef[@OID=$enrfoid]/@Name
    let $enrtptoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'ENRTPT')]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
    )
    let $enrtptname := $definedoc//odm:ItemDef[@OID=$enrtptoid]/@Name
    (: iterate over all records that do have a --ENDTC, --ENRF or --ENRTPT data point :)
    for $record in doc($datasetlocation)//odm:ItemGroupData[odm:ItemData[@ItemOID=$endtcoid] or odm:ItemData[@ItemOID=$enrfoid] or odm:ItemData[@ItemOID=$enrtptoid]] 
    let $recnum := $record/@data:ItemGroupDataSeq
    (: check whether a value for STDTC, STRF, or STRTPT is present :)
    where not($record/odm:ItemData[@ItemOID=$stdtcoid]) and not($record/odm:ItemData[@ItemOID=$strfoid]) and not($record/odm:ItemData[@ItemOID=$strtptoid]) 
    return <warning rule="SD0031" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">Missing values for {data(concat($name,'STDTC'))}, {data(concat($name,'STRF'))} and {data(concat($name,'STRTPT'))}, when one of {data(concat($name,'ENDTC'))}, {data(concat($name,'ENRF'))} or {data(concat($name,'ENRTPT'))} is provided in dataset {data($name)} </warning>	
]]></rulexquery>
</sdsrule>

<!-- Rule SD0031 applies to: INTERVENTIONS, EVENTS, SE, SV -->
<sdsrule id="SD0031-SD" last-update="2020-08-08" originator="FDA" standard="SDTM" requiresDomainOrDataset="Yes">
<ruledescription>One of --STDTC, --STRF or --STRTPT must be populated, when --ENDTC, --ENRF or --ENRTPT is provided</ruledescription>
<ruledetaileddescription>Start Date/Time of Observation (--STDTC), Start Relative to Reference Period (--STRF) or Start Relative to Reference Time Point (--STRTPT) should not be NULL, when End Date/Time of Observation (--ENDTC), End Relative to Reference Period (--ENRF) or End Relative to Reference Time Period (--ENRTPT) is not NULL</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>INTERVENTIONS</domain>
<domain>EVENTS</domain>
<domain>SE</domain>
<domain>SV</domain>
<rulexquery><![CDATA[
(: TODO: requires further testing :)
(: Rule SD0031 - Missing values for --STDTC, --STRF and --STRTPT, when --ENDTC, --ENRF or --ENRTPT is provided
Start Date/Time of Observation (--STDTC), Start Relative to Reference Period (--STRF) or Start Relative to Reference Time Point (--STRTPT) should not be NULL, 
when End Date/Time of Observation (--ENDTC), End Relative to Reference Period (--ENRF) or End Relative to Reference Time Period (--ENRTPT) is not NULL
:)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
declare variable $datasetname external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :) 
let $definedoc := doc(concat($base,$define)) 
(: iterate over all the datasets :)
for $dataset in $definedoc//odm:ItemGroupDef[@Name=$datasetname][upper-case(@def:Class)='INTERVENTIONS' or upper-case(@def:Class)='EVENTS' 
		or upper-case(./def21:Class/@Name)='INTERVENTIONS' or upper-case(./def21:Class/@Name)='EVENTS'
		or @Domain='SE' or @Domain='SV']
    let $name := $dataset/@Name
    let $dsname := (
		if($defineversion = '2.1') then $dataset/def21:leaf/@xlink:href
		else $dataset/def:leaf/@xlink:href
	)
    let $datasetlocation := concat($base,$dsname)
    (: Get the OID and Name of the STDTC, STRF, STRTPT variables :)
    let $stdtcoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'STDTC')]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
    )
    let $stdtcname := $definedoc//odm:ItemDef[@OID=$stdtcoid]/@Name
    let $strfoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'STRF')]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
    )
    let $strfname := $definedoc//odm:ItemDef[@OID=$strfoid]/@Name
    let $strtptoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'STRTPT')]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
    )
    let $strtptname := $definedoc//odm:ItemDef[@OID=$strtptoid]/@Name
    (: and of the ENDTC, ENRF and ENRTPT variables :) 
    let $endtcoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'ENDTC')]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
    )
    let $endtcname := $definedoc//odm:ItemDef[@OID=$endtcoid]/@Name
    let $enrfoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'ENDTC')]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
    )
    let $enrfname := $definedoc//odm:ItemDef[@OID=$enrfoid]/@Name
    let $enrtptoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'ENRTPT')]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
    )
    let $enrtptname := $definedoc//odm:ItemDef[@OID=$enrtptoid]/@Name
    (: iterate over all records that do have a --ENDTC, --ENRF or --ENRTPT data point :)
    for $record in doc($datasetlocation)//odm:ItemGroupData[odm:ItemData[@ItemOID=$endtcoid] or odm:ItemData[@ItemOID=$enrfoid] or odm:ItemData[@ItemOID=$enrtptoid]] 
    let $recnum := $record/@data:ItemGroupDataSeq
    (: check whether a value for STDTC, STRF, or STRTPT is present :)
    where not($record/odm:ItemData[@ItemOID=$stdtcoid]) and not($record/odm:ItemData[@ItemOID=$strfoid]) and not($record/odm:ItemData[@ItemOID=$strtptoid]) 
    return <warning rule="SD0031" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">Missing values for {data(concat($name,'STDTC'))}, {data(concat($name,'STRF'))} and {data(concat($name,'STRTPT'))}, when one of {data(concat($name,'ENDTC'))}, {data(concat($name,'ENRF'))} or {data(concat($name,'ENRTPT'))} is provided in dataset {data($name)} </warning>	
]]></rulexquery>
</sdsrule>

<sdsrule id="SD0049" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>--STTPT must be populated, when --STRTPT is populated</ruledescription>
<ruledetaileddescription>Start Reference Time Point (--STTPT) must not be NULL, when Start Relative to Reference Time Point (--STRTPT) is provided</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>INTERVENTIONS</domain>
<domain>EVENTS</domain>
<domain>FINDINGS</domain>
<domain>SV</domain>
<domain>SE</domain>
<rulexquery><![CDATA[	
(: Rule SD0049 - Missing value for --STTPT, when --STRTPT is populated:
Start Reference Time Point (--STTPT) must not be NULL, when Start Relative to Reference Time Point (--STRTPT) is provided
:)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: iterate over all the datasets within INTERVENTIONS, EVENTS, FINDINGS, SE, SV:)
for $dataset in $definedoc//odm:ItemGroupDef[upper-case(@def:Class)='INTERVENTIONS' or upper-case(@def:Class)='EVENTS' or upper-case(@def:Class)='FINDINGS' 
		or upper-case(./def21:Class/@Name)='INTERVENTIONS' or upper-case(./def21:Class/@Name)='EVENTS' or upper-case(./def21:Class/@Name)='FINDINGS'
		or @Domain='SE' or @Domain='SV']
    let $name := $dataset/@Name
    let $datasetname := (
		if($defineversion = '2.1') then $dataset/def21:leaf/@xlink:href
		else $dataset/def:leaf/@xlink:href
	)
    let $datasetdoc := (
		if($datasetname) then doc(concat($base,$datasetname))
		else ()
	)
    (: get the OIDs of the STTPT and STRTPT variables (if any) :)
    let $sttptoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'STTPT')]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
    )
    let $sttptname := $definedoc//odm:ItemDef[@OID=$sttptoid]/@Name
    let $strtptoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'STRTPT')]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
    )
    let $strtptname := $definedoc//odm:ItemDef[@OID=$strtptoid]/@Name
    (: iterate over all the records in the dataset for which there is a STRTPT data point :)
    for $record in $datasetdoc//odm:ItemGroupData[odm:ItemData/@ItemOID=$strtptoid]
    let $recnum := $record/@data:ItemGroupDataSeq
        (: and check whether there is STTPT data point :)
        let $sttptvalue := $record/odm:ItemData[@ItemOID=$sttptoid]/@Value
        let $strtptvalue := $record/odm:ItemData[@ItemOID=$strtptoid]/@Value
        where not($sttptvalue)
        return <warning rule="SD0049" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">Missing value for {data($sttptname)}, when {data($strtptname)} is populated, value = {data($strtptvalue)}</warning>
]]></rulexquery>
</sdsrule>

<sdsrule id="SD0049-SD" last-update="2020-08-08" originator="FDA" standard="SDTM" requiresDomainOrDataset="Yes">
<ruledescription>--STTPT must be populated, when --STRTPT is populated</ruledescription>
<ruledetaileddescription>Start Reference Time Point (--STTPT) must not be NULL, when Start Relative to Reference Time Point (--STRTPT) is provided</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>INTERVENTIONS</domain>
<domain>EVENTS</domain>
<domain>FINDINGS</domain>
<domain>SV</domain>
<domain>SE</domain>
<rulexquery><![CDATA[	
(: Rule SD0049 - Missing value for --STTPT, when --STRTPT is populated:
Start Reference Time Point (--STTPT) must not be NULL, when Start Relative to Reference Time Point (--STRTPT) is provided
:)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
declare variable $datasetname external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: iterate over all the datasets within INTERVENTIONS, EVENTS, FINDINGS, SE, SV:)
for $dataset in $definedoc//odm:ItemGroupDef[@Name=$datasetname][upper-case(@def:Class)='INTERVENTIONS' or upper-case(@def:Class)='EVENTS' or upper-case(@def:Class)='FINDINGS' 
		or upper-case(./def21:Class/@Name)='INTERVENTIONS' or upper-case(./def21:Class/@Name)='EVENTS' or upper-case(./def21:Class/@Name)='FINDINGS'
		or @Domain='SE' or @Domain='SV']
    let $name := $dataset/@Name
    let $ds := (
		if($defineversion = '2.1') then $dataset/def21:leaf/@xlink:href
		else $dataset/def:leaf/@xlink:href
	)
    let $datasetdoc := (
		if($ds) then doc(concat($base,$ds))
		else ()
	)
    (: get the OIDs of the STTPT and STRTPT variables (if any) :)
    let $sttptoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'STTPT')]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
    )
    let $sttptname := $definedoc//odm:ItemDef[@OID=$sttptoid]/@Name
    let $strtptoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'STRTPT')]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
    )
    let $strtptname := $definedoc//odm:ItemDef[@OID=$strtptoid]/@Name
    (: iterate over all the records in the dataset for which there is a STRTPT data point :)
    for $record in $datasetdoc//odm:ItemGroupData[odm:ItemData/@ItemOID=$strtptoid]
    let $recnum := $record/@data:ItemGroupDataSeq
        (: and check whether there is STTPT data point :)
        let $sttptvalue := $record/odm:ItemData[@ItemOID=$sttptoid]/@Value
        let $strtptvalue := $record/odm:ItemData[@ItemOID=$strtptoid]/@Value
        where not($sttptvalue)
        return <warning rule="SD0049" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">Missing value for {data($sttptname)}, when {data($strtptname)} is populated, value = {data($strtptvalue)}</warning>
]]></rulexquery>
</sdsrule>

<!-- Rule SD0050 applies to INTERVENTIONS, EVENTS, FINDINGS, SE, SV -->
<sdsrule id="SD0050" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>--ENTPT must be populated, when --ENRTPT is populated</ruledescription>
<ruledetaileddescription>End Reference Time Point (--ENTPT) must not be NULL, when End Relative to Reference Time Point (--ENRTPT) is provided</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>ALL</domain>
<rulexquery><![CDATA[	
(: Rule SD0050 - Missing value for --ENTPT, when --ENRTPT is populated:
End Reference Time Point (--ENTPT) must not be NULL, when End Relative to Reference Time Point (--ENRTPT) is provided
:)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: iterate over all the datasets :)
for $dataset in $definedoc//odm:ItemGroupDef[upper-case(@def:Class)='INTERVENTIONS' or upper-case(@def:Class)='EVENTS' or upper-case(@def:Class)='FINDINGS' 
		or upper-case(./def21:Class/@Name)='INTERVENTIONS' or upper-case(./def21:Class/@Name)='EVENTS' or upper-case(./def21:Class/@Name)='FINDINGS'
		or @Domain='SE' or @Domain='SV']
    let $name := $dataset/@Name
    let $datasetname := (
		if($defineversion = '2.1') then $dataset/def21:leaf/@xlink:href
		else $dataset/def:leaf/@xlink:href
	)
    let $datasetdoc := (
		if($datasetname) then doc(concat($base,$datasetname))
		else ()
	)
    (: get the OIDs of the ENTPT and ENRTPT variables (if any) :)
    let $entptoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'ENTPT')]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
    )
    let $entptname := $definedoc//odm:ItemDef[@OID=$entptoid]/@Name
    let $enrtptoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'ENRTPT')]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
    )
    let $enrtptname := $definedoc//odm:ItemDef[@OID=$enrtptoid]/@Name
    (: iterate over all the records in the dataset for which there is a ENRTPT data point :)
    for $record in $datasetdoc//odm:ItemGroupData[odm:ItemData/@ItemOID=$enrtptoid]
    let $recnum := $record/@data:ItemGroupDataSeq
        (: and check whether there is ENTPT data point :)
        let $entptvalue := $record/odm:ItemData[@ItemOID=$entptoid]/@Value
        let $enrtptvalue := $record/odm:ItemData[@ItemOID=$enrtptoid]/@Value
        where not($entptvalue)
        return <warning rule="SD0050" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">Missing value for {data($entptname)}, when {data($enrtptname)} is populated, value = {data($enrtptvalue)}</warning>
]]></rulexquery>
</sdsrule>

<!-- Rule SD0050 applies to INTERVENTIONS, EVENTS, FINDINGS, SE, SV -->
<sdsrule id="SD0050-SD" last-update="2020-08-08" originator="FDA" standard="SDTM" requiresDomainOrDataset="Yes">
<ruledescription>--ENTPT must be populated, when --ENRTPT is populated</ruledescription>
<ruledetaileddescription>End Reference Time Point (--ENTPT) must not be NULL, when End Relative to Reference Time Point (--ENRTPT) is provided</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>ALL</domain>
<rulexquery><![CDATA[	
(: Rule SD0050 - Missing value for --ENTPT, when --ENRTPT is populated:
End Reference Time Point (--ENTPT) must not be NULL, when End Relative to Reference Time Point (--ENRTPT) is provided
:)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
declare variable $datasetname external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: iterate over all the datasets :)
for $dataset in $definedoc//odm:ItemGroupDef[@Name=$datasetname][upper-case(@def:Class)='INTERVENTIONS' or upper-case(@def:Class)='EVENTS' or upper-case(@def:Class)='FINDINGS' 
		or upper-case(./def21:Class/@Name)='INTERVENTIONS' or upper-case(./def21:Class/@Name)='EVENTS' or upper-case(./def21:Class/@Name)='FINDINGS'
		or @Domain='SE' or @Domain='SV']
    let $name := $dataset/@Name
    let $dsname := (
		if($defineversion = '2.1') then $dataset/def21:leaf/@xlink:href
		else $dataset/def:leaf/@xlink:href
	)
    let $datasetdoc := (
		if($dsname) then doc(concat($base,$dsname))
		else ()
	)
    (: get the OIDs of the ENTPT and ENRTPT variables (if any) :)
    let $entptoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'ENTPT')]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
    )
    let $entptname := $definedoc//odm:ItemDef[@OID=$entptoid]/@Name
    let $enrtptoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'ENRTPT')]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
    )
    let $enrtptname := $definedoc//odm:ItemDef[@OID=$enrtptoid]/@Name
    (: iterate over all the records in the dataset for which there is a STRTPT data point :)
    for $record in $datasetdoc//odm:ItemGroupData[odm:ItemData/@ItemOID=$enrtptoid]
    let $recnum := $record/@data:ItemGroupDataSeq
        (: and check whether there is STTPT data point :)
        let $entptvalue := $record/odm:ItemData[@ItemOID=$entptoid]/@Value
        let $enrtptvalue := $record/odm:ItemData[@ItemOID=$enrtptoid]/@Value
        where not($entptvalue)
        return <warning rule="SD0050" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">Missing value for {data($entptname)}, when {data($enrtptname)} is populated, value = {data($enrtptvalue)}</warning>
]]></rulexquery>
</sdsrule>

<!-- ATTENTION! Only seems to apply to SDTM-IG 3.1.2, NOT to 3.1.3 and 3.2! -->
<sdsrule id="SD0093" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>AGEU must be provided, when AGE is provided</ruledescription>
<ruledetaileddescription>Age Units (AGEU) should be provided, when Age (AGE) is populated</ruledetaileddescription>
<igversion>3.1.2</igversion>
<domain>DM</domain>
<rulexquery><![CDATA[	
(: Rule SD0093 - Missing value for AGEU, when AGE is provided:
Age Units (AGEU) should be provided, when Age (AGE) is populated (DM)
SDTM 3.1.1 and 3.1.2
:)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: Get the DM dataset :)
for $dataset in $definedoc//odm:ItemGroupDef[@Name='DM']
    let $datasetname := (
		if($defineversion = '2.1') then $dataset/def21:leaf/@xlink:href
		else $dataset/def:leaf/@xlink:href
	)
    let $datasetdoc := (
		if($datasetname) then doc(concat($base,$datasetname))
		else ()
	)
    (: get the OID of the AGE and AGEU variables :)
    let $ageoid := (
        for $a in $definedoc//odm:ItemDef[@Name='AGE']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name='DM']/odm:ItemRef/@ItemOID
        return $a
    )
    let $ageuoid := (
        for $a in $definedoc//odm:ItemDef[@Name='AGEU']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name='DM']/odm:ItemRef/@ItemOID
        return $a
    )
(: iterate over all DM records that do have an AGE data point :)
    for $record in $datasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$ageoid]]
        let $recnum := $record/@data:ItemGroupDataSeq
        (: and check whether AGEU is present :)
        let $ageuvalue := $record/odm:ItemData[@ItemOID=$ageuoid]/@Value
        where not($ageuvalue)
        return <error rule="SD0093" dataset="DM" variable="AGEU" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">Missing value for AGEU, when AGE is provided</error>
]]></rulexquery>
</sdsrule>

<sdsrule id="SD1003" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>AGE must be provided, when AGEU is provided</ruledescription>
<ruledetaileddescription>Age (AGE) should be provided, when Age Units (AGEU) are populated</ruledetaileddescription>
<igversion>3.1.2</igversion>
<domain>DM</domain>
<rulexquery><![CDATA[	
(: Rule SD1003 - Missing value for AGE, when AGEU is provided:
Age (AGE) should be provided, when Age Units (AGEU) are populated
SDTM 3.1.1 and 3.1.2
:)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/'  :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: Get the DM dataset :)
for $dataset in $definedoc//odm:ItemGroupDef[@Name='DM']
	let $datasetname := (
		if($defineversion = '2.1') then $dataset/def21:leaf/@xlink:href
		else $dataset/def:leaf/@xlink:href
	)
    let $datasetdoc := (
		if($datasetname) then doc(concat($base,$datasetname))
		else ()
	)
    (: get the OID of the AGE and AGEU variables :)
    let $ageoid := (
        for $a in $definedoc//odm:ItemDef[@Name='AGE']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name='DM']/odm:ItemRef/@ItemOID
        return $a
    )
    let $ageuoid := (
        for $a in $definedoc//odm:ItemDef[@Name='AGEU']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name='DM']/odm:ItemRef/@ItemOID
        return $a
    )
    (: iterate over all records that do have an AGEU data point :)
    for $record in $datasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$ageuoid]]
        let $recnum := $record/@data:ItemGroupDataSeq
        (: and check whether AGE is present :)
        let $agevalue := $record/odm:ItemData[@ItemOID=$ageoid]/@Value
        where not($agevalue)
        return <error rule="SD1003" dataset="DM" variable="AGE" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">Missing value for AGE, when AGEU is provided</error>	
	]]></rulexquery>
</sdsrule>


<sdsrule id="SD2019" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>AGETXT must be provided in number-number format</ruledescription>
<ruledetaileddescription>Age Range (AGETXT) variable values should be populated with 'number-number' format</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.2</igversion>
<domain>DM</domain>
<rulexquery><![CDATA[	
(: TODO: further testing - we need a good test file :)
(: Rule SD2019 - Invalid value for AGETXT:
Age Range (AGETXT) variable values should be populated with 'number-number' format
:)
(: Remark: regular expression was tested using: https://regex101.com/  :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: Get the DM dataset :)
for $dataset in $definedoc//odm:ItemGroupDef[@Name='DM']
	let $datasetname := (
		if($defineversion = '2.1') then $dataset/def21:leaf/@xlink:href
		else $dataset/def:leaf/@xlink:href
	)
    let $datasetdoc := (
		if($datasetname) then doc(concat($base,$datasetname))
		else ()
	)
    (: get the OID of the AGETXT variable  :)
    let $agetxtoid := (
        for $a in $definedoc//odm:ItemDef[@Name='AGETXT']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name='DM']/odm:ItemRef/@ItemOID
        return $a
    )
    (: iterate over all records where AGETXT is populated :)
    for $record in $datasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$agetxtoid]]
        let $recnum := $record/@data:ItemGroupDataSeq
        (: get the value of AGETXT :)
        let $agetxtvalue := $record/odm:ItemData[@ItemOID=$agetxtoid]/@Value
        where not(matches($agetxtvalue,'^[\d]*.[\d]*-[\d]*.[\d]*$'))
        return <warning rule="SD2019" dataset="DM" variable="AGETXT" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">Invalid value for AGETXT. '{data($agetxtvalue)}' was found</warning>
]]></rulexquery>
</sdsrule>


<!-- Rule SD0038 applies to: INTERVENTIONS, EVENTS, FINDINGS, DM, SE, SV -->
<sdsrule id="SD0038" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>Study Day variables (*DY) value may not equal 0</ruledescription>
<ruledetaileddescription>Study Day variables (*DY) value should not equal 0</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>INTERVENTIONS</domain>
<domain>EVENTS</domain>
<domain>FINDINGS</domain>
<domain>DM</domain>
<domain>SE</domain>
<domain>SV</domain>
<rulexquery><![CDATA[
(: Rule SD0038 - Value of Study Day variable equals 0
Study Day variables (*DY) value should not equal 0
:)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: iterate over all the datasets :)
for $dataset in $definedoc//odm:ItemGroupDef[upper-case(@def:Class)='INTERVENTIONS' or upper-case(@def:Class)='EVENTS' or upper-case(@def:Class)='FINDINGS' 
		or upper-case(./def21:Class/@Name)='INTERVENTIONS' or upper-case(./def21:Class/@Name)='EVENTS' or upper-case(./def21:Class/@Name)='FINDINGS' 
		or @Domain='SE' or @Domain='SV' or @Domain='DM']
    let $name := $dataset/@Name
	let $datasetname := (
		if($defineversion = '2.1') then $dataset/def21:leaf/@xlink:href
		else $dataset/def:leaf/@xlink:href
	)
    let $datasetdoc := (
		if($datasetname) then doc(concat($base,$datasetname))
		else ()
	)
    (: for this dataset, find all *DY variables - attention, there can be more than one :)
    let $dyvaroids := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'DY')]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
    return $a
    )
    (: iterate over all records in the dataset :)
    for $record in $datasetdoc//odm:ItemGroupData
        let $recnum := $record/@data:ItemGroupDataSeq
        (: iterate over all *DY variables :)
        for $dyvaroid in $dyvaroids
            let $dyname := $definedoc//odm:ItemDef[@OID=$dyvaroid]/@Name
            let $dyvalue := $record/odm:ItemData[@ItemOID=$dyvaroid]/@Value
            (: and check whether it is 0 :)
            where number($dyvalue) = 0
            (: and give out a warning :)
            return <warning rule="SD0038" dataset="{data($name)}" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">Value of Study Day {data($dyname)} variable equals 0</warning>
]]></rulexquery>
</sdsrule>

<!-- Rule SD0038 applies to: INTERVENTIONS, EVENTS, FINDINGS, DM, SE, SV -->
<sdsrule id="SD0038-SD" last-update="2020-08-08" originator="FDA" standard="SDTM" requiresDomainOrDataset="Yes">
<ruledescription>Study Day variables (*DY) value may not equal 0</ruledescription>
<ruledetaileddescription>Study Day variables (*DY) value should not equal 0</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>INTERVENTIONS</domain>
<domain>EVENTS</domain>
<domain>FINDINGS</domain>
<domain>DM</domain>
<domain>SE</domain>
<domain>SV</domain>
<rulexquery><![CDATA[
(: Rule SD0038 - Value of Study Day variable equals 0
Study Day variables (*DY) value should not equal 0
:)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
declare variable $datasetname external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: iterate over all the datasets :)
for $dataset in $definedoc//odm:ItemGroupDef[@Name=$datasetname][upper-case(@def:Class)='INTERVENTIONS' or upper-case(@def:Class)='EVENTS' 
		or upper-case(./def21:Class/@Name)='INTERVENTIONS' or upper-case(./def21:Class/@Name)='EVENTS' 
		or upper-case(@def:Class)='FINDINGS' or @Domain='SE' or @Domain='SV' or @Domain='DM']
    let $name := $dataset/@Name
    let $dsname := (
		if($defineversion = '2.1') then $dataset/def21:leaf/@xlink:href
		else $dataset/def:leaf/@xlink:href
	)
    let $datasetdoc := (
		if($dsname) then doc(concat($base,$dsname))
		else ()
	)
    (: for this dataset, find all *DY variables - attention, there can be more than one :)
    let $dyvaroids := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'DY')]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
    return $a
    )
    (: iterate over all records in the dataset :)
    for $record in $datasetdoc//odm:ItemGroupData
        let $recnum := $record/@data:ItemGroupDataSeq
        (: iterate over all *DY variables :)
        for $dyvaroid in $dyvaroids
            let $dyname := $definedoc//odm:ItemDef[@OID=$dyvaroid]/@Name
            let $dyvalue := $record/odm:ItemData[@ItemOID=$dyvaroid]/@Value
            (: and check whether it is 0 :)
            where number($dyvalue) = 0
            (: and give out a warning :)
            return <warning rule="SD0038" dataset="{data($name)}" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">Value of Study Day {data($dyname)} variable equals 0</warning>	
]]></rulexquery>
</sdsrule>

<!-- Rule SD1086 is now limited to INTERVENTIONS, EVENTS, FINDINGS -->
<!-- NEW algorithm, using a temporary structure for USUBJID-RFSTDTC pairs: reduces the time usage by a factor of 15  -->
<sdsrule id="SD1086" last-update="2020-08-08" originator="FDA" standard="SDTM" timeintensive="Yes">
<ruledescription>--DY variable value must be correctly calculated</ruledescription>
<ruledetaileddescription>Collection Study Day (--DY) variable value should be populated as defined by CDISC standards: --DY = (date portion of --DTC) - (date portion of RFSTDTC) +1 if --DTC is on or after RFSTDTC; --DY = (date portion of --DTC) - (date portion of RFSTDTC) if --DTC precedes RFSTDTC</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>INTERVENTIONS</domain>
<domain>EVENTS</domain>
<domain>FINDINGS</domain>
<rulexquery><![CDATA[	
(: Rule SD1086 - Incorrect value for --DY variable:
Collection Study Day (--DY) variable value should be populated as defined by CDISC standards: --DY = (date portion of --DTC) - (date portion of RFSTDTC) +1 if --DTC is on or after RFSTDTC; --DY = (date portion of --DTC) - (date portion of RFSTDTC) if --DTC precedes RFSTDTC.
:)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/'  
let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: Get the location of the DM dataset :)
let $dmdataset := (
	if($defineversion = '2.1') then $definedoc//odm:ItemGroupDef[@Name='DM']/def21:leaf/@xlink:href
	else $definedoc//odm:ItemGroupDef[@Name='DM']/def:leaf/@xlink:href
)
let $dmdatasetdoc := doc(concat($base,$dmdataset))
(: get the OID of the RFSTDTC variable :)
let $rfstdtcoid := (
    for $a in $definedoc//odm:ItemDef[@Name='RFSTDTC']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name='DM']/odm:ItemRef/@ItemOID
        return $a   
)
(: get the OID of the USUBJID variable :)
let $dmusubjidoid := (
    for $a in $definedoc//odm:ItemDef[@Name='USUBJID']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name='DM']/odm:ItemRef/@ItemOID
        return $a  
)
(: make a temporary construct with USUBJID-RFSTDTC pairs: this will speed up the lookups later :)
let $usubjidrfstdtcpairs := (
    for $record in $dmdatasetdoc//odm:ItemGroupData
        let $dmusubjidvalue := $record/odm:ItemData[@ItemOID=$dmusubjidoid]/@Value
        let $rfstdtcvalue := $record/odm:ItemData[@ItemOID=$rfstdtcoid]/@Value
        return <rfstdtcpair usubjid="{data($dmusubjidvalue)}" rfstdtc="{data($rfstdtcvalue)}" />
)
(: iterate over all other provided datasets within INTERVENTIONS, EVENTS, FINDINGS :)
for $dataset in $definedoc//odm:ItemGroupDef[not(@Name='DM')][upper-case(@def:Class)='INTERVENTIONS' or upper-case(@def:Class)='EVENTS' or upper-case(@def:Class)='FINDINGS' 
		or upper-case(./def21:Class/@Name)='INTERVENTIONS' or upper-case(./def21:Class/@Name)='EVENTS' or upper-case(./def21:Class/@Name)='FINDINGS'] 
    let $name := $dataset/@Name
    let $dsname := (
		if($defineversion = '2.1') then $dataset/def21:leaf/@xlink:href
		else $dataset/def:leaf/@xlink:href
	)
    let $datasetdoc := (
		if($dsname) then doc(concat($base,$dsname))
		else ()
	)
    (: Get the OIDs of the --DY and --DTC variables  :)
    let $dyoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'DY') and string-length(@Name)=4]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
    )
    let $dyname := concat($name,'DY')
    let $dtcoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'DTC') and string-length(@Name)=5]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
    ) 
    let $dtcname := $definedoc//odm:ItemDef[@OID=$dtcoid]/@Name 
    (: and the OID of the USUBJID variable (might be different from the one in DM) :)
    let $usubjidoid := (
        for $a in $definedoc//odm:ItemDef[@Name='USUBJID']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
    )
    for $record in $datasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$dtcoid]]
        let $recnum := $record/@data:ItemGroupDataSeq
        (: get the USUBJID value :)
        let $usubjidvalue := $record/odm:ItemData[@ItemOID=$usubjidoid]/@Value
        (: get the --DTC value :)
        let $dtcvalue := $record/odm:ItemData[@ItemOID=$dtcoid]/@Value
        (: and check whether it is a complete date - i.e. string-length at least 10 :)
        let $iscompletedtcvalue := string-length($dtcvalue) >= 10  (: boolean :)
        (: look up the record in the DM dataset and get the RFSTDTC :)
        (: keeping worst case scenario into account that there are two records for the same USUBJID in DM - take the first :)  
        (: let $rfstdtcvalue := doc($dmdatasetlocation)//odm:ItemGroupData[odm:ItemData[@ItemOID=$dmusubjidoid and @Value=$usubjidvalue]][1]/odm:ItemData[@ItemOID=$rfstdtcoid]/@Value :)
        (: replacement 2020-08-08: more efficient :)
        let $rfstdtcvalue := $usubjidrfstdtcpairs[@usubjid=$usubjidvalue]/@rfstdtc
        (: and check whether also RFSTDTC is complete :)
        let $iscompleterfstdtcvalue := string-length($rfstdtcvalue) >= 10 (: boolean :)
        let $dyvalue := $record/odm:ItemData[@ItemOID=$dyoid]/@Value
        (: now calculate the DY value but only when RFSTDTC and DTC have a complete date part :)
        let $dycalculated := (
            if ($iscompleterfstdtcvalue and $iscompletedtcvalue) then
                (: if there is one, we must strip off the time part :)
                let $dtcvalueshort := substring($dtcvalue,1,10)
                let $rfstdtcvalueshort := substring($rfstdtcvalue,1,10)
                let $dycalc := (
                    if($dtcvalueshort castable as xs:date and $rfstdtcvalueshort castable as xs:date) then
                        days-from-duration(xs:date($dtcvalueshort)-xs:date($rfstdtcvalueshort))
                    else()
                ) 
                return $dycalc
            else ()  (: one or both dates are not complete dates :)
        )
        (: for the FDA there is no day 0, DY can only be <0 or >0, first day is day 1 :)
        let $dycalculatedfda := (
            if($dycalculated >= 0) then
                $dycalculated + 1
            else (
                $dycalculated    
            )
        )
        (: before comparing, check that the DY value is really an integer :)
        where $dyvalue castable as xs:integer and $dycalculatedfda and $dyvalue and not($dycalculatedfda = $dyvalue) 
        return <warning rule="SD1086" dataset="{data($name)}" variable="{data($dyname)}" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">Invalid value for {data($dyname)}, calculated value={data($dycalculatedfda)}, provided value={data($dyvalue)}, in dataset {data($name)} </warning>	
]]></rulexquery>
</sdsrule>


<!-- Rule SD1086 is now limited to INTERVENTIONS, EVENTS, FINDINGS -->
<!-- NEW algorithm, using a temporary structure for USUBJID-RFSTDTC pairs: reduces the time usage by a factor of 15  -->
<sdsrule id="SD1086-SD" last-update="2020-08-08" originator="FDA" standard="SDTM" requiresDomainOrDataset="Yes" timeintensive="Yes">
<ruledescription>--DY variable value must be correctly calculated</ruledescription>
<ruledetaileddescription>Collection Study Day (--DY) variable value should be populated as defined by CDISC standards: --DY = (date portion of --DTC) - (date portion of RFSTDTC) +1 if --DTC is on or after RFSTDTC; --DY = (date portion of --DTC) - (date portion of RFSTDTC) if --DTC precedes RFSTDTC</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>INTERVENTIONS</domain>
<domain>EVENTS</domain>
<domain>FINDINGS</domain>
<rulexquery><![CDATA[
(: Rule SD1086 - Incorrect value for --DY variable:
Collection Study Day (--DY) variable value should be populated as defined by CDISC standards: --DY = (date portion of --DTC) - (date portion of RFSTDTC) +1 if --DTC is on or after RFSTDTC; --DY = (date portion of --DTC) - (date portion of RFSTDTC) if --DTC precedes RFSTDTC.
:)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
declare variable $datasetname external; 
(: let $base := '/db/fda_submissions/cdisc01/'  
let $define := 'define2-0-0-example-sdtm.xml' 
let $datasetname := 'LB' :)
let $definedoc := doc(concat($base,$define))
(: Get the location of the DM dataset :)
let $dmdataset := (
	if($defineversion = '2.1') then $definedoc//odm:ItemGroupDef[@Name='DM']/def21:leaf/@xlink:href
	else $definedoc//odm:ItemGroupDef[@Name='DM']/def:leaf/@xlink:href
)
let $dmdatasetdoc := doc(concat($base,$dmdataset))
(: get the OID of the RFSTDTC variable :)
let $rfstdtcoid := (
    for $a in $definedoc//odm:ItemDef[@Name='RFSTDTC']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name='DM']/odm:ItemRef/@ItemOID
        return $a   
)
(: get the OID of the USUBJID variable :)
let $dmusubjidoid := (
    for $a in $definedoc//odm:ItemDef[@Name='USUBJID']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name='DM']/odm:ItemRef/@ItemOID
        return $a  
)
(: make a temporary construct with USUBJID-RFSTDTC pairs: this will speed up the lookups later :)
let $usubjidrfstdtcpairs := (
    for $record in $dmdatasetdoc//odm:ItemGroupData
        let $dmusubjidvalue := $record/odm:ItemData[@ItemOID=$dmusubjidoid]/@Value
        let $rfstdtcvalue := $record/odm:ItemData[@ItemOID=$rfstdtcoid]/@Value
        return <rfstdtcpair usubjid="{data($dmusubjidvalue)}" rfstdtc="{data($rfstdtcvalue)}" />
)
(: iterate over all other provided datasets within INTERVENTIONS, EVENTS, FINDINGS :)
for $dataset in $definedoc//odm:ItemGroupDef[@Name=$datasetname][not(@Name='DM')][upper-case(@def:Class)='INTERVENTIONS' or upper-case(@def:Class)='EVENTS' or upper-case(@def:Class)='FINDINGS' 
		or upper-case(./def21:Class/@Name)='INTERVENTIONS' or upper-case(./def21:Class/@Name)='EVENTS' or upper-case(./def21:Class/@Name)='FINDINGS'] 
    let $name := $dataset/@Name
    let $dsname := (
		if($defineversion = '2.1') then $dataset/def21:leaf/@xlink:href
		else $dataset/def:leaf/@xlink:href
	)
    let $datasetdoc := (
		if($dsname) then doc(concat($base,$dsname))
		else ()
	)
    (: Get the OIDs of the --DY and --DTC variables  :)
    let $dyoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'DY') and string-length(@Name)=4]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
    )
    let $dyname := concat($name,'DY')
    let $dtcoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'DTC') and string-length(@Name)=5]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
    ) 
    let $dtcname := $definedoc//odm:ItemDef[@OID=$dtcoid]/@Name 
    (: and the OID of the USUBJID variable (might be different from the one in DM) :)
    let $usubjidoid := (
        for $a in $definedoc//odm:ItemDef[@Name='USUBJID']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
    )
    for $record in $datasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$dtcoid]]
        let $recnum := $record/@data:ItemGroupDataSeq
        (: get the USUBJID value :)
        let $usubjidvalue := $record/odm:ItemData[@ItemOID=$usubjidoid]/@Value
        (: get the --DTC value :)
        let $dtcvalue := $record/odm:ItemData[@ItemOID=$dtcoid]/@Value
        (: and check whether it is a complete date - i.e. string-length at least 10 :)
        let $iscompletedtcvalue := string-length($dtcvalue) >= 10  (: boolean :)
        (: look up the record in the DM dataset and get the RFSTDTC :)
        (: keeping worst case scenario into account that there are two records for the same USUBJID in DM - take the first :)  
        (: let $rfstdtcvalue := doc($dmdatasetlocation)//odm:ItemGroupData[odm:ItemData[@ItemOID=$dmusubjidoid and @Value=$usubjidvalue]][1]/odm:ItemData[@ItemOID=$rfstdtcoid]/@Value :)
        (: replacement 2020-08-08: more efficient :)
        let $rfstdtcvalue := $usubjidrfstdtcpairs[@usubjid=$usubjidvalue]/@rfstdtc
        (: and check whether also RFSTDTC is complete :)
        let $iscompleterfstdtcvalue := string-length($rfstdtcvalue) >= 10 (: boolean :)
        let $dyvalue := $record/odm:ItemData[@ItemOID=$dyoid]/@Value
        (: now calculate the DY value but only when RFSTDTC and DTC have a complete date part :)
        let $dycalculated := (
            if ($iscompleterfstdtcvalue and $iscompletedtcvalue) then
                (: if there is one, we must strip off the time part :)
                let $dtcvalueshort := substring($dtcvalue,1,10)
                let $rfstdtcvalueshort := substring($rfstdtcvalue,1,10)
                let $dycalc := (
                    if($dtcvalueshort castable as xs:date and $rfstdtcvalueshort castable as xs:date) then
                        days-from-duration(xs:date($dtcvalueshort)-xs:date($rfstdtcvalueshort))
                    else()
                ) 
                return $dycalc
            else ()  (: one or both dates are not complete dates :)
        )
        (: for the FDA there is no day 0, DY can only be <0 or >0, first day is day 1 :)
        let $dycalculatedfda := (
            if($dycalculated >= 0) then
                $dycalculated + 1
            else (
                $dycalculated    
            )
        )
        (: before comparing, check that the DY value is really an integer :)
        where $dyvalue castable as xs:integer and $dycalculatedfda and $dyvalue and not($dycalculatedfda = $dyvalue) 
        return <warning rule="SD1086" dataset="{data($name)}" variable="{data($dyname)}" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">Invalid value for {data($dyname)}, calculated value={data($dycalculatedfda)}, provided value={data($dyvalue)}, in dataset {data($name)} </warning>		
]]></rulexquery>
</sdsrule>


<!--  SD1084 " -DY variable value is not populated" - very similar to SD1085 -->
<sdsrule id="SD1084" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>--DY variable value must be populated</ruledescription>
<ruledetaileddescription>Collection Study Day (--DY) variable value should be populated, when Collection Study Date/Time (--DTC) and Subject Reference Start Date/Time (RFSTDTC) variables values are provided, and both of them include complete date part</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>INTERVENTIONS</domain>
<domain>EVENTS</domain>
<domain>FINDINGS</domain>
<rulexquery><![CDATA[	
(: Rule SD1084: Collection Study Day (--DY) variable value should be populated, when Collection Study Date/Time (--DTC) and Subject Reference Start Date/Time (RFSTDTC) variables values are provided, and both of them include complete date part :)
(: Applies to: INTERVENTIONS, EVENTS, FINDINGS :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
declare namespace functx = "http://www.functx.com";
(: function to find out whether a value is in a sequence (array) :)
declare function functx:is-value-in-sequence
  ( $value as xs:anyAtomicType? ,
    $seq as xs:anyAtomicType* )  as xs:boolean {
   $value = $seq
} ;
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' 
let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: Get the location of the DM dataset :)
let $dmdataset := (
	if($defineversion = '2.1') then $definedoc//odm:ItemGroupDef[@Name='DM']/def21:leaf/@xlink:href
	else $definedoc//odm:ItemGroupDef[@Name='DM']/def:leaf/@xlink:href
)
let $dmdatasetdoc := doc(concat($base,$dmdataset))
(: get the OID of the RFSTDTC variable :)
let $rfstdtcoid := (
    for $a in $definedoc//odm:ItemDef[@Name='RFSTDTC']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name='DM']/odm:ItemRef/@ItemOID
        return $a   
)
(: get the OID of the USUBJID variable :)
let $dmusubjidoid := (
    for $a in $definedoc//odm:ItemDef[@Name='USUBJID']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name='DM']/odm:ItemRef/@ItemOID
        return $a  
)
(: make a list of all USUBJID values for which RFSTDTC has a complete date part :)
let $usubjidcompleterfstdtc := (
    for $record in $dmdatasetdoc//odm:ItemGroupData
        where string-length($record/odm:ItemData[@ItemOID=$rfstdtcoid]/@Value) >= 10
        return $record/odm:ItemData[@ItemOID=$dmusubjidoid]/@Value
)
(: iterate over all other datasets in INTERVENTIONS, EVENTS, FINDINGS :)
for $dataset in $definedoc//odm:ItemGroupDef[upper-case(@def:Class)='INTERVENTIONS' or upper-case(@def:Class)='EVENTS' or upper-case(@def:Class)='FINDINGS' 
		or upper-case(./def21:Class/@Name)='INTERVENTIONS' or upper-case(./def21:Class/@Name)='EVENTS' or upper-case(./def21:Class/@Name)='FINDINGS']
    let $name := $dataset/@Name
    let $datasetname := (
		if($defineversion = '2.1') then $dataset/def21:leaf/@xlink:href
		else $dataset/def:leaf/@xlink:href
	)
    let $datasetdoc := (
		if($datasetname) then doc(concat($base,$datasetname))
		else ()
	)
    (: Get the OIDs of the --DY and --DTC variables  :)
    let $dyoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'DY') and string-length(@Name)=4]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
    )
    let $dyname := concat($name,'DY')
    let $dtcoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'DTC') and string-length(@Name)=5]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
    ) 
    let $dtcname := $definedoc//odm:ItemDef[@OID=$dtcoid]/@Name 
    (: and the OID of the USUBJID variable (might be different from the one in DM) :)
    let $usubjidoid := (
        for $a in $definedoc//odm:ItemDef[@Name='USUBJID']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
    )
    (: iterate over all records but only when --DY and --DTC have been defined :)
    for $record in $datasetdoc[$dtcoid and $dyoid]//odm:ItemGroupData[odm:ItemData[@ItemOID=$dtcoid]]
        let $recnum := $record/@data:ItemGroupDataSeq
        (: get the USUBJID value :)
        let $usubjidvalue := $record/odm:ItemData[@ItemOID=$usubjidoid]/@Value
        (: get the --DTC value :)
        let $dtcvalue := $record/odm:ItemData[@ItemOID=$dtcoid]/@Value
        (: and check whether it is a complete date - i.e. string-length at least 10 :)
        let $iscompletedtcvalue := string-length($dtcvalue) >= 10  (: boolean :)
        (: look up the record in the DM dataset and get the RFSTDTC :)
        (: and check whether also RFSTDTC is complete :)
        let $hascompleterfstdtcvalue := functx:is-value-in-sequence($usubjidvalue,$usubjidcompleterfstdtc)
        let $dyvalue := $record/odm:ItemData[@ItemOID=$dyoid]/@Value
        (: when RFSTDTC and xxDTC are complete, there MUST be a xxDY value :)
        where $iscompletedtcvalue and $hascompleterfstdtcvalue and not($dyvalue)
        return <warning rule="SD1084" dataset="{data($name)}" variable="{data($dyname)}" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">{data($dyname)} is absent although both {data($dtcname)} (value={data($dtcvalue)}) and RFSTDTC contain complete dates</warning> 	
]]></rulexquery>
</sdsrule>

<!--  SD1084 " -DY variable value is not populated" - very similar to SD1085 -->
<sdsrule id="SD1084-SD" last-update="2020-08-08" originator="FDA" standard="SDTM" requiresDomainOrDataset="Yes">
<ruledescription>--DY variable value must be populated</ruledescription>
<ruledetaileddescription>Collection Study Day (--DY) variable value should be populated, when Collection Study Date/Time (--DTC) and Subject Reference Start Date/Time (RFSTDTC) variables values are provided, and both of them include complete date part</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>INTERVENTIONS</domain>
<domain>EVENTS</domain>
<domain>FINDINGS</domain>
<rulexquery><![CDATA[	
(: Rule SD1084: Collection Study Day (--DY) variable value should be populated, when Collection Study Date/Time (--DTC) and Subject Reference Start Date/Time (RFSTDTC) variables values are provided, and both of them include complete date part :)
(: Applies to: INTERVENTIONS, EVENTS, FINDINGS :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
declare namespace functx = "http://www.functx.com";
(: function to find out whether a value is in a sequence (array) :)
declare function functx:is-value-in-sequence
  ( $value as xs:anyAtomicType? ,
    $seq as xs:anyAtomicType* )  as xs:boolean {
   $value = $seq
} ;
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
declare variable $datasetname external;
(: let $base := '/db/fda_submissions/cdisc01/' 
let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: Get the location of the DM dataset :)
let $dmdataset := (
	if($defineversion = '2.1') then $definedoc//odm:ItemGroupDef[@Name='DM']/def21:leaf/@xlink:href
	else $definedoc//odm:ItemGroupDef[@Name='DM']/def:leaf/@xlink:href
)
let $dmdatasetdoc := doc(concat($base,$dmdataset))
(: get the OID of the RFSTDTC variable :)
let $rfstdtcoid := (
    for $a in $definedoc//odm:ItemDef[@Name='RFSTDTC']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name='DM']/odm:ItemRef/@ItemOID
        return $a   
)
(: get the OID of the USUBJID variable :)
let $dmusubjidoid := (
    for $a in $definedoc//odm:ItemDef[@Name='USUBJID']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name='DM']/odm:ItemRef/@ItemOID
        return $a  
)
(: make a list of all USUBJID values for which RFSTDTC has a complete date part :)
let $usubjidcompleterfstdtc := (
    for $record in $dmdatasetdoc//odm:ItemGroupData
        where string-length($record/odm:ItemData[@ItemOID=$rfstdtcoid]/@Value) >= 10
        return $record/odm:ItemData[@ItemOID=$dmusubjidoid]/@Value
)
(: iterate over all provided datasets in INTERVENTIONS, EVENTS, FINDINGS :)
for $dataset in $definedoc//odm:ItemGroupDef[@Name=$datasetname][upper-case(@def:Class)='INTERVENTIONS' or upper-case(@def:Class)='EVENTS' or upper-case(@def:Class)='FINDINGS' 
		or upper-case(./def21:Class/@Name)='INTERVENTIONS' or upper-case(./def21:Class/@Name)='EVENTS' or upper-case(./def21:Class/@Name)='FINDINGS']
    let $name := $dataset/@Name
    let $datasetname := (
		if($defineversion = '2.1') then $dataset/def21:leaf/@xlink:href
		else $dataset/def:leaf/@xlink:href
	)
    let $datasetdoc := (
		if($datasetname) then doc(concat($base,$datasetname))
		else ()
	)
    (: Get the OIDs of the --DY and --DTC variables  :)
    let $dyoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'DY') and string-length(@Name)=4]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
    )
    let $dyname := concat($name,'DY')
    let $dtcoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'DTC') and string-length(@Name)=5]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
    ) 
    let $dtcname := $definedoc//odm:ItemDef[@OID=$dtcoid]/@Name 
    (: and the OID of the USUBJID variable (might be different from the one in DM) :)
    let $usubjidoid := (
        for $a in $definedoc//odm:ItemDef[@Name='USUBJID']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
    )
    (: iterate over all records but only when --DY and --DTC have been defined :)
    for $record in $datasetdoc[$dtcoid and $dyoid]//odm:ItemGroupData[odm:ItemData[@ItemOID=$dtcoid]]
        let $recnum := $record/@data:ItemGroupDataSeq
        (: get the USUBJID value :)
        let $usubjidvalue := $record/odm:ItemData[@ItemOID=$usubjidoid]/@Value
        (: get the --DTC value :)
        let $dtcvalue := $record/odm:ItemData[@ItemOID=$dtcoid]/@Value
        (: and check whether it is a complete date - i.e. string-length at least 10 :)
        let $iscompletedtcvalue := string-length($dtcvalue) >= 10  (: boolean :)
        (: look up the record in the DM dataset and get the RFSTDTC :)
        (: and check whether also RFSTDTC is complete :)
        let $hascompleterfstdtcvalue := functx:is-value-in-sequence($usubjidvalue,$usubjidcompleterfstdtc)
        let $dyvalue := $record/odm:ItemData[@ItemOID=$dyoid]/@Value
        (: when RFSTDTC and xxDTC are complete, there MUST be a xxDY value :)
        where $iscompletedtcvalue and $hascompleterfstdtcvalue and not($dyvalue)
        return <warning rule="SD1084" dataset="{data($name)}" variable="{data($dyname)}" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">{data($dyname)} is absent although both {data($dtcname)} (value={data($dtcvalue)}) and RFSTDTC contain complete dates</warning> 	
]]></rulexquery>
</sdsrule>

<!-- NEW much faster ALGORITHM 2020-08-08  -->
<!-- applies to INTERVENTIONS, EVENTS, FINDINGS, CO, SE, SV -->
<sdsrule id="SD1085" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>--DY variable value may not be imputed</ruledescription>
<ruledetaileddescription>--DY variable may be only populated, when both Collection Study Date/Time (--DTC) and Subject Reference Start Date/Time (RFSTDTC) variables values are provided and both of them include complete date part</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>INTERVENTIONS</domain>
<domain>EVENTS</domain>
<domain>FINDINGS</domain>
<domain>CO</domain>
<domain>SE</domain>
<domain>SV</domain>
<rulexquery><![CDATA[	
(: Rule SD1085 - --DY variable value is imputed:
Collection Study Day (--DY) variable value should be not be imputed. It may be only populated, when both Collection Study Date/Time (--DTC) and Subject Reference Start Date/Time (RFSTDTC) variables values are provided and both of them include complete date part.
:)
(: Applies to: INTERVENTIONS, EVENTS, FINDINGS, CO, SE, SV :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
declare namespace functx = "http://www.functx.com";
(: function to find out whether a value is in a sequence (array) :)
declare function functx:is-value-in-sequence
  ( $value as xs:anyAtomicType? ,
    $seq as xs:anyAtomicType* )  as xs:boolean {
   $value = $seq
} ;
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' 
let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: Get the location of the DM dataset :)
let $dmdataset := (
	if($defineversion = '2.1') then $definedoc//odm:ItemGroupDef[@Name='DM']/def21:leaf/@xlink:href
	else $definedoc//odm:ItemGroupDef[@Name='DM']/def:leaf/@xlink:href
)
let $dmdatasetdoc := doc(concat($base,$dmdataset))
(: get the OID of the RFSTDTC variable :)
let $rfstdtcoid := (
    for $a in $definedoc//odm:ItemDef[@Name='RFSTDTC']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name='DM']/odm:ItemRef/@ItemOID
        return $a   
)
(: get the OID of the USUBJID variable :)
let $dmusubjidoid := (
    for $a in $definedoc//odm:ItemDef[@Name='USUBJID']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name='DM']/odm:ItemRef/@ItemOID
        return $a  
)
(: make a list of all USUBJID values for which RFSTDTC is not a complete date :)
let $usubjidincompleterfstdtc := (
    for $record in $dmdatasetdoc//odm:ItemGroupData
        where string-length($record/odm:ItemData[@ItemOID=$rfstdtcoid]/@Value) < 10
        return $record/odm:ItemData[@ItemOID=$dmusubjidoid]/@Value
)
(: iterate over all other datasets in INTERVENTIONS, EVENTS, FINDINGS, CO, SE, SV :)
for $dataset in $definedoc//odm:ItemGroupDef[upper-case(@def:Class)='INTERVENTIONS' or upper-case(@def:Class)='EVENTS' or upper-case(@def:Class)='FINDINGS' 
		or upper-case(./def21:Class/@Name)='INTERVENTIONS' or upper-case(./def21:Class/@Name)='EVENTS' or upper-case(./def21:Class/@Name)='FINDINGS'
		or @Domain='CO' or @Domain='SE' or @Domain='SV']
    let $name := $dataset/@Name
    let $datasetname := (
		if($defineversion = '2.1') then $dataset/def21:leaf/@xlink:href
		else $dataset/def:leaf/@xlink:href
	)
    let $datasetdoc := (
		if($datasetname) then doc(concat($base,$datasetname))
		else ()
	)
    (: Get the OIDs of the --DY and --DTC variables  :)
    let $dyoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'DY') and string-length(@Name)=4]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
    )
    let $dyname := concat($name,'DY')
    let $dtcoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'DTC') and string-length(@Name)=5]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
    ) 
    let $dtcname := $definedoc//odm:ItemDef[@OID=$dtcoid]/@Name 
    (: and the OID of the USUBJID variable (might be different from the one in DM) :)
    let $usubjidoid := (
        for $a in $definedoc//odm:ItemDef[@Name='USUBJID']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
    )
    for $record in $datasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$dtcoid]]
        let $recnum := $record/@data:ItemGroupDataSeq
        (: get the USUBJID value :)
        let $usubjidvalue := $record/odm:ItemData[@ItemOID=$usubjidoid]/@Value
        (: get the --DTC value :)
        let $dtcvalue := $record/odm:ItemData[@ItemOID=$dtcoid]/@Value
        (: and check whether it is a complete date - i.e. string-length at least 10 :)
        let $iscompletedtcvalue := string-length($dtcvalue) >= 10  (: boolean :)
        (: look up the record in the DM dataset and get the RFSTDTC :)
        (: keeping worst case scenario into account that there are two records for the same USUBJID in DM - take the first :)  
        (: let $rfstdtcvalue := doc($dmdatasetlocation)//odm:ItemGroupData[odm:ItemData[@ItemOID=$dmusubjidoid and @Value=$usubjidvalue]][1]/odm:ItemData[@ItemOID=$rfstdtcoid]/@Value :)
        (: and check whether also RFSTDTC is complete :)
        let $hascompleterfstdtcvalue := functx:is-value-in-sequence($usubjidvalue,$usubjidincompleterfstdtc)
        let $dyvalue := $record/odm:ItemData[@ItemOID=$dyoid]/@Value
        (: when one of RFSTDTC and xxDTC is not complete, there may NOT be a xxDY value :)
        where (not($iscompletedtcvalue) or $hascompleterfstdtcvalue) and $dyvalue
        return <warning rule="SD1085" dataset="{data($name)}" variable="{data($dyname)}" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">{data($dyname)} is imputed (value={data($dyvalue)}) although one of {data($dtcname)} (value={data($dtcvalue)}) or RFSTDTC is not a complete date</warning> 	
]]>
</rulexquery>
</sdsrule>

<!-- NEW much faster ALGORITHM 2020-08-08  -->
<sdsrule id="SD1085-SD" last-update="2020-08-08" originator="FDA" standard="SDTM" requiresDomainOrDataset="Yes">
<ruledescription>--DY variable value may not be imputed</ruledescription>
<ruledetaileddescription>--DY variable may be only populated, when both Collection Study Date/Time (--DTC) and Subject Reference Start Date/Time (RFSTDTC) variables values are provided and both of them include complete date part</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>INTERVENTIONS</domain>
<domain>EVENTS</domain>
<domain>FINDINGS</domain>
<domain>CO</domain>
<domain>SE</domain>
<domain>SV</domain>
<rulexquery><![CDATA[	
(: Rule SD1085 - --DY variable value is imputed:
Collection Study Day (--DY) variable value should be not be imputed. It may be only populated, when both Collection Study Date/Time (--DTC) and Subject Reference Start Date/Time (RFSTDTC) variables values are provided and both of them include complete date part.
:)
(: Applies to: INTERVENTIONS, EVENTS, FINDINGS, CO, SE, SV :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
declare namespace functx = "http://www.functx.com";
(: function to find out whether a value is in a sequence (array) :)
declare function functx:is-value-in-sequence
  ( $value as xs:anyAtomicType? ,
    $seq as xs:anyAtomicType* )  as xs:boolean {
   $value = $seq
} ;
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
declare variable $datasetname external;
(: let $base := '/db/fda_submissions/cdisc01/' 
let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: Get the location of the DM dataset :)
let $dmdataset := (
	if($defineversion = '2.1') then $definedoc//odm:ItemGroupDef[@Name='DM']/def21:leaf/@xlink:href
	else $definedoc//odm:ItemGroupDef[@Name='DM']/def:leaf/@xlink:href
)
let $dmdatasetdoc := doc(concat($base,$dmdataset))
(: get the OID of the RFSTDTC variable :)
let $rfstdtcoid := (
    for $a in $definedoc//odm:ItemDef[@Name='RFSTDTC']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name='DM']/odm:ItemRef/@ItemOID
        return $a   
)
(: get the OID of the USUBJID variable :)
let $dmusubjidoid := (
    for $a in $definedoc//odm:ItemDef[@Name='USUBJID']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name='DM']/odm:ItemRef/@ItemOID
        return $a  
)
(: make a list of all USUBJID values for which RFSTDTC is not a complete date :)
let $usubjidincompleterfstdtc := (
    for $record in $dmdatasetdoc//odm:ItemGroupData
        where string-length($record/odm:ItemData[@ItemOID=$rfstdtcoid]/@Value) < 10
        return $record/odm:ItemData[@ItemOID=$dmusubjidoid]/@Value
)
(: iterate over all other datasets in INTERVENTIONS, EVENTS, FINDINGS, CO, SE, SV :)
for $dataset in $definedoc//odm:ItemGroupDef[@Name=$datasetname][upper-case(@def:Class)='INTERVENTIONS' or upper-case(@def:Class)='EVENTS' or upper-case(@def:Class)='FINDINGS' 
		or upper-case(./def21:Class/@Name)='INTERVENTIONS' or upper-case(./def21:Class/@Name)='EVENTS' or upper-case(./def21:Class/@Name)='FINDINGS'
		or @Domain='CO' or @Domain='SE' or @Domain='SV']
    let $name := $dataset/@Name
    let $dsname := (
		if($defineversion = '2.1') then $dataset/def21:leaf/@xlink:href
		else $dataset/def:leaf/@xlink:href
	)
    let $datasetdoc := (
		if($dsname) then doc(concat($base,$dsname))
		else ()
	)
    (: Get the OIDs of the --DY and --DTC variables  :)
    let $dyoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'DY') and string-length(@Name)=4]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
    )
    let $dyname := concat($name,'DY')
    let $dtcoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'DTC') and string-length(@Name)=5]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
    ) 
    let $dtcname := $definedoc//odm:ItemDef[@OID=$dtcoid]/@Name 
    (: and the OID of the USUBJID variable (might be different from the one in DM) :)
    let $usubjidoid := (
        for $a in $definedoc//odm:ItemDef[@Name='USUBJID']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
    )
    for $record in $datasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$dtcoid]]
        let $recnum := $record/@data:ItemGroupDataSeq
        (: get the USUBJID value :)
        let $usubjidvalue := $record/odm:ItemData[@ItemOID=$usubjidoid]/@Value
        (: get the --DTC value :)
        let $dtcvalue := $record/odm:ItemData[@ItemOID=$dtcoid]/@Value
        (: and check whether it is a complete date - i.e. string-length at least 10 :)
        let $iscompletedtcvalue := string-length($dtcvalue) >= 10  (: boolean :)
        (: look up the record in the DM dataset and get the RFSTDTC :)
        (: keeping worst case scenario into account that there are two records for the same USUBJID in DM - take the first :)  
        (: let $rfstdtcvalue := doc($dmdatasetlocation)//odm:ItemGroupData[odm:ItemData[@ItemOID=$dmusubjidoid and @Value=$usubjidvalue]][1]/odm:ItemData[@ItemOID=$rfstdtcoid]/@Value :)
        (: and check whether also RFSTDTC is complete :)
        let $hasincompleterfstdtcvalue := functx:is-value-in-sequence($usubjidvalue,$usubjidincompleterfstdtc)
        let $dyvalue := $record/odm:ItemData[@ItemOID=$dyoid]/@Value
        (: when one of RFSTDTC and xxDTC is not complete, there may NOT be a xxDY value :)
        where (not($iscompletedtcvalue) or $hasincompleterfstdtcvalue) and $dyvalue
        return <warning rule="SD1085" dataset="{data($name)}" variable="{data($dyname)}" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">{data($dyname)} is imputed (value={data($dyvalue)}) although one of {data($dtcname)} (value={data($dtcvalue)}) or RFSTDTC is not a complete date</warning> 	
]]>
</rulexquery>
</sdsrule>

<!-- Rule SD0034 applies to: INTERVENTIONS, EVENTS, FINDINGS, CO -->
<sdsrule id="SD0034" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>--TPTREF must be provided, when --ELTM is provided</ruledescription>
<ruledetaileddescription>Time Point Reference (--TPTREF) should not be NULL, when Planned Elapsed Time from Time Point Ref (--ELTM) is populated</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>ALL</domain>
<rulexquery><![CDATA[	
(: Rule SD0034 - Missing value for --TPTREF, when --ELTM is provided
Time Point Reference (--TPTREF) should not be NULL, when Planned Elapsed Time from Time Point Ref (--ELTM) is populated :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink"; 
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/'  :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: iterate over all datasets except for DM :)
for $dataset in $definedoc//odm:ItemGroupDef[upper-case(@def:Class)='INTERVENTIONS' or upper-case(@def:Class)='EVENTS' or upper-case(@def:Class)='FINDINGS' 
		or upper-case(./def21:Class/@Name)='INTERVENTIONS' or upper-case(./def21:Class/@Name)='EVENTS' or upper-case(./def21:Class/@Name)='FINDINGS'
		or @Domain='CO']
    let $name := $dataset/@Name
    let $datasetname := (
		if($defineversion = '2.1') then $dataset/def21:leaf/@xlink:href
		else $dataset/def:leaf/@xlink:href
	)
    let $datasetdoc := (
		if($datasetname) then doc(concat($base,$datasetname))
		else ()
	)
    (: Get the OID of the TPTREF and ELTM variable (when present) :)
    let $tptrefoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'TPTREF')]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
    )
    (: let $tptrefname := $definedoc//odm:ItemDef[@OID=$tptrefoid]/@Name :)
    let $tptrefname := concat($name,'TPTREF')
    let $eltmoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'ELTM')]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
    )
    let $eltmname := $definedoc//odm:ItemDef[@OID=$eltmoid]/@Name
    (: iterate over all the records that do have an ELTM data point :)
    for $record in $datasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$eltmoid]]
        let $recnum := $record/@data:ItemGroupDataSeq
        (: get the value of the TPTREF and ELTM data points (if any) :)
        let $tptrefvalue := $record/odm:ItemData[@ItemOID=$tptrefoid]/@Value
        let $eltmvalue := $record/odm:ItemData[@ItemOID=$eltmoid]/@Value 
        (: when ELTM is populated, TPTREF must be present :)
        where $eltmvalue and not($tptrefvalue)
        return <warning rule="SD0034" variable="{data($tptrefname)}" dataset="{data($name)}" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">Missing value for {data($tptrefname)} when {data($eltmname)} is populated, value={data($eltmvalue)}</warning>	
]]></rulexquery>
</sdsrule>

<!-- Rule SD0034 applies to: INTERVENTIONS, EVENTS, FINDINGS, CO -->
<sdsrule id="SD0034-SD" last-update="2020-08-08" originator="FDA" standard="SDTM" requiresDomainOrDataset="Yes">
<ruledescription>--TPTREF must be provided, when --ELTM is provided</ruledescription>
<ruledetaileddescription>Time Point Reference (--TPTREF) should not be NULL, when Planned Elapsed Time from Time Point Ref (--ELTM) is populated</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>ALL</domain>
<rulexquery><![CDATA[	
(: Rule SD0034 - Missing value for --TPTREF, when --ELTM is provided
Time Point Reference (--TPTREF) should not be NULL, when Planned Elapsed Time from Time Point Ref (--ELTM) is populated :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink"; 
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
declare variable $datasetname external;
(: let $base := '/db/fda_submissions/cdisc01/'  :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: iterate over all datasets except for DM :)
for $dataset in $definedoc//odm:ItemGroupDef[@Name=$datasetname][upper-case(@def:Class)='INTERVENTIONS' or upper-case(@def:Class)='EVENTS' or upper-case(@def:Class)='FINDINGS' 
		or upper-case(./def21:Class/@Name)='INTERVENTIONS' or upper-case(./def21:Class/@Name)='EVENTS' or upper-case(./def21:Class/@Name)='FINDINGS' or @Domain='CO']
    let $name := $dataset/@Name
    let $dsname := (
		if($defineversion = '2.1') then $dataset/def21:leaf/@xlink:href
		else $dataset/def:leaf/@xlink:href
	)
    let $datasetdoc := (
		if($dsname) then doc(concat($base,$dsname))
		else ()
	)
    (: Get the OID of the TPTREF and ELTM variable (when present) :)
    let $tptrefoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'TPTREF')]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
    )
    (: let $tptrefname := $definedoc//odm:ItemDef[@OID=$tptrefoid]/@Name :)
    let $tptrefname := concat($name,'TPTREF')
    let $eltmoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'ELTM')]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
    )
    let $eltmname := $definedoc//odm:ItemDef[@OID=$eltmoid]/@Name
    (: iterate over all the records that do have an ELTM data point :)
    for $record in $datasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$eltmoid]]
        let $recnum := $record/@data:ItemGroupDataSeq
        (: get the value of the TPTREF and ELTM data points (if any) :)
        let $tptrefvalue := $record/odm:ItemData[@ItemOID=$tptrefoid]/@Value
        let $eltmvalue := $record/odm:ItemData[@ItemOID=$eltmoid]/@Value 
        (: when ELTM is populated, TPTREF must be present :)
        where $eltmvalue and not($tptrefvalue)
        return <warning rule="SD0034" variable="{data($tptrefname)}" dataset="{data($name)}" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">Missing value for {data($tptrefname)} when {data($eltmname)} is populated, value={data($eltmvalue)}</warning>
]]></rulexquery>
</sdsrule>

<!-- NEW: using a slightly faster algorithm 2020-08-08  -->
<sdsrule id="SD1015" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>EPOCH value must match an entry in TA</ruledescription>
<ruledetaileddescription>Epoch (EPOCH) values in all datasets should match entries in the Trial Arms (TA) dataset</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>ALL</domain>
<rulexquery><![CDATA[	
(: Rule SD1015 - Invalid EPOCH:
Epoch (EPOCH) values in all datasets should match entries in the Trial Arms (TA) dataset
:)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
declare namespace functx = "http://www.functx.com";
(: function to find out whether a value is in a sequence (array) :)
declare function functx:is-value-in-sequence
  ( $value as xs:anyAtomicType? ,
    $seq as xs:anyAtomicType* )  as xs:boolean {
   $value = $seq
} ;
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: Get the TA dataset :)
let $tadataset := $definedoc//odm:ItemGroupDef[@Name='TA']
let $tadatasetname := (
	if($defineversion = '2.1') then $tadataset/def21:leaf/@xlink:href
	else $tadataset/def:leaf/@xlink:href
)
let $tadatasetdoc := (
	if($tadatasetname) then doc(concat($base,$tadatasetname))
	else ()
)
(: and get the OID of the EPOCH variable :)
let $taepochoid := (
    for $a in $definedoc//odm:ItemDef[@Name='EPOCH']/@OID 
    where $a = $definedoc//odm:ItemGroupDef[@Name='TA']/odm:ItemRef/@ItemOID
    return $a
)
(: NEW method 2020-08-08: make a list of all EPOCH values in TA :)
let $epochvalues := (
    for $epoch in $tadatasetdoc//odm:ItemGroupData/odm:ItemData[@ItemOID=$taepochoid]/@Value
    return $epoch
)
(: now iterate over all other datasets :)
let $datasets := $definedoc//odm:ItemGroupDef[not(@Name='TA')]
for $dataset in $datasets
    (: get name and location :)
    let $name := $dataset/@Name
    let $datasetname := (
		if($defineversion = '2.1') then $dataset/def21:leaf/@xlink:href
		else $dataset/def:leaf/@xlink:href
	)
    let $datasetdoc := (
		if($datasetname) then doc(concat($base,$datasetname))
		else ()
	)
    (: and the OID of EPOCH :)
    let $datasetepochoid := (
        for $a in $definedoc//odm:ItemDef[@Name='EPOCH']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
    )
    (: iterate over all records in this dataset that have a EPOCH variable :)
    for $record in $datasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$datasetepochoid]]
        let $recnum := $record/@data:ItemGroupDataSeq
        (: now get the value of the EPOCH variable :)
        let $epochvalue := $record/odm:ItemData[@ItemOID=$datasetepochoid]/@Value
        (: and check whether it is present in the TA dataset :)
        (: where not(doc($tadatasetlocation)//odm:ItemGroupData/odm:ItemData[@ItemOID=$taepochoid][@Value=$epochvalue]) :)
        (: NEW METHOD 2020-08-08: compare with the list of EPOCH values in TA :)
        where not(functx:is-value-in-sequence($epochvalue, $epochvalues))
        return <error rule="SD1015" dataset="{data($name)}" variable="EPOCH" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">Invalid EPOCH. Value for EPOCH={data($epochvalue)} in dataset {data($datasetname)} was not found in the TA dataset {data($tadatasetname)}</error>		
]]></rulexquery>
</sdsrule>

<!-- NEW: using a slightly faster algorithm 2020-08-08  -->
<sdsrule id="SD1015-SD" last-update="2020-08-08" originator="FDA" standard="SDTM" requiresDomainOrDataset="Yes">
<ruledescription>EPOCH value must match an entry in TA</ruledescription>
<ruledetaileddescription>Epoch (EPOCH) values in all datasets should match entries in the Trial Arms (TA) dataset</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>ALL</domain>
<rulexquery><![CDATA[	
(: Rule SD1015 - Invalid EPOCH:
Epoch (EPOCH) values in all datasets should match entries in the Trial Arms (TA) dataset
:)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
declare namespace functx = "http://www.functx.com";
(: function to find out whether a value is in a sequence (array) :)
declare function functx:is-value-in-sequence
  ( $value as xs:anyAtomicType? ,
    $seq as xs:anyAtomicType* )  as xs:boolean {
   $value = $seq
} ;
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
declare variable $datasetname external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: Get the TA dataset :)
let $tadataset := $definedoc//odm:ItemGroupDef[@Name='TA']
let $tadatasetname := (
	if($defineversion = '2.1') then $tadataset/def21:leaf/@xlink:href
	else $tadataset/def:leaf/@xlink:href
)
let $tadatasetdoc := (
	if($tadatasetname) then doc(concat($base,$tadatasetname))
	else ()
)
(: and get the OID of the EPOCH variable :)
let $taepochoid := (
    for $a in $definedoc//odm:ItemDef[@Name='EPOCH']/@OID 
    where $a = $definedoc//odm:ItemGroupDef[@Name='TA']/odm:ItemRef/@ItemOID
    return $a
)
(: NEW method 2020-08-08: make a list of all EPOCH values in TA :)
let $epochvalues := (
    for $epoch in $tadatasetdoc//odm:ItemGroupData/odm:ItemData[@ItemOID=$taepochoid]/@Value
    return $epoch
)
(: now iterate over all other provided datasets :)
let $datasets := $definedoc//odm:ItemGroupDef[@Name=$datasetname][not(@Name='TA')]
for $dataset in $datasets
    (: get name and location :)
    let $name := $dataset/@Name
    let $dsname := (
		if($defineversion = '2.1') then $dataset/def21:leaf/@xlink:href
		else $dataset/def:leaf/@xlink:href
	)
    let $datasetdoc := (
		if($dsname) then doc(concat($base,$dsname))
		else ()
	)
    (: and the OID of EPOCH :)
    let $datasetepochoid := (
        for $a in $definedoc//odm:ItemDef[@Name='EPOCH']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
    )
    (: iterate over all records in this dataset that have a EPOCH variable :)
    for $record in $datasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$datasetepochoid]]
        let $recnum := $record/@data:ItemGroupDataSeq
        (: now get the value of the EPOCH variable :)
        let $epochvalue := $record/odm:ItemData[@ItemOID=$datasetepochoid]/@Value
        (: and check whether it is present in the TA dataset :)
        (: where not(doc($tadatasetlocation)//odm:ItemGroupData/odm:ItemData[@ItemOID=$taepochoid][@Value=$epochvalue]) :)
        (: NEW METHOD 2020-08-08: compare with the list of EPOCH values in TA :)
        where not(functx:is-value-in-sequence($epochvalue, $epochvalues))
        return <error rule="SD1015" dataset="{data($name)}" variable="EPOCH" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">Invalid EPOCH. Value for EPOCH={data($epochvalue)} in dataset {data($datasetname)} was not found in the TA dataset {data($tadatasetname)}</error>		
]]></rulexquery>
</sdsrule>

<!-- Rule SD0055: SDTM/dataset variable type mismatch - IG: Using SDTM-specified data types for all variables
The data Type (e.g., whether the variable value is a character or numeric).
TODO: will be implemented using the CDISC Library API -->

<sdsrule id="SD0058" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>Variable appearing in dataset must be in the SDTM model</ruledescription>
<ruledetaileddescription>Only variables listed in SDTM model should appear in a dataset. New sponsor defined variables must not be added, and existing variables must not be renamed or modified</ruledetaileddescription>
<!--igversion>3.1.2</igversion-->
<!--igversion>3.1.3</igversion-->
<igversion>3.2</igversion>
<domain>ALL</domain>
<rulexquery><![CDATA[
(: Rule SD0058 - Variable appears in dataset, but is not in SDTM model :)
(: TODO: implement using CDISC Library API :)
(: Currently only implemented for SDTM 1.4 - SDTM-IG 3.2 :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
declare namespace xs="http://www.w3.org/2001/XMLSchema";
declare namespace functx = "http://www.functx.com";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external;
declare variable $defineversion external;
(: function to find out whether a value is in a sequence (array) :)
declare function functx:is-value-in-sequence
  ( $value as xs:anyAtomicType? ,
    $seq as xs:anyAtomicType* )  as xs:boolean {
   $value = $seq
} ;
(: value-intersect function: returns the intersection of two sequences :)
declare function functx:value-intersect
  ( $arg1 as xs:anyAtomicType* ,
    $arg2 as xs:anyAtomicType* )  as xs:anyAtomicType* {

  distinct-values($arg1[.=$arg2])
 } ;
(: combines two sequences :)
declare function functx:value-union
  ( $arg1 as xs:anyAtomicType* ,
    $arg2 as xs:anyAtomicType* )  as xs:anyAtomicType* {
  distinct-values(($arg1, $arg2))
 } ;
(: function replacing two subsequent dashes with the domain code :)
declare function local:replacedash($sequence,$domain) {
    for $v in $sequence
            return replace($v,'\-\-',$domain)
} ;
(: TODO: can we get the identifiers, timing variables, and all other variables from a RESTful web service as function of SDTM version? 
Yes, this is possible - TODO :)
(: SDTM version 1.4 :)
(: Identifiers for all classes :)
let $identifiers := ('STUDYID','DOMAIN','USUBJID','POOLID','SPDEVID','--SEQ','--GRPID',
    '--REFID','--SPID','--LNKID','--LNKGRP')
(: Timing variables for all classes :)
let $timingvars := ('VISITNUM','VISIT','VISITDY','TAETORD','EPOCH','--DTC','--STDTC','--ENDTC', '--DY','--STDY','--ENDY','--DUR','--TPT','--TPTNUM','--ELTM','--TPTREF','--RFTDTC','--STRF','--ENRF','--EVLINT','--EVINTX','--STRTPT','--STTPT','--ENRTPT','--ENTPT','--STINT','--ENINT','--DETECT')
let $dmvars := ('STUDYID','DOMAIN','USUBJID','SUBJID','RFSTDTC','RFENDTC','RFXSTDTC','RFXENDTC','RFICDTC','RFPENDTC','DTHDTC','DTHFL','SITEID','INVID','INVNAM','BRTHDTC','AGE','AGETXT','AGEU','SEX','RACE','ETHNIC','SPECIES','STRAIN','SBSTRAIN','ARMCD','ARM','ACTARMCD','ACTARM','SETCD','COUNTRY','DMDTC','DMDY')
(: variables for Interventions classes :) 
let $interventionvars := ('--TRT','--MODIFY','--DECOD','--MOOD','--CAT','--SCAT','--PRESP','--OCCUR','--STAT','--REASND','--INDC','--CLAS','--CLASCD','--DOSE','--DOSTXT','--DOSU','--DOSFRM','--DOSFRQ','--DOSTOT','--DOSRGM','--ROUTE','--LOT','--LOC','--LAT','--DIR','--PORTOT','--FAST','--PSTRG','--PSTRGU','--TRTV','--VAMT','--VAMTU','--ADJ')
(: variables for all Event classes :)
let $eventvars := ('--TERM','--MODIFY','--LLT','--LLTCD','--DECOD','--PTCD','--HLT','--HLTCD','--HLGT','--HLGTCD','--CAT','--SCAT','--PRESP','--OCCUR','--STAT','--REASND','--BODSYS','--BDSYCD','--SOC','--SOCCD','--LOC','--LAT','--DIR','--PORTOT','--PARTY','--PRTYID','--SEV','--SER','--ACN','--ACNOTH','--ACNDEV','--REL','--RELNST','--PATT','--OUT','--SCAN','--SCONG','--SDISAB','--SDTH','--SHOSP','--SLIFE','--SOD','--SMIE','--CONTRT','--TOX','--TOXGR')
let $findingvars := ('--TESTCD','--TEST','--MODIFY','--TSTDTL','--CAT','--SCAT','--POS','--BODSYS','--ORRES','--ORRESU','--ORNRLO','--ORNRHI','--STRESC','--STRESN','--STRESU','--STNRLO','--STNRHI','--STNRC','--NRIND','--RESCAT','--STAT','--REASND','--XFN','--NAM','--LOINC','--SPEC','--ANTREG','--SPCCND','--SPCUFL','--LOC','--LAT','--DIR','--PORTOT','--METHOD','--RUNID','--ANMETH','--LEAD','--CSTATE','--BLFL','--FAST','--DRVFL','--EVAL','--EVALID','--ACPTFL','--TOX','--TOXGR','--SEV','--DTHREL','--LLOQ','--ULOQ','--EXCLFL','--REASEX')
(: FA-domains: --OBJ - comes after --TEST :)
let $findingsaboutvars := ('--TESTCD','--TEST','--OBJ','--MODIFY','--TSTDTL','--CAT','--SCAT','--POS','--BODSYS','--ORRES','--ORRESU','--ORNRLO','--ORNRHI','--STRESC','--STRESN','--STRESU','--STNRLO','--STNRHI','--STNRC','--NRIND','--RESCAT','--STAT','--REASND','--XFN','--NAM','--LOINC','--SPEC','--ANTREG','--SPCCND','--SPCUFL','--LOC','--LAT','--DIR','--PORTOT','--METHOD','--RUNID','--ANMETH','--LEAD','--CSTATE','--BLFL','--FAST','--DRVFL','--EVAL','--EVALID','--ACPTFL','--TOX','--TOXGR','--SEV','--DTHREL','--LLOQ','--ULOQ','--EXCLFL','--REASEX') 
(: Special purpose: Comments (CO) domain - 
 Remark that we do not allow for COVAL1, COVAL2, ... as we are using Dataset-XML 
 which doesn't have the 200 character limitation :)
let $commentvars := ('STUDYID','DOMAIN','RDOMAIN','USUBJID','POOLID','COSEQ','IDVAR','IDVARVAL','COREF','COVAL','COEVAL','CODTC')
(: Subject Elements (SE) :)
let $subjectelementsvar := ('STUDYID','DOMAIN','USUBJID','SESEQ','ETCD','ELEMENT','SESTDTC','SEENDTC','TAETORD','EPOCH','SEUPDES')
(: Subject Visits (SV) :)
let $subjectvisitsvar := ('STUDYID','DOMAIN','USUBJID','VISITNUM','VISIT','VISITDY','SVSTDTC','SVENDTC','SVSTDY','SVENDY','SVUPDES')
(: Trial Design datasets :)
(: Trial Elements (TE) :)
let $trialelementvars := ('STUDYID','DOMAIN','ETCD','ELEMENT','TESTRL','TEENRL','TEDUR')
(: Trial Arms :)
let $trialarmvars := ('STUDYID','DOMAIN','ARMCD','ARM','TAETORD','ETCD','ELEMENT','TABRANCH','TATRANS','EPOCH')
(: Trial Visits (TV) :)
let $trialvisitvars := ('STUDYID','DOMAIN','VISITNUM','VISIT','VISITDY','ARMCD','ARM','TVSTRL','TVENRL')
(: Trial Sets (TX) :)
let $trialsetvars := ('STUDYID','DOMAIN','SETCD','SET','TXSEQ','TXPARMCD','TXPARM','TXVAL')
(: Trial Inclusion/Exclusion Criteria (TI) :)
let $trialievars := ('STUDYID','DOMAIN','IETESTCD','IETEST','IECAT','IESCAT','TIRL','TIVERS')
(: Trial Summary (TS) :)
let $trialsummaryvars := ('STUDYID','DOMAIN','TSSEQ','TSGRPID','TSPARMCD','TSPARM','TSVAL','TSVALNF','TSVALCD','TSVCDREF','TSVCDVER')
(: Trial Disease Assessments (TD) :)
let $trialdiseasevars := ('STUDYID','DOMAIN','TDORDER','TDANCVAR','TDSTOFF','TDTGTPAI','TDMINPAI','TDMAXPAI','TDNUMRPT')
(: Related Records (RELREC) :)
let $relrecvars := ('STUDYID','RDOMAIN','USUBJID','APID','POOLID','IDVAR','IDVARVAL','RELTYPE','RELID')
(: Get the define.xml file :)
(: let $base:= '/db/fda_submissions/cdiscpilot01/' :)
(: let $define := 'define_2_0.xml' :)
let $definedoc := doc(concat($base,$define))
(: iterate over all ItemGroupDef elements in define.xml :)
for $dataset in $definedoc//odm:ItemGroupDef
	let $datasetname := $dataset/@Name
    (: get the 2-letter domain (except for SUPPQUAL, RELREC, ...)
        as we need it to replace the "--" by the domain code
    :)
    (: Get the domain name and class :)
    (: let $domaincode := $dataset/@Name :)
    let $domaincode := $dataset/@Domain
    let $domainclass := (
		if($defineversion = '2.1') then $dataset/./def21:Class/@Name
		else $dataset/@def:Class
	)
    (: iterate over the ItemRef elements and get the OID - then transform it to the Name
    make an array out of it :)
    let $vararray := (
    for $itemref in $dataset/odm:ItemRef
        (: Attention - we need to exclude non-standard variables (NSVs) :)
        let $itemrefoid := $itemref[not(starts-with(upper-case(@Role),'SUPPLEMENTAL'))]/@ItemOID
        (: Get the name of the variable from the corresponding ItemDef :)
        let $varname := $definedoc//odm:ItemDef[@OID=$itemrefoid]/@Name
        return $varname
    )
    (: now create the array to compare with - we need to replace any '--' by the domain code :)
    (: replace any dashes in the variable names by the domain code :)
    let $identifiers := local:replacedash($identifiers,$domaincode)
    let $timingvars := local:replacedash($timingvars,$domaincode)
    let $interventionvars := local:replacedash($interventionvars,$domaincode)
    let $eventvars := local:replacedash($eventvars,$domaincode)
    let $findingvars := local:replacedash($findingvars,$domaincode)
    (: combine all variables: identifiers, class-specific variables and timing variables 
    into a single sequence :)
    let $standardvars := (
        if ($domaincode = 'DM') then $dmvars
        else if($domaincode = 'CO') then $commentvars
        else if($domaincode = 'SE') then $subjectelementsvar
        else if($domaincode = 'SV') then $subjectvisitsvar
        else if($domaincode = 'TV') then $trialvisitvars
        (: Trial Design datasets :)
        else if($domaincode = 'TE') then $trialelementvars
        else if($domaincode = 'TA') then $trialarmvars
        else if($domaincode = 'TX') then $trialsetvars
        else if($domaincode = 'TI') then $trialievars
        else if($domaincode = 'TS') then $trialsummaryvars
        else if($domaincode = 'TD') then $trialdiseasevars
        (: Related Records dataset :)
        else if($domaincode = 'RELREC') then $relrecvars
        else if (upper-case($domainclass) = 'INTERVENTIONS' ) then
            functx:value-union(functx:value-union($identifiers,$interventionvars),$timingvars)
        else if (upper-case($domainclass) = 'EVENTS') then
            functx:value-union(functx:value-union($identifiers,$eventvars),$timingvars)
        else if (upper-case($domainclass) = 'FINDINGS') then
            functx:value-union(functx:value-union($identifiers,$findingvars),$timingvars)    
        else ()
    )
    
    (: now check whether each of the variables in the SDTM dataset as declared in the define.xml is in the sequence of the allowed variables :)
    for $var in $vararray
        where functx:is-value-in-sequence($var,$standardvars) = false()
        (: and give an error when this is not the case :)
        return <error rule="SD0058" dataset="{data($datasetname)}" variable="{data($var)}" rulelastupdate="2020-08-08">Variable {data($var)} is not in the SDTM model (v.1.4) for domain {data($domaincode)} </error>	
]]></rulexquery>
</sdsrule>

<sdsrule id="SD0058-SD" last-update="2020-08-08" originator="FDA" standard="SDTM" requiresDomainOrDataset="Yes">
<ruledescription>Variable appearing in dataset must be in the SDTM model</ruledescription>
<ruledetaileddescription>Only variables listed in SDTM model should appear in a dataset. New sponsor defined variables must not be added, and existing variables must not be renamed or modified</ruledetaileddescription>
<!--igversion>3.1.2</igversion-->
<!--igversion>3.1.3</igversion-->
<igversion>3.2</igversion>
<domain>ALL</domain>
<rulexquery><![CDATA[
(: Rule SD0058 - Variable appears in dataset, but is not in SDTM model :)
(: Currently only implemented for SDTM 1.4 - SDTM-IG 3.2 :)
(: TODO: implement using CDISC Library API :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
declare namespace xs="http://www.w3.org/2001/XMLSchema";
declare namespace functx = "http://www.functx.com";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external;
declare variable $defineversion external;
declare variable $datasetname external;
(: function to find out whether a value is in a sequence (array) :)
declare function functx:is-value-in-sequence
  ( $value as xs:anyAtomicType? ,
    $seq as xs:anyAtomicType* )  as xs:boolean {
   $value = $seq
} ;
(: value-intersect function: returns the intersection of two sequences :)
declare function functx:value-intersect
  ( $arg1 as xs:anyAtomicType* ,
    $arg2 as xs:anyAtomicType* )  as xs:anyAtomicType* {

  distinct-values($arg1[.=$arg2])
 } ;
(: combines two sequences :)
declare function functx:value-union
  ( $arg1 as xs:anyAtomicType* ,
    $arg2 as xs:anyAtomicType* )  as xs:anyAtomicType* {
  distinct-values(($arg1, $arg2))
 } ;
(: function replacing two subsequent dashes with the domain code :)
declare function local:replacedash($sequence,$domain) {
    for $v in $sequence
            return replace($v,'\-\-',$domain)
} ;
(: TODO: can we get the identifiers, timing variables, and all other variables from a RESTful web service as function of SDTM version? 
Yes, this is possible - TODO :)
(: SDTM version 1.4 :)
(: Identifiers for all classes :)
let $identifiers := ('STUDYID','DOMAIN','USUBJID','POOLID','SPDEVID','--SEQ','--GRPID',
    '--REFID','--SPID','--LNKID','--LNKGRP')
(: Timing variables for all classes :)
let $timingvars := ('VISITNUM','VISIT','VISITDY','TAETORD','EPOCH','--DTC','--STDTC','--ENDTC', '--DY','--STDY','--ENDY','--DUR','--TPT','--TPTNUM','--ELTM','--TPTREF','--RFTDTC','--STRF','--ENRF','--EVLINT','--EVINTX','--STRTPT','--STTPT','--ENRTPT','--ENTPT','--STINT','--ENINT','--DETECT')
let $dmvars := ('STUDYID','DOMAIN','USUBJID','SUBJID','RFSTDTC','RFENDTC','RFXSTDTC','RFXENDTC','RFICDTC','RFPENDTC','DTHDTC','DTHFL','SITEID','INVID','INVNAM','BRTHDTC','AGE','AGETXT','AGEU','SEX','RACE','ETHNIC','SPECIES','STRAIN','SBSTRAIN','ARMCD','ARM','ACTARMCD','ACTARM','SETCD','COUNTRY','DMDTC','DMDY')
(: variables for Interventions classes :) 
let $interventionvars := ('--TRT','--MODIFY','--DECOD','--MOOD','--CAT','--SCAT','--PRESP','--OCCUR','--STAT','--REASND','--INDC','--CLAS','--CLASCD','--DOSE','--DOSTXT','--DOSU','--DOSFRM','--DOSFRQ','--DOSTOT','--DOSRGM','--ROUTE','--LOT','--LOC','--LAT','--DIR','--PORTOT','--FAST','--PSTRG','--PSTRGU','--TRTV','--VAMT','--VAMTU','--ADJ')
(: variables for all Event classes :)
let $eventvars := ('--TERM','--MODIFY','--LLT','--LLTCD','--DECOD','--PTCD','--HLT','--HLTCD','--HLGT','--HLGTCD','--CAT','--SCAT','--PRESP','--OCCUR','--STAT','--REASND','--BODSYS','--BDSYCD','--SOC','--SOCCD','--LOC','--LAT','--DIR','--PORTOT','--PARTY','--PRTYID','--SEV','--SER','--ACN','--ACNOTH','--ACNDEV','--REL','--RELNST','--PATT','--OUT','--SCAN','--SCONG','--SDISAB','--SDTH','--SHOSP','--SLIFE','--SOD','--SMIE','--CONTRT','--TOX','--TOXGR')
let $findingvars := ('--TESTCD','--TEST','--MODIFY','--TSTDTL','--CAT','--SCAT','--POS','--BODSYS','--ORRES','--ORRESU','--ORNRLO','--ORNRHI','--STRESC','--STRESN','--STRESU','--STNRLO','--STNRHI','--STNRC','--NRIND','--RESCAT','--STAT','--REASND','--XFN','--NAM','--LOINC','--SPEC','--ANTREG','--SPCCND','--SPCUFL','--LOC','--LAT','--DIR','--PORTOT','--METHOD','--RUNID','--ANMETH','--LEAD','--CSTATE','--BLFL','--FAST','--DRVFL','--EVAL','--EVALID','--ACPTFL','--TOX','--TOXGR','--SEV','--DTHREL','--LLOQ','--ULOQ','--EXCLFL','--REASEX')
(: FA-domains: --OBJ - comes after --TEST :)
let $findingsaboutvars := ('--TESTCD','--TEST','--OBJ','--MODIFY','--TSTDTL','--CAT','--SCAT','--POS','--BODSYS','--ORRES','--ORRESU','--ORNRLO','--ORNRHI','--STRESC','--STRESN','--STRESU','--STNRLO','--STNRHI','--STNRC','--NRIND','--RESCAT','--STAT','--REASND','--XFN','--NAM','--LOINC','--SPEC','--ANTREG','--SPCCND','--SPCUFL','--LOC','--LAT','--DIR','--PORTOT','--METHOD','--RUNID','--ANMETH','--LEAD','--CSTATE','--BLFL','--FAST','--DRVFL','--EVAL','--EVALID','--ACPTFL','--TOX','--TOXGR','--SEV','--DTHREL','--LLOQ','--ULOQ','--EXCLFL','--REASEX') 
(: Special purpose: Comments (CO) domain - 
 Remark that we do not allow for COVAL1, COVAL2, ... as we are using Dataset-XML 
 which doesn't have the 200 character limitation :)
let $commentvars := ('STUDYID','DOMAIN','RDOMAIN','USUBJID','POOLID','COSEQ','IDVAR','IDVARVAL','COREF','COVAL','COEVAL','CODTC')
(: Subject Elements (SE) :)
let $subjectelementsvar := ('STUDYID','DOMAIN','USUBJID','SESEQ','ETCD','ELEMENT','SESTDTC','SEENDTC','TAETORD','EPOCH','SEUPDES')
(: Subject Visits (SV) :)
let $subjectvisitsvar := ('STUDYID','DOMAIN','USUBJID','VISITNUM','VISIT','VISITDY','SVSTDTC','SVENDTC','SVSTDY','SVENDY','SVUPDES')
(: Trial Design datasets :)
(: Trial Elements (TE) :)
let $trialelementvars := ('STUDYID','DOMAIN','ETCD','ELEMENT','TESTRL','TEENRL','TEDUR')
(: Trial Arms :)
let $trialarmvars := ('STUDYID','DOMAIN','ARMCD','ARM','TAETORD','ETCD','ELEMENT','TABRANCH','TATRANS','EPOCH')
(: Trial Visits (TV) :)
let $trialvisitvars := ('STUDYID','DOMAIN','VISITNUM','VISIT','VISITDY','ARMCD','ARM','TVSTRL','TVENRL')
(: Trial Sets (TX) :)
let $trialsetvars := ('STUDYID','DOMAIN','SETCD','SET','TXSEQ','TXPARMCD','TXPARM','TXVAL')
(: Trial Inclusion/Exclusion Criteria (TI) :)
let $trialievars := ('STUDYID','DOMAIN','IETESTCD','IETEST','IECAT','IESCAT','TIRL','TIVERS')
(: Trial Summary (TS) :)
let $trialsummaryvars := ('STUDYID','DOMAIN','TSSEQ','TSGRPID','TSPARMCD','TSPARM','TSVAL','TSVALNF','TSVALCD','TSVCDREF','TSVCDVER')
(: Trial Disease Assessments (TD) :)
let $trialdiseasevars := ('STUDYID','DOMAIN','TDORDER','TDANCVAR','TDSTOFF','TDTGTPAI','TDMINPAI','TDMAXPAI','TDNUMRPT')
(: Related Records (RELREC) :)
let $relrecvars := ('STUDYID','RDOMAIN','USUBJID','APID','POOLID','IDVAR','IDVARVAL','RELTYPE','RELID')
(: Get the define.xml file :)
(: let $base:= '/db/fda_submissions/cdiscpilot01/' :)
(: let $define := 'define_2_0.xml' :)
let $definedoc := doc(concat($base,$define))
(: iterate over all ItemGroupDef elements in define.xml :)
for $dataset in $definedoc//odm:ItemGroupDef[@Name=$datasetname or @Domain=$datasetname]
	let $dsname := $dataset/@Name
    (: get the 2-letter domain (except for SUPPQUAL, RELREC, ...)
        as we need it to replace the "--" by the domain code
    :)
    (: Get the domain name and class :)
    (: let $domaincode := $dataset/@Name :)
    let $domaincode := $dataset/@Domain
	let $domainclass := (
		if($defineversion = '2.1') then $dataset/./def21:Class/@Name
		else $dataset/@def:Class
	)
    (: iterate over the ItemRef elements and get the OID - then transform it to the Name
    make an array out of it :)
    let $vararray := (
    for $itemref in $dataset/odm:ItemRef
        (: Attention - we need to exclude non-standard variables (NSVs) :)
        let $itemrefoid := $itemref[not(starts-with(upper-case(@Role),'SUPPLEMENTAL'))]/@ItemOID
        (: Get the name of the variable from the corresponding ItemDef :)
        let $varname := $definedoc//odm:ItemDef[@OID=$itemrefoid]/@Name
        return $varname
    )
    (: now create the array to compare with - we need to replace any '--' by the domain code :)
    (: replace any dashes in the variable names by the domain code :)
    let $identifiers := local:replacedash($identifiers,$domaincode)
    let $timingvars := local:replacedash($timingvars,$domaincode)
    let $interventionvars := local:replacedash($interventionvars,$domaincode)
    let $eventvars := local:replacedash($eventvars,$domaincode)
    let $findingvars := local:replacedash($findingvars,$domaincode)
    (: combine all variables: identifiers, class-specific variables and timing variables 
    into a single sequence :)
    let $standardvars := (
        if ($domaincode = 'DM') then $dmvars
        else if($domaincode = 'CO') then $commentvars
        else if($domaincode = 'SE') then $subjectelementsvar
        else if($domaincode = 'SV') then $subjectvisitsvar
        else if($domaincode = 'TV') then $trialvisitvars
        (: Trial Design datasets :)
        else if($domaincode = 'TE') then $trialelementvars
        else if($domaincode = 'TA') then $trialarmvars
        else if($domaincode = 'TX') then $trialsetvars
        else if($domaincode = 'TI') then $trialievars
        else if($domaincode = 'TS') then $trialsummaryvars
        else if($domaincode = 'TD') then $trialdiseasevars
        (: Related Records dataset :)
        else if($domaincode = 'RELREC') then $relrecvars
        else if (upper-case($domainclass) = 'INTERVENTIONS' ) then
            functx:value-union(functx:value-union($identifiers,$interventionvars),$timingvars)
        else if (upper-case($domainclass) = 'EVENTS') then
            functx:value-union(functx:value-union($identifiers,$eventvars),$timingvars)
        else if (upper-case($domainclass) = 'FINDINGS') then
            functx:value-union(functx:value-union($identifiers,$findingvars),$timingvars)    
        else ()
    )
    
    (: now check whether each of the variables in the SDTM dataset as declared in the define.xml is in the sequence of the allowed variables :)
    for $var in $vararray
        where functx:is-value-in-sequence($var,$standardvars) = false()
        (: and give an error when this is not the case :)
        return <error rule="SD0058" dataset="{data($datasetname)}" variable="{data($var)}" rulelastupdate="2020-08-08">Variable {data($var)} is not in the SDTM model (v.1.4) for domain {data($domaincode)} </error>	
]]></rulexquery>
</sdsrule>

<!-- Rule SD1076 (was FDAC031) "SDTM model variable may be added into standard domains according its domain general class, if there are no restrictions on their usage specified in IG"
 is not implemented as it is completely nonsense, 
and leads to false positives and confusion. For example, for EPOCH, one would always get a warning, 
whether EPOCH is included or not -->


<sdsrule id="SD1078" last-update="2020-08-08" originator="FDA" standard="SDTM" webservice="Uses XML4Pharma RESTful Web Service" timeintensive="Yes">
<ruledescription>Permissible variable with missing value for all records is not allowed</ruledescription>
<ruledetaileddescription>Permissible variable whould not be present in domain, when the variable has missing value for all records in the dataset</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>ALL</domain>
<rulexquery><![CDATA[
(: Rule SD1078 - Permissible variable with missing value for all records:
Permissible variable whould not be present in domain, when the variable has missing value for all records in the dataset :)
(:  How do we know that a variable is permissible? Mandatory="No" is essentially not sufficient. 
Use the web service to find out - http://www.xml4pharmaserver.com:8080/CDISCCTService/rest/CDISCCoreFromSDTMVariable/{sdsVersion}/{domain}/{varName} :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external;
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: get the SDTM-IG version :)
(: TODO: for define.xml,get it at the dataset definition level :)
let $igversion := (
	if($defineversion = '2.1') then $definedoc//odm:MetaDataVersion/def21:Standards/def21:Standard[@Type='IG'][1]/@Version
	else $definedoc//odm:MetaDataVersion/@def:StandardVersion
)
(: iterate over all datasets and variables in the define.xml :)
for $itemgroup in $definedoc//odm:ItemGroupDef
	let $itemgroupdefname := $itemgroup/@Name
	let $domain := $itemgroup/@Name
    (: get the dataset name and location :)
    let $datasetname := (
		if($defineversion = '2.1') then $itemgroup/def21:leaf/@xlink:href
		else $itemgroup/def:leaf/@xlink:href
	)
    let $datasetdoc := (
		if($datasetname) then doc(concat($base,$datasetname))
		else ()
	)
    (: iterate over all ItemRef elements that hava Mandatory="No" :)
    for $itemref in $itemgroup/odm:ItemRef[@Mandatory='No']
        (: get the OID and the Name of the variable :)
        let $variableoid := $itemref/@ItemOID
        let $variablename := $definedoc//odm:ItemDef[@OID=$variableoid]/@Name
        (: invoke the webservice to find out whether the variable is permissible :)
        let $webservice := concat('http://www.xml4pharmaserver.com:8080/CDISCCTService/rest/CDISCCoreFromSDTMVariable/',$igversion,'/',$itemgroupdefname,'/',$variablename)
        let $webserviceresult := data(doc($webservice)/XML4PharmaServerWebServiceResponse/Response)  (: returns 'Req', 'Exp', or 'Perm' :)
        (: now take the dataset and count the number of data points for this variable :)
        let $count := count($datasetdoc//odm:ItemGroupData/odm:ItemData[@ItemOID=$variableoid])
        (: no data points found :)
        where $webserviceresult='Perm' and $count = 0
        (: and give out an error message :)
        return <warning rule="SD1078" rulelastupdate="2020-08-08" dataset="{data($variablename)}" variable="{data($itemgroupdefname)}">Permissible variable {data($variablename)} has no values for all records in dataset {data($datasetname)}</warning>	
]]></rulexquery>
</sdsrule>


<sdsrule id="SD1078-SD" last-update="2020-08-08" originator="FDA" standard="SDTM" webservice="Uses XML4Pharma RESTful Web Service" requiresDomainOrDataset="Yes">
<ruledescription>Permissible variable with missing value for all records is not allowed</ruledescription>
<ruledetaileddescription>Permissible variable whould not be present in domain, when the variable has missing value for all records in the dataset</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>ALL</domain>
<rulexquery><![CDATA[
(: Rule SD1078 - Permissible variable with missing value for all records:
Permissible variable whould not be present in domain, when the variable has missing value for all records in the dataset :)
(:  How do we know that a variable is permissible? Mandatory="No" is essentially not sufficient. 
Use the web service to find out - http://www.xml4pharmaserver.com:8080/CDISCCTService/rest/CDISCCoreFromSDTMVariable/{sdsVersion}/{domain}/{varName} :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external;
declare variable $defineversion external;
declare variable $datasetname external; 
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: get the SDTM-IG version :)
(: TODO: for define.xml,get it at the dataset definition level :)
let $igversion := (
	if($defineversion = '2.1') then $definedoc//odm:MetaDataVersion/def21:Standards/def21:Standard[@Type='IG'][1]/@Version
	else $definedoc//odm:MetaDataVersion/@def:StandardVersion
)
(: iterate over all datasets and variables in the define.xml :)
for $itemgroup in $definedoc//odm:ItemGroupDef[@Name=$datasetname]
	let $itemgroupdefname := $itemgroup/@Name
	let $domain := $itemgroup/@Domain
    (: get the dataset name and location :)
    let $dsname := (
		if($defineversion = '2.1') then $itemgroup/def21:leaf/@xlink:href
		else $itemgroup/def:leaf/@xlink:href
	)
    let $datasetdoc := (
		if($dsname) then doc(concat($base,$dsname))
		else ()
	)
    (: iterate over all ItemRef elements that hava Mandatory="No" :)
    for $itemref in $itemgroup/odm:ItemRef[@Mandatory='No']
        (: get the OID and the Name of the variable :)
        let $variableoid := $itemref/@ItemOID
        let $variablename := $definedoc//odm:ItemDef[@OID=$variableoid]/@Name
        (: invoke the webservice to find out whether the variable is permissible :)
        let $webservice := concat('http://www.xml4pharmaserver.com:8080/CDISCCTService/rest/CDISCCoreFromSDTMVariable/',$igversion,'/',$domain,'/',$variablename)
        let $webserviceresult := data(doc($webservice)/XML4PharmaServerWebServiceResponse/Response)  (: returns 'Req', 'Exp', or 'Perm' :)
        (: now take the dataset and count the number of data points for this variable :)
        let $count := count($datasetdoc//odm:ItemGroupData/odm:ItemData[@ItemOID=$variableoid])
        (: no data points found :)
        where $webserviceresult='Perm' and $count = 0
        (: and give out an error message :)
        return <warning rule="SD1078" rulelastupdate="2020-08-08" dataset="{data($variablename)}" variable="{data($itemgroupdefname)}">Permissible variable {data($variablename)} has no values for all records in dataset {data($datasetname)}</warning>	
]]></rulexquery>
</sdsrule>


<sdsrule id="SD0057" last-update="2020-08-08" originator="FDA" standard="SDTM" webservice="Uses XML4Pharma RESTful Web Service" timeintensive="Yes">
<ruledescription>SDTM Expected variables must be included in datasets</ruledescription>
<ruledetaileddescription>When no data has been collected for an expected variable, however, a null column must still be included in the dataset, and a comment must be included in the define.xml to state that data was not collected</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>ALL</domain>
<rulexquery><![CDATA[	
(: Rule SD0057: SDTM Expected variable not found :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
declare namespace functx = "http://www.functx.com";
(: function to find out whether a value is in a sequence (array) :)
declare function functx:is-value-in-sequence
  ( $value as xs:anyAtomicType? ,
    $seq as xs:anyAtomicType* )  as xs:boolean {
   $value = $seq
} ;
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' 
let $define := 'define2-0-0-example-sdtm.xml' :)
(: The base of the RESTful web service to get whether a variable is "required", "expected" or "permissible" :)
let $webservicebase := 'http://www.xml4pharmaserver.com:8080/CDISCCTService/rest/CDISCCoreFromSDTMVariable/'
(: get the define document :)
let $definedoc := doc(concat($base,$define))
(: get the SDTM-IG version :)
(: TODO: for define.xml,get it at the dataset definition level :)
let $igversion := (
	if($defineversion = '2.1') then $definedoc//odm:MetaDataVersion/def21:Standards/def21:Standard[@Type='IG'][1]/@Version
	else $definedoc//odm:MetaDataVersion/@def:StandardVersion
)
(: iterate over all dataset definitions in the define.xml :)
for $datasetdef in $definedoc//odm:ItemGroupDef
    (: get the name of the dataset, the domain and the dataset location :)
    let $name := $datasetdef/@Name
    let $datasetlocation := (
		if($defineversion = '2.1') then $datasetdef/def21:leaf/@xlink:href
		else $datasetdef/def:leaf/@xlink:href
	)
    let $domain := $datasetdef/@Domain
    (: create a list of all "expected" variables in the dataset
    This uses an XML4Pharma RESTful web service.
    TODO: use the CDISC Library API :)
    let $expectedvaroids := (
        for $varoid in $datasetdef/odm:ItemRef/@ItemOID
            let $varname := $definedoc//odm:ItemDef[@OID=$varoid]/@Name  (: the variable name :)
            (: get the "core" ("required", "expected" or "permissible" from the RESTful web service :)
            let $query := concat($webservicebase,$igversion,'/',$domain,'/',$varname)
            let $core := doc($query)//Response/text()
            where $core = 'Exp'
            return $varoid (: we return the OID, as this will be more direct for checking the presence :)
    )
    (: return <test>{data($expectedvaroids)}</test> :)
    (: check whether each of the expected variables is at least defined in the define.xml - 
    which does mean that there is a column for it, empty or not :)
    for $oid in $expectedvaroids
        let $varname := $definedoc//odm:ItemDef[@OID=$oid]/@Name  (: needed for reporting :)
        let $expectedpresent := $datasetdef/odm:ItemRef[@ItemOID=$oid]  (: a boolean :)
        where not($expectedpresent)  (: expected variable is not present :)
        return <warning rule="SD1015" dataset="{data($name)}" variable="{$varname}" rulelastupdate="2020-08-08">Expected variable {data($varname)} was not found in dataset {data($name)} for domain {data($domain)}</warning>		
]]>
</rulexquery>
</sdsrule>

<sdsrule id="SD0057-SD" last-update="2020-08-08" originator="FDA" standard="SDTM" requiresDomainOrDataset="Yes" webservice="Uses XML4Pharma RESTful Web Service">
<ruledescription>SDTM Expected variables must be included in datasets</ruledescription>
<ruledetaileddescription>When no data has been collected for an expected variable, however, a null column must still be included in the dataset, and a comment must be included in the define.xml to state that data was not collected</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>ALL</domain>
<rulexquery><![CDATA[	
(: Rule SD0057: SDTM Expected variable not found :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
declare namespace functx = "http://www.functx.com";
(: function to find out whether a value is in a sequence (array) :)
declare function functx:is-value-in-sequence
  ( $value as xs:anyAtomicType? ,
    $seq as xs:anyAtomicType* )  as xs:boolean {
   $value = $seq
} ;
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
declare variable $datasetname external;
(: let $base := '/db/fda_submissions/cdisc01/' 
let $define := 'define2-0-0-example-sdtm.xml' :)
(: The base of the RESTful web service to get whether a variable is "required", "expected" or "permissible" :)
let $webservicebase := 'http://www.xml4pharmaserver.com:8080/CDISCCTService/rest/CDISCCoreFromSDTMVariable/'
(: get the define document :)
let $definedoc := doc(concat($base,$define))
(: get the SDTM-IG version :)
(: TODO: for v.2.1 get standard version at the dataset definition level :)
let $igversion := (
	if($defineversion = '2.1') then $definedoc//odm:MetaDataVersion/def21:Standards/def21:Standard[@Type='IG'][1]/@Version
	else $definedoc//odm:MetaDataVersion/@def:StandardVersion
)
(: iterate over all the provided dataset definitions in the define.xml :)
for $datasetdef in $definedoc//odm:ItemGroupDef[@Name=$datasetname]
    (: get the name of the dataset, the domain and the dataset location :)
    let $name := $datasetdef/@Name
    let $datasetlocation := (
		if($defineversion = '2.1') then $datasetdef/def21:leaf/@xlink:href
		else $datasetdef/def:leaf/@xlink:href
	)
    let $domain := $datasetdef/@Domain
    (: create a list of all "expected" variables in the dataset
    This uses an XML4Pharma RESTful web service.
    TODO: use the CDISC Library API :)
    let $expectedvaroids := (
        for $varoid in $datasetdef/odm:ItemRef/@ItemOID
            let $varname := $definedoc//odm:ItemDef[@OID=$varoid]/@Name  (: the variable name :)
            (: get the "core" ("required", "expected" or "permissible" from the RESTful web service :)
            let $query := concat($webservicebase,$igversion,'/',$domain,'/',$varname)
            let $core := doc($query)//Response/text()
            where $core = 'Exp'
            return $varoid (: we return the OID, as this will be more direct for checking the presence :)
    )
    (: return <test>{data($expectedvaroids)}</test> :)
    (: check whether each of the expected variables is at least defined in the define.xml - 
    which does mean that there is a column for it, empty or not :)
    for $oid in $expectedvaroids
        let $varname := $definedoc//odm:ItemDef[@OID=$oid]/@Name  (: needed for reporting :)
        let $expectedpresent := $datasetdef/odm:ItemRef[@ItemOID=$oid]  (: a boolean :)
        where not($expectedpresent)  (: expected variable is not present :)
        return <warning rule="SD1015" dataset="{data($name)}" variable="{$varname}" rulelastupdate="2020-08-08">Expected variable {data($varname)} was not found in dataset {data($name)} for domain {data($domain)}</warning>		
]]>
</rulexquery>
</sdsrule>


<!-- Rule SD1095 applies to INTERVENTIONS, FINDINGS, EVENTS -->
<sdsrule id="SD1095" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>Split datasets must not have name of original domain</ruledescription>
<ruledetaileddescription>Split datasets must not have name of original domain. E.g., lbhm.xpt is a valid name, when lb.xpt is not a valid dataset name for split domain.</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>INTERVENTIONS</domain>
<domain>FINDINGS</domain>
<domain>EVENTS</domain>
<rulexquery><![CDATA[
(: Rule SD1095 - Split datasets must not have name of original domain. 
E.g., lbhm.xpt is a valid name, when lb.xpt is not a valid dataset name for split domain.
:)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
declare namespace xs="http://www.w3.org/2001/XMLSchema";
declare namespace functx = "http://www.functx.com";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external;
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: iterate over all datasets in the define.xml that have a 'Domain' attribute (this should be all of them)
and within INTERVENTIONS, FINDINGS, EVENTS :)
for $itemgroup in $definedoc//odm:ItemGroupDef[@Domain][upper-case(@def:Class)='INTERVENTIONS' or upper-case(@def:Class)='FINDINGS' or upper-case(@def:Class)='EVENTS' 
		or upper-case(./def21:Class/@Name)='INTERVENTIONS' or upper-case(./def21:Class/@Name)='FINDINGS' or upper-case(./def21:Class/@Name)='EVENTS']
    (: get the domain name - value of the 'Domain' attribute :)
    let $domain := $itemgroup/@Domain
    (: and the dataset name (@Name attribute) :)
    let $name := $itemgroup/@Name
    (: and get the filename :)
    let $filename := (
		if($defineversion = '2.1') then $itemgroup/def21:leaf/@xlink:href
		else $itemgroup/def:leaf/@xlink:href
	)
    (: get the position of the current ItemGroupDef in the series :)
    let $pos := $itemgroup/position()
    (: get all the ItemGroupDefs with the same value for the 'Domain' attribute
    If so, this means that the dataset is a 'splitted' dataset. 
    P.S. There currently is no other way to find out :)
    let $count := count($definedoc//odm:ItemGroupDef[@Domain=$domain])
    (: in case there is more than one such dataset, the prefix (part before the dot)
    MUST have MORE than two characters (e.g. qscs.xpt) :)
    where $count > 1 and not(string-length(substring-before($filename,'.')) > 2)
        (: filenames should have >2 characters before the dot :)
        return <error rule="SD1095" dataset="{data($name)}" rulelastupdate="2020-08-08">Split dataset {data($name)} has a file name '{data($filename)}' that is typical for non-split datasets</error>
]]></rulexquery>
</sdsrule>

<sdsrule id="SD1128" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>RELTYPE may oly be 'ONE' or 'MANY'</ruledescription>
<ruledetaileddescription>Relationship Type (RELTYPE)  variable values should be populated with terms 'ONE', 'MANY', when Related Records (RELREC) dataset is used to identify relationships between datasets</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>RELREC</domain>
<rulexquery><![CDATA[	
(: Rule SD1128 - Invalid value for RELTYPE variable:
Relationship Type (RELTYPE)  variable values should be populated with terms 'ONE', 'MANY', when Related Records (RELREC) dataset is used to identify relationships between datasets.
TODO: how can we see that the RELREC dataset is used to identify relationships between datasets?
The only thing we do here is check whether RELTYPE is either empty (but then the data point should be absent when using Dataset-XML
or either has the value "ONE" or "MANY"
:)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external;
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdiscpilot01/' :)
(: let $define := 'define_2_0.xml' :)
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: iterate over all RELREC datasets in the define.xml - 
 usually there will be only one :)
for $itemgroup in $definedoc//odm:ItemGroupDef[@Name='RELREC']
    (: get the dataset name and location :)
    let $datasetname := (
		if($defineversion = '2.1') then $itemgroup/def21:leaf/@xlink:href
		else $itemgroup/def:leaf/@xlink:href
	)
    let $datasetdoc := (
		if($datasetname) then doc(concat($base,$datasetname))
		else ()
	)
    (: get the OID of the "RELTYPE" variable :)
    let $reltypeoid := (
    for $a in $definedoc//odm:ItemDef[@Name='RELTYPE']/@OID 
    where $a = $itemgroup/odm:ItemRef/@ItemOID 
    return $a 
    )
    (: now iterate over all RELREC records for which there IS a RELTYPE datapoint :)
    for $record in $datasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$reltypeoid]]
        (: get the record number and the value for RELTYPE :)
        let $recnum := $record/@data:ItemGroupDataSeq
        let $value := $record/odm:ItemData[@ItemOID=$reltypeoid]/@Value
        (: the value may only be 'ONE' or 'MANY' - if null, the data point should not exist in Dataset-XML  :)
        where not($value='ONE') and not($value='MANY')
        return <error rule="SD1128" dataset="RELREC" variable="RELTYPE" recordnumber="{data($recnum)}" rulelastupdate="2020-08-08">Invalid value for RELTYPE variable: only 'ONE' or 'MANY' is allowed. The value '{data($value)}'was found</error>
]]></rulexquery>
</sdsrule>

<!-- TODO? Rule CT2001/CT2002: "Variable value not found in non-extensible codelist" -->

<sdsrule id="CT2003" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>Coded and Decoded values must have the same NCI Code in CDISC CT</ruledescription>
<ruledetaileddescription>Paired variables TEST/TESTCD must be populated using terms with the same Codelist Code value in CDISC control terminology. There is one-to-one relationship between paired variable values defined in CDISC control terminology by Codelist Code value</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>ALL</domain>
<rulexquery><![CDATA[
(: Rule  : Coded and Decoded values do not have the same Code in CDISC CT - 
Paired variables such as TEST/TESTCD must be populated using terms with the same Codelist Code value in CDISC control terminology. There is one-to-one relationship between paired variable values defined in CDISC control terminology by Codelist Code value :)
(: The rule will only be implemented on TESTCD-TEST pairs :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' 
let $define := 'define2-0-0-example-sdtm.xml' :)
(: get the define.xml document :)
let $definedoc := doc(concat($base,$define))
(: iterate over the dataset definitions :)
for $datasetdef in $definedoc//odm:ItemGroupDef
    (: get the name of the dataset :)
    let $name := $datasetdef/@Name
    (: get the OIDs of the --TESTCD and --TEST variables :)
    let $testcdoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'TESTCD')]/@OID 
            where $a = $datasetdef/odm:ItemRef/@ItemOID
        return $a
    )
    let $testcdname := $definedoc//odm:ItemDef[@OID=$testcdoid]/@Name
    let $testoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'TEST')]/@OID 
            where $a = $datasetdef/odm:ItemRef/@ItemOID
        return $a
    )
    let $testname := $definedoc//odm:ItemDef[@OID=$testoid]/@Name
    (: get the associated codelist :)
    let $testcdcodelistoid := $definedoc//odm:ItemDef[@OID=$testcdoid]/odm:CodeListRef/@CodeListOID
    let $testcodelistoid := $definedoc//odm:ItemDef[@OID=$testoid]/odm:CodeListRef/@CodeListOID
    (: and the codelist itself :)
    let $testcdcodelist := $definedoc//odm:CodeList[@OID=$testcdcodelistoid]  (: this is an XML structure :)
    let $testcodelist := $definedoc//odm:CodeList[@OID=$testcodelistoid]  (: this is an XML structure :)
    (: get the location of the dataset :)
    let $datasetlocation := (
		if($defineversion = '2.1') then $datasetdef/def21:leaf/@xlink:href
		else $datasetdef/def:leaf/@xlink:href
	)
    (: and the dataset document itself :)
    let $datasetdoc := (
		if($datasetlocation) then doc(concat($base,$datasetlocation))
		else ()
	)
    (: iterate over all the records in the dataset, at least when TESTCD and TEST have been defined for the dataset :)
    for $record in $datasetdoc[$testcdoid and $testoid]//odm:ItemGroupData
        let $recnum := $record/@data:ItemGroupDataSeq
        (: get the values of --TESTCD and of --TEST :)
        let $testcdvalue := $record/odm:ItemData[@ItemOID=$testcdoid]/@Value
        let $testvalue := $record/odm:ItemData[@ItemOID=$testoid]/@Value
        (: get the NCI code of the --TESTCD value if there is one.
        the case there is one usually corresponds with an extension to the codelist :)
        (: ATTENTION: case sensitive! :)
        let $testcdncivalue := $testcdcodelist/odm:*[@CodedValue=$testcdvalue]/odm:Alias[@Context='nci:ExtCodeID']/@Name
        let $testncivalue := $testcodelist/odm:*[@CodedValue=$testvalue]/odm:Alias[@Context='nci:ExtCodeID']/@Name
        (: return <test>{data($testcdncivalue)} - {data($testncivalue)}</test> :)
        (: if there is an NCI value for the TESTCD, then the NCI value for the TEST must be identical :)
        where $testcdncivalue and not($testcdncivalue=$testncivalue) 
        return <error rule="SD1015" dataset="{data($name)}" variable="{$testcdname}" recordnumber="{$recnum}" rulelastupdate="2020-08-08">The Coded value  '{data($testcdvalue)}' with NCI code '{data($testcdncivalue)}' and Decode value '{data($testvalue)}' with NCI code '{data($testcdncivalue)}' do not have the same NCI code</error>		
]]>
</rulexquery>
</sdsrule>

<sdsrule id="CT2003-SD" last-update="2020-08-08" originator="FDA" standard="SDTM" requiresDomainOrDataset="Yes">
<ruledescription>Coded and Decoded values must have the same NCI Code in CDISC CT</ruledescription>
<ruledetaileddescription>Paired variables TEST/TESTCD must be populated using terms with the same Codelist Code value in CDISC control terminology. There is one-to-one relationship between paired variable values defined in CDISC control terminology by Codelist Code value</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>ALL</domain>
<rulexquery><![CDATA[
(: Rule  : Coded and Decoded values do not have the same Code in CDISC CT - 
Paired variables such as TEST/TESTCD must be populated using terms with the same Codelist Code value in CDISC control terminology. There is one-to-one relationship between paired variable values defined in CDISC control terminology by Codelist Code value :)
(: The rule will only be implemented on TESTCD-TEST pairs :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
declare variable $datasetname external;
(: let $base := '/db/fda_submissions/cdisc01/' 
let $define := 'define2-0-0-example-sdtm.xml' :)
(: get the define.xml document :)
let $definedoc := doc(concat($base,$define))
(: iterate over the dataset definitions :)
for $datasetdef in $definedoc//odm:ItemGroupDef[@Name=$datasetname]
    (: get the name of the dataset :)
    let $name := $datasetdef/@Name
    (: get the OIDs of the --TESTCD and --TEST variables :)
    let $testcdoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'TESTCD')]/@OID 
            where $a = $datasetdef/odm:ItemRef/@ItemOID
        return $a
    )
    let $testcdname := $definedoc//odm:ItemDef[@OID=$testcdoid]/@Name
    let $testoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'TEST')]/@OID 
            where $a = $datasetdef/odm:ItemRef/@ItemOID
        return $a
    )
    let $testname := $definedoc//odm:ItemDef[@OID=$testoid]/@Name
    (: get the associated codelist :)
    let $testcdcodelistoid := $definedoc//odm:ItemDef[@OID=$testcdoid]/odm:CodeListRef/@CodeListOID
    let $testcodelistoid := $definedoc//odm:ItemDef[@OID=$testoid]/odm:CodeListRef/@CodeListOID
    (: and the codelist itself :)
    let $testcdcodelist := $definedoc//odm:CodeList[@OID=$testcdcodelistoid]  (: this is an XML structure :)
    let $testcodelist := $definedoc//odm:CodeList[@OID=$testcodelistoid]  (: this is an XML structure :)
    (: get the location of the dataset :)
    let $datasetlocation := (
		if($defineversion = '2.1') then $datasetdef/def21:leaf/@xlink:href
		else $datasetdef/def:leaf/@xlink:href
	)
    (: and the dataset document itself :)
    let $datasetdoc := (
		if($datasetlocation) then doc(concat($base,$datasetlocation))
		else ()
	)
    (: iterate over all the records in the dataset, at least when TESTCD and TEST have been defined for the dataset :)
    for $record in $datasetdoc[$testcdoid and $testoid]//odm:ItemGroupData
        let $recnum := $record/@data:ItemGroupDataSeq
        (: get the values of --TESTCD and of --TEST :)
        let $testcdvalue := $record/odm:ItemData[@ItemOID=$testcdoid]/@Value
        let $testvalue := $record/odm:ItemData[@ItemOID=$testoid]/@Value
        (: get the NCI code of the --TESTCD value if there is one.
        the case there is one usually corresponds with an extension to the codelist :)
        (: ATTENTION: case sensitive! :)
        let $testcdncivalue := $testcdcodelist/odm:*[@CodedValue=$testcdvalue]/odm:Alias[@Context='nci:ExtCodeID']/@Name
        let $testncivalue := $testcodelist/odm:*[@CodedValue=$testvalue]/odm:Alias[@Context='nci:ExtCodeID']/@Name
        (: return <test>{data($testcdncivalue)} - {data($testncivalue)}</test> :)
        (: if there is an NCI value for the TESTCD, then the NCI value for the TEST must be identical :)
        where $testcdncivalue and not($testcdncivalue=$testncivalue) 
        return <error rule="SD1015" dataset="{data($name)}" variable="{$testcdname}" recordnumber="{$recnum}" rulelastupdate="2020-08-08">The Coded value  '{data($testcdvalue)}' with NCI code '{data($testcdncivalue)}' and Decode value '{data($testvalue)}' with NCI code '{data($testcdncivalue)}' do not have the same NCI code</error>		
]]>
</rulexquery>
</sdsrule>

<!-- Rule CT CT2004: Variable value not found in non-extensible codelist when value-level condition occurs.
This rule is very similar to rule SD1228: Variable value not found in user-defined codelist when value-level condition occurs.
NOT implemented until it is clear what the difference is -->

<!-- Rule CT CT2005: Variable value not found in non-extensible codelist when value-level condition occurs.
This rule is very similar to rule SD1228: Variable value not found in user-defined codelist when value-level condition occurs.
NOT implemented until it is clear what the difference is -->

<!-- Rule CT2006: Is identical to rule CT2003 -->

<!-- Rule SD1238 needs further testing. A good example was not available -->
<sdsrule id="SD1238" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>--RFTDTC may not be populated when --TPTREF is not populated</ruledescription>
<ruledetaileddescription>--RFTDTC may not be populated when --TPTREF is not populated</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>ALL</domain>
<rulexquery><![CDATA[
(: Rule : SD1238: --RFTDTC is populated when --TPTREF is not populated - 
 Applies to all datasets :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' 
let $define := 'define2-0-0-example-sdtm.xml' :)
(: get the define.xml document :)
let $definedoc := doc(concat($base,$define))
(: iterate over the dataset definitions :)
for $datasetdef in $definedoc//odm:ItemGroupDef
    (: get the name of the dataset :)
    let $name := $datasetdef/@Name
    (: get the OIDs of the --RFTDTC and --TPTREF variables :)
    let $rftdtcoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'RFTDTC')]/@OID 
            where $a = $datasetdef/odm:ItemRef/@ItemOID
        return $a
    )
    let $rftdtcdname := $definedoc//odm:ItemDef[@OID=$rftdtcoid]/@Name
    let $tptrefoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'TPTREF')]/@OID 
            where $a = $datasetdef/odm:ItemRef/@ItemOID
        return $a
    )
    let $tptrefname := $definedoc//odm:ItemDef[@OID=$tptrefoid]/@Name
    (: get the location of the dataset :)
    let $datasetlocation := (
		if($defineversion = '2.1') then $datasetdef/def21:leaf/@xlink:href
		else $datasetdef/def:leaf/@xlink:href
	)
    (: and the dataset document itself :)
    let $datasetdoc := (
		if($datasetlocation) then doc(concat($base,$datasetlocation))
		else ()
	)
    (: iterate over all the records in the dataset, at least when TESTCD and TEST have been defined for the dataset :)
    for $record in $datasetdoc[$rftdtcoid and $tptrefoid]//odm:ItemGroupData
        let $recnum := $record/@data:ItemGroupDataSeq
        (: get the values of --RFTDTC and of --TPTREF :)
        let $tptrefvalue := $record/odm:ItemData[@ItemOID=$tptrefoid]/@Value
        let $rftdtcvalue := $record/odm:ItemData[@ItemOID=$rftdtcoid]/@Value
        (: when RFTDTC is populated, also TPTREF must be populated :)
        where $rftdtcvalue != '' and not($tptrefvalue) 
        return <error rule="SD1238" dataset="{data($name)}" variable="{$tptrefname}" rulelastupdate="2020-08-08">{data($rftdtcdname)} is populated but {$tptrefname} is not populated</error>		
]]>
</rulexquery>
</sdsrule>

<!-- Rule SD1238 needs further testing. A good example was not available -->
<sdsrule id="SD1238-SD" last-update="2020-08-08" originator="FDA" standard="SDTM" requiresDomainOrDataset="Yes">
<ruledescription>--RFTDTC may not be populated when --TPTREF is not populated</ruledescription>
<ruledetaileddescription>--RFTDTC may not be populated when --TPTREF is not populated</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>ALL</domain>
<rulexquery><![CDATA[
(: Rule : SD1238: --RFTDTC is populated when --TPTREF is not populated - 
 Applies to all datasets :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
declare variable $datasetname external;
(: let $base := '/db/fda_submissions/cdisc01/' 
let $define := 'define2-0-0-example-sdtm.xml' :)
(: get the define.xml document :)
let $definedoc := doc(concat($base,$define))
(: iterate over the dataset provided definitions :)
for $datasetdef in $definedoc//odm:ItemGroupDef[@Name=$datasetname]
    (: get the name of the dataset :)
    let $name := $datasetdef/@Name
    (: get the OIDs of the --RFTDTC and --TPTREF variables :)
    let $rftdtcoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'RFTDTC')]/@OID 
            where $a = $datasetdef/odm:ItemRef/@ItemOID
        return $a
    )
    let $rftdtcdname := $definedoc//odm:ItemDef[@OID=$rftdtcoid]/@Name
    let $tptrefoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'TPTREF')]/@OID 
            where $a = $datasetdef/odm:ItemRef/@ItemOID
        return $a
    )
    let $tptrefname := $definedoc//odm:ItemDef[@OID=$tptrefoid]/@Name
    (: get the location of the dataset :)
	let $datasetlocation := (
		if($defineversion = '2.1') then $datasetdef/def21:leaf/@xlink:href
		else $datasetdef/def:leaf/@xlink:href
	)
    (: and the dataset document itself :)
    let $datasetdoc := (
		if($datasetlocation) then doc(concat($base,$datasetlocation))
		else ()
	)
    (: iterate over all the records in the dataset, at least when TESTCD and TEST have been defined for the dataset :)
    for $record in $datasetdoc[$rftdtcoid and $tptrefoid]//odm:ItemGroupData
        let $recnum := $record/@data:ItemGroupDataSeq
        (: get the values of --RFTDTC and of --TPTREF :)
        let $tptrefvalue := $record/odm:ItemData[@ItemOID=$tptrefoid]/@Value
        let $rftdtcvalue := $record/odm:ItemData[@ItemOID=$rftdtcoid]/@Value
        (: when RFTDTC is populated, also TPTREF must be populated :)
        where $rftdtcvalue != '' and not($tptrefvalue) 
        return <error rule="SD1238" dataset="{data($name)}" variable="{$tptrefname}" rulelastupdate="2020-08-08">{data($rftdtcdname)} is populated but {$tptrefname} is not populated</error>		
]]>
</rulexquery>
</sdsrule>

<sdsrule id="SD1041" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>Values for --CAT and --SCAT may not be identical</ruledescription>
<ruledetaileddescription>Values of Category (--CAT) and Subcategory (--SCAT) variables should not be identical</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>ALL</domain>
<rulexquery><![CDATA[
(: Rule : SD1041: Values for --CAT and --SCAT are identical - 
 Applies to INTERVENTIONS, EVENTS, FINDINGS, TI :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' 
let $define := 'define2-0-0-example-sdtm.xml' :)
(: get the define.xml document :)
let $definedoc := doc(concat($base,$define))
(: iterate over the dataset definitions in INTERVENTIONS, EVENTS, FINDINGS, TI :)
for $datasetdef in $definedoc//odm:ItemGroupDef[upper-case(@def:Class)='INTERVENTIONS' or upper-case(@def:Class)='EVENTS' or upper-case(@def:Class)='FINDINGS' 
		or upper-case(./def21:Class/@Name)='INTERVENTIONS' or upper-case(./def21:Class/@Name)='EVENTS' or upper-case(./def21:Class/@Name)='FINDINGS'
		or @Domain='TI']
    (: get the name of the dataset :)
    let $name := $datasetdef/@Name
    (: get the OIDs of the --CAT and --SCAT variables :)
    let $catoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'CAT') and string-length(@Name)=5]/@OID 
            where $a = $datasetdef/odm:ItemRef/@ItemOID
        return $a
    )
    let $catname := $definedoc//odm:ItemDef[@OID=$catoid]/@Name
    let $scatoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'SCAT') and string-length(@Name)=6]/@OID
            where $a = $datasetdef/odm:ItemRef/@ItemOID
        return $a
    )
    let $scatname := $definedoc//odm:ItemDef[@OID=$scatoid]/@Name
    (: get the location of the dataset :)
	let $datasetlocation := (
		if($defineversion = '2.1') then $datasetdef/def21:leaf/@xlink:href
		else $datasetdef/def:leaf/@xlink:href
	)
    (: and the dataset document itself :)
    let $datasetdoc := (
		if($datasetlocation) then doc(concat($base,$datasetlocation))
		else ()
	)
    (: iterate over all the records in the dataset, at least when --CAT and --SCAT have been defined for the dataset :)
    for $record in $datasetdoc[$catoid and $scatoid]//odm:ItemGroupData
        let $recnum := $record/@data:ItemGroupDataSeq
        (: get the values of --CAT and of --SCAT :)
        let $catvalue := $record/odm:ItemData[@ItemOID=$catoid]/@Value
        let $scatvalue := $record/odm:ItemData[@ItemOID=$scatoid]/@Value
        (: SCAT and CAT may not be identical :)
        where $catvalue = $scatvalue
        return <error rule="SD1041" dataset="{data($name)}" variable="{$scatname}" rulelastupdate="2020-08-08">{data($scatname)} value '{data($catvalue)}' is identical to {data($catname)} value '{data($scatvalue)}'</error>		
]]>
</rulexquery>
</sdsrule>

<sdsrule id="SD1041-SD" last-update="2020-08-08" originator="FDA" standard="SDTM" requiresDomainOrDataset="Yes">
<ruledescription>Values for --CAT and --SCAT may not be identical</ruledescription>
<ruledetaileddescription>Values of Category (--CAT) and Subcategory (--SCAT) variables should not be identical</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>ALL</domain>
<rulexquery><![CDATA[
(: Rule : SD1041: Values for --CAT and --SCAT are identical - 
 Applies to INTERVENTIONS, EVENTS, FINDINGS, TI :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
declare variable $datasetname external;
(: let $base := '/db/fda_submissions/cdisc01/' 
let $define := 'define2-0-0-example-sdtm.xml' :)
(: get the define.xml document :)
let $definedoc := doc(concat($base,$define))
(: iterate over the dataset definitions in INTERVENTIONS, EVENTS, FINDINGS, TI :)
for $datasetdef in $definedoc//odm:ItemGroupDef[@Name=$datasetname][upper-case(@def:Class)='INTERVENTIONS' or upper-case(@def:Class)='EVENTS' or upper-case(@def:Class)='FINDINGS' 
		or upper-case(./def21:Class/@Name)='INTERVENTIONS' or upper-case(./def21:Class/@Name)='EVENTS' or upper-case(./def21:Class/@Name)='FINDINGS'
		or @Domain='TI']
    (: get the name of the dataset :)
    let $name := $datasetdef/@Name
    (: get the OIDs of the --CAT and --SCAT variables :)
    let $catoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'CAT') and string-length(@Name)=5]/@OID 
            where $a = $datasetdef/odm:ItemRef/@ItemOID
        return $a
    )
    let $catname := $definedoc//odm:ItemDef[@OID=$catoid]/@Name
    let $scatoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'SCAT') and string-length(@Name)=6]/@OID 
            where $a = $datasetdef/odm:ItemRef/@ItemOID
        return $a
    )
    let $scatname := $definedoc//odm:ItemDef[@OID=$scatoid]/@Name
    (: get the location of the dataset :)
	let $datasetlocation := (
		if($defineversion = '2.1') then $datasetdef/def21:leaf/@xlink:href
		else $datasetdef/def:leaf/@xlink:href
	)
    (: and the dataset document itself :)
    let $datasetdoc := (
		if($datasetlocation) then doc(concat($base,$datasetlocation))
		else ()
	)
    (: iterate over all the records in the dataset, at least when --CAT and --SCAT have been defined for the dataset :)
    for $record in $datasetdoc[$catoid and $scatoid]//odm:ItemGroupData
        let $recnum := $record/@data:ItemGroupDataSeq
        (: get the values of --CAT and of --SCAT :)
        let $catvalue := $record/odm:ItemData[@ItemOID=$catoid]/@Value
        let $scatvalue := $record/odm:ItemData[@ItemOID=$scatoid]/@Value
        (: SCAT and CAT may not be identical :)
        where $catvalue = $scatvalue
        return <error rule="SD1041" dataset="{data($name)}" variable="{$scatname}" rulelastupdate="2020-08-08">{data($scatname)} value '{data($catvalue)}' is identical to {data($catname)} value '{data($scatvalue)}'</error>		
]]>
</rulexquery>
</sdsrule>

<!-- REPLACED 2020-08-08: new algorithm uses XML4Pharma RESTful web services
<sdsrule id="SD0002" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>NULL value in variable marked as Required - Required variables (where Core attribute is 'Req') cannot be NULL for any records</ruledescription>
<domain>ALL</domain>
<rulexquery><![CDATA[
(: Rule SD0002: NULL value in variable marked as Required - Required variables (where Core attribute is 'Req') cannot be NULL for any records :)
(: The following Query relies on that the define.xml is complete and 
 that Mandatory='Yes' is set for each required variable :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external;
declare variable $defineversion external;
declare variable $domain external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
(: let $domain := 'VS'  :)
(: EITHER provide $domain=:'ALL', meaning: validate for all domains referenced from the define.xml OR:
$domain:='XX' where XX is a specific domain, MEANING validate for a single domain only :)
(:  get the definitions for the domains (ItemGroupDefs in define.xml) :)
let $domains := (
    if($domain != 'ALL') then doc(concat($base,$define))//odm:ItemGroupDef[@Domain=$domain]
    else doc(concat($base,$define))//odm:ItemGroupDef
)
(: iterate over all datasets mentioned in the define.xml :)
for $itemgroup in $domains
    let $itemgroupoid := $itemgroup/@OID
    let $dataset := $itemgroup/def:leaf/@xlink:href
    let $datasetname := $itemgroup/@Name
    let $datasetpath := concat($base,$dataset)
    (: iterate over all ItemRefs that are mandatory, corresponding to 'required' variables :)
    for $itemref in $itemgroup/odm:ItemRef[@Mandatory='Yes']
        let $itemrefoid := $itemref/@ItemOID
        let $itemname := $itemref/../../odm:ItemDef[@OID=$itemrefoid]/@Name
        (: iterate over each dataset :)
        for $record in doc($datasetpath)//odm:ItemGroupData[@ItemGroupOID=$itemgroupoid]
            (: get the record number :)
            let $recnum := $record/@data:ItemGroupDataSeq
            (: data point is absent in this record :)
            where empty($record/odm:ItemData[@ItemOID=$itemrefoid])
            return <error rule="SD0002" dataset="{$datasetname}" variable="{data($itemname)" rulelastupdate="2020-08-08" recordnumber="{$recnum}">No data found for required variable {data($itemname)} in record number {data($recnum)} in dataset {data($datasetname)}</error>
]]></rulexquery>
</sdsrule>
-->

<!-- replacement 2020-08-08, using XML4Pharma RESTful Web Services 
TODO: use CDISC Library API -->
<sdsrule id="SD0002-SD" last-update="2020-08-08" originator="FDA" standard="SDTM" requiresDomainOrDataset="Yes" webservice="Uses XML4Pharma RESTful Web Service">
<ruledescription>Variables marked as Required may not have a null value</ruledescription>
<ruledetaileddescription>Required variables (where Core attribute is 'Req') cannot be NULL for any records</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>ALL</domain>
<rulexquery><![CDATA[
(: Rule SD0002: NULL value in variable marked as Required - Required variables (where Core attribute is 'Req') cannot be NULL for any records :)
(: Until 2020-08-08, the implementation relies on that the define.xml is complete and 
 that Mandatory='Yes' is set for each required variable :)
(: As of 2020-08-08 it uses a RESTful web service to find out what the "core" status of the variable is
 TODO: use CDISC Library API for this :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external;
declare variable $defineversion external;
declare variable $datasetname external; 
(: let $base := '/db/fda_submissions/cdisc01/' 
let $define := 'define2-0-0-example-sdtm.xml' 
let $datasetname := 'LB' :)
let $definedoc := doc(concat($base,$define))
(: The base of the RESTful web service :)
let $webservicebase := 'http://www.xml4pharmaserver.com:8080/CDISCCTService/rest/CDISCCoreFromSDTMVariable/'
(: The SDTM-IG version this define.xml refers to :)
(: TODO: for v.2.1 get standard version at the dataset definition level :)
let $igversion := (
	if($defineversion = '2.1') then $definedoc //odm:MetaDataVersion/def21:Standards/def21:Standard[@Type='IG'][1]/@Version
	else $definedoc //odm:MetaDataVersion/@def:StandardVersion
)
(:  get the definitions for the provided datasets (ItemGroupDefs in define.xml) :)
for $itemgroup in $definedoc //odm:ItemGroupDef[@Name=$datasetname]
    let $itemgroupoid := $itemgroup/@OID
    let $dataset := (
		if($defineversion = '2.1') then $itemgroup/def21:leaf/@xlink:href
		else $itemgroup/def:leaf/@xlink:href
	)
    let $dsname := $itemgroup/@Name
    let $datasetdoc := (
		if($dataset) then doc(concat($base,$dataset))
		else ()
	)
    let $domain := $itemgroup/@Domain  (: The domain this dataset belongs to :)
    (: iterate over all ItemRefs that are mandatory, corresponding to 'required' variables :)
    for $itemref in $itemgroup/odm:ItemRef
        let $itemrefoid := $itemref/@ItemOID
        let $itemname := $definedoc //odm:ItemDef[@OID=$itemrefoid]/@Name
        (: get the "core" status from the RESTful web service :)
        let $query := concat($webservicebase,$igversion,'/',$domain,'/',$itemname)
        (: return <test>{data($query)}</test> :)
        (: Execute the RESTful web service query and get the value for "core" :)
        let $core := doc($query)//Response/text() 
        (: iterate over each dataset record :)
        for $record in $datasetdoc//odm:ItemGroupData[@ItemGroupOID=$itemgroupoid] 
            (: get the record number :)
            let $recnum := $record/@data:ItemGroupDataSeq 
            (: data point is absent in this record, althout "core" is "Req" :)
            where $core='Req' and empty($record/odm:ItemData[@ItemOID=$itemrefoid])
            return <error rule="SD0002" dataset="{$datasetname}" variable="{data($itemname)}" rulelastupdate="2020-08-08" recordnumber="{$recnum}">No data found for required variable {data($itemname)} in record number {data($recnum)} in dataset {data($datasetname)}</error> 
]]>
</rulexquery>
</sdsrule>

<sdsrule id="SD0005" last-update="2020-08-08" originator="FDA" standard="SDTM" timeintensive="Yes">
<ruledescription>Values for --SEQ variable may not be duplicate within USUBJID/POOLID</ruledescription>
<ruledetaileddescription>The value of Sequence Number (--SEQ) variable must be unique for each record within a domain and within each Unique Subject Identifier (USUBJID) or Pool Identifier (POOLID) variables value when they are present in the domain</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>ALL</domain>
<rulexquery><![CDATA[
(: Rule SD0005 The value of Sequence Number (--SEQ) variable must be unique for each record within a domain and within each Unique Subject Identifier (USUBJID) or Pool Identifier (POOLID) variables value when they are present in the domain :)
(: TODO: must be done OVER datasets within the same domain :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;  
declare variable $define external;
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml'  :)
let $definedoc := doc(concat($base,$define))
for $itemgroupdef in $definedoc//odm:ItemGroupDef
	let $domain := $itemgroupdef/@Domain
	let $itemgroupoid := $itemgroupdef/@OID
	let $itemgroupname := $itemgroupdef/@Name
	let $datasetname := (
		if($defineversion = '2.1') then $itemgroupdef/def21:leaf/@xlink:href
		else $itemgroupdef/def:leaf/@xlink:href
	)
	let $datasetdoc := (
		if($datasetname) then doc(concat($base,$datasetname))
		else ()
	)
	let $datasets := concat($base,$datasetname) (: result is a set of datasets :)
	(: get the OID of --SEQ, USUBJID :)
	let $usubjidoid := 
		for $a in $definedoc//odm:ItemDef[@Name='USUBJID']/@OID 
		where $a = $definedoc//odm:ItemGroupDef[@Name=$itemgroupname]/odm:ItemRef/@ItemOID
		return $a 
	let $seqoid := 
		for $a in $definedoc//odm:ItemDef[ends-with(@Name,'SEQ')]/@OID 
		where $a = $definedoc//odm:ItemGroupDef[@Name=$itemgroupname]/odm:ItemRef/@ItemOID
		return $a 
	 (: get the Name of --SEQ :)
	 let $seqname := $definedoc//odm:ItemDef[@OID=$seqoid]/@Name
	 let $orderedrecords := (
		for $record in $datasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$usubjidoid] and odm:ItemData[@ItemOID=$seqoid]]
			group by 
			$b := $record/odm:ItemData[@ItemOID=$usubjidoid]/@Value,
			$c := $record/odm:ItemData[@ItemOID=$seqoid]/@Value
			return element group {  
				$record
			}
	 )	
	 for $group in $orderedrecords
			(: each group has the same values for USUBJID and --SEQ :)
			let $usubjid := $group/odm:ItemGroupData[1]/odm:ItemData[@ItemOID=$usubjidoid]/@Value
			let $seq := $group/odm:ItemGroupData[1]/odm:ItemData[@ItemOID=$seqoid]/@Value
			let $recnums := $group/odm:ItemGroupData/@data:ItemGroupDataSeq
			(: for reporting the record number, we take the one of the last record in the group :)
			let $recnum := $group/odm:ItemGroupData[last()]/@data:ItemGroupDataSeq
			(: each group should only have one record. If not, there are duplicate values of USUBJID and --SEQ :)
			where count($group/odm:ItemGroupData) > 1
				return <error rule="SD0005" dataset="{data($itemgroupname)}" variable="{data($seqname)}" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">The record with USUBJID = {data($usubjid)} and {data($seqname)} is not unique in the dataset {data($itemgroupname)}. The following records have the same combination of USUBJID and {data($seqname)}: records number {data($recnums)}</error>
]]></rulexquery>
</sdsrule>

<sdsrule id="SD0005-SD" last-update="2020-08-08" originator="FDA" standard="SDTM" requiresDomainOrDataset="Yes">
<ruledescription>Values for --SEQ variable may not be duplicate within USUBJID/POOLID</ruledescription>
<ruledetaileddescription>The value of Sequence Number (--SEQ) variable must be unique for each record within a domain and within each Unique Subject Identifier (USUBJID) or Pool Identifier (POOLID) variables value when they are present in the domain</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>ALL</domain>
<rulexquery><![CDATA[
(: Rule SD0005 The value of Sequence Number (--SEQ) variable must be unique for each record within a domain and within each Unique Subject Identifier (USUBJID) or Pool Identifier (POOLID) variables value when they are present in the domain :)
(: TODO: must be done OVER datasets within the same domain :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;  
declare variable $define external;
declare variable $defineversion external;
declare variable $datasetname external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml'  :)
let $definedoc := doc(concat($base,$define))
for $itemgroupdef in $definedoc//odm:ItemGroupDef[@Name=$datasetname]
	let $domain := $itemgroupdef/@Domain
	let $itemgroupoid := $itemgroupdef/@OID
	let $itemgroupname := $itemgroupdef/@Name
	let $dsname := (
		if($defineversion = '2.1') then $itemgroupdef/def21:leaf/@xlink:href
		else $itemgroupdef/def:leaf/@xlink:href
	)
	let $datasetdoc := (
		if($dsname) then doc(concat($base,$dsname))
		else ()
	)
	(: get the OID of --SEQ, USUBJID :)
	let $usubjidoid := 
		for $a in $definedoc//odm:ItemDef[@Name='USUBJID']/@OID 
		where $a = $definedoc//odm:ItemGroupDef[@Name=$itemgroupname]/odm:ItemRef/@ItemOID
		return $a 
	let $seqoid := 
		for $a in $definedoc//odm:ItemDef[ends-with(@Name,'SEQ')]/@OID 
		where $a = $definedoc//odm:ItemGroupDef[@Name=$itemgroupname]/odm:ItemRef/@ItemOID
		return $a 
	 (: get the Name of --SEQ :)
	 let $seqname := $definedoc//odm:ItemDef[@OID=$seqoid]/@Name
	   let $orderedrecords := (
		for $record in $datasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$usubjidoid] and odm:ItemData[@ItemOID=$seqoid]]
			group by 
			$b := $record/odm:ItemData[@ItemOID=$usubjidoid]/@Value,
			$c := $record/odm:ItemData[@ItemOID=$seqoid]/@Value
			return element group {  
				$record
			}
		)	
		for $group in $orderedrecords
			(: each group has the same values for USUBJID and --SEQ :)
			let $usubjid := $group/odm:ItemGroupData[1]/odm:ItemData[@ItemOID=$usubjidoid]/@Value
			let $seq := $group/odm:ItemGroupData[1]/odm:ItemData[@ItemOID=$seqoid]/@Value
			let $recnums := $group/odm:ItemGroupData/@data:ItemGroupDataSeq
			(: for reporting the record number, we take the one of the last record in the group :)
			let $recnum := $group/odm:ItemGroupData[last()]/@data:ItemGroupDataSeq
			(: each group should only have one record. If not, there are duplicate values of USUBJID and --SEQ :)
			where count($group/odm:ItemGroupData) > 1
				return <error rule="SD0005" dataset="{data($itemgroupname)}" variable="{data($seqname)}" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">The record with USUBJID = {data($usubjid)} and {data($seqname)} is not unique in the dataset {data($itemgroupname)}. The following records have the same combination of USUBJID and {data($seqname)}: records number {data($recnums)}</error>
]]></rulexquery>
</sdsrule>

<sdsrule id="SD0064" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>Subject must be present in DM domain</ruledescription>
<ruledetaileddescription>All Subjects (USUBJID) must be present in Demographics (DM) domain</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>ALL</domain>
<rulexquery><![CDATA[
(: Rule SD0064 FAST ALTERNATIVE - All Subjects (USUBJID) must be present in Demographics (DM) domain :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
declare namespace functx = "http://www.functx.com";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external;
declare variable $defineversion external;
declare function functx:is-value-in-sequence
  ( $value as xs:anyAtomicType? ,
    $seq as xs:anyAtomicType* )  as xs:boolean {
   $value = $seq
 } ;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
(: request-parameter allows to pass $base from an external programm :)
(: let $base:= request:get-parameter("mybase","/db/fda_submissions/cdiscpilot01/") :)
(: let $define := request:get-parameter("mydefine","define_2_0.xml")  :)
(: let $base := '/db/fda_submissions/cdiscpilot01/' :)
(: let $define := 'define_2_0.xml' :)
let $definedoc := doc(concat($base,$define))
(: we need the ItemOID of the USUBJID variable - and need to take care of the use case that people have used different ItemDefs for the same variable in
different domains/datasets :)
(: first get the one for the DM dataset :)
let $dmitemgroupdef := $definedoc//odm:ItemGroupDef[@Name='DM']
let $dmdatasetname := (
	if($defineversion = '2.1') then $dmitemgroupdef/def21:leaf/@xlink:href
	else $dmitemgroupdef/def:leaf/@xlink:href
)
let $dmdatasetpath := concat($base,$dmdatasetname)
(: this assumes that the third variable in the DM is USUBJID (which essentially always should be the case :)
let $usubjoiddm := (
    for $a in $definedoc//odm:ItemDef[@Name='USUBJID']/@OID 
    where $a = $definedoc//odm:ItemGroupDef[@Name='DM']/odm:ItemRef/@ItemOID
    return $a
)
(: and all the values in the DM dataset :)
let $usubjiddm := doc($dmdatasetpath)//odm:ItemData[@ItemOID=$usubjoiddm]/@Value
(: now iterate over all dataset definitions in the define.xml and get the USUBJID :)
for $itemgroupdef in $definedoc//odm:ItemGroupDef
	let $name := $itemgroupdef/@Name
    let $dataset := (
		if($defineversion = '2.1') then $itemgroupdef/def21:leaf/@xlink:href
		else $itemgroupdef/def:leaf/@xlink:href
	)
    let $datasetname := (
		if($defineversion = '2.1') then $itemgroupdef/def21:leaf/@xlink:href
		else $itemgroupdef/def:leaf/@xlink:href
	)
    let $datasetdoc := (
		if($datasetname) then doc(concat($base,$datasetname))
		else ()
	)
    (: find the variable for which the name is 'USUBJID' - there should be 0 or 1 :)
    let $usubjidoid := (
        for $a in $definedoc//odm:ItemDef[@Name='USUBJID']/@OID 
        where $a = $itemgroupdef/odm:ItemRef/@ItemOID
        return $a
    )
    for $d in $datasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$usubjidoid]]
        let $recnum := $d/@data:ItemGroupDataSeq
        let $value := $d/odm:ItemData[@ItemOID=$usubjidoid]/@Value
        (: check whether it DM-USUBJID :)
        where not(functx:is-value-in-sequence($value,$usubjiddm))
        return <error rule="SD0064" rulelastupdate="2020-08-08" dataset="{data($name)}" variable="USUBJID"  recordnumber="{data($recnum)}">USUBJID {data($value)} in dataset {data($datasetname)} could not be found in DM dataset</error>
]]></rulexquery>
</sdsrule>

<sdsrule id="SD0064-SD" last-update="2020-08-08" originator="FDA" requiresDomainOrDataset="Yes" standard="SDTM">
<ruledescription>Subject must be present in DM domain</ruledescription>
<ruledetaileddescription>All Subjects (USUBJID) must be present in Demographics (DM) domain</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>ALL</domain>
<rulexquery><![CDATA[
(: Rule SD0064 FAST ALTERNATIVE - All Subjects (USUBJID) must be present in Demographics (DM) domain :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
declare namespace functx = "http://www.functx.com";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external;
declare variable $defineversion external;
declare variable $datasetname external;
declare function functx:is-value-in-sequence
  ( $value as xs:anyAtomicType? ,
    $seq as xs:anyAtomicType* )  as xs:boolean {
   $value = $seq
 } ;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
(: request-parameter allows to pass $base from an external programm :)
(: let $base:= request:get-parameter("mybase","/db/fda_submissions/cdiscpilot01/") :)
(: let $define := request:get-parameter("mydefine","define_2_0.xml")  :)
(: let $base := '/db/fda_submissions/cdiscpilot01/' :)
(: let $define := 'define_2_0.xml' :)
let $definedoc := doc(concat($base,$define))
(: we need the ItemOID of the USUBJID variable - and need to take care of the use case that people have used different ItemDefs for the same variable in
different domains/datasets :)
(: first get the one for the DM dataset :)
let $dmitemgroupdef := $definedoc//odm:ItemGroupDef[@Name='DM']
let $dmdatasetname := (
	if($defineversion = '2.1') then $dmitemgroupdef/def21:leaf/@xlink:href
	else $dmitemgroupdef/def:leaf/@xlink:href
)
let $dmdatasetdoc := doc(concat($base,$dmdatasetname))
(: this assumes that the third variable in the DM is USUBJID (which essentially always should be the case :)
let $usubjoiddm := (
    for $a in $definedoc//odm:ItemDef[@Name='USUBJID']/@OID 
    where $a = $definedoc//odm:ItemGroupDef[@Name='DM']/odm:ItemRef/@ItemOID
    return $a
)
(: and all the values in the DM dataset :)
let $usubjiddm := $dmdatasetdoc//odm:ItemData[@ItemOID=$usubjoiddm]/@Value
(: now iterate over all dataset definitions in the define.xml and get the USUBJID :)
for $itemgroupdef in $definedoc//odm:ItemGroupDef[@Name=$datasetname]
    let $dataset := (
		if($defineversion = '2.1') then $itemgroupdef/def21:leaf/@xlink:href
		else $itemgroupdef/def:leaf/@xlink:href
	)
    let $datasetdoc := (
		if($dataset) then doc(concat($base,$dataset))
		else ()
	)
    (: find the variable for which the name is 'USUBJID' - there should be 0 or 1 :)
    let $usubjidoid := (
        for $a in $definedoc//odm:ItemDef[@Name='USUBJID']/@OID 
        where $a = $itemgroupdef/odm:ItemRef/@ItemOID
        return $a
    )
    for $d in $datasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$usubjidoid]]
        let $recnum := $d/@data:ItemGroupDataSeq
        let $value := $d/odm:ItemData[@ItemOID=$usubjidoid]/@Value
        (: check whether it DM-USUBJID :)
        where not(functx:is-value-in-sequence($value,$usubjiddm))
        return <error rule="SD0064" rulelastupdate="2020-08-08" dataset="{data($datasetname)}" variable="USUBJID"  recordnumber="{data($recnum)}">USUBJID {data($value)} in dataset {data($datasetname)} could not be found in DM dataset</error>
]]></rulexquery>
</sdsrule>

<!-- TODO: add check on SVUPDES  -->
<sdsrule id="SD1023" last-update="2020-08-08" originator="FDA" standard="SDTM" timeintensive="Yes">
<ruledescription>VISIT/VISITNUM values must match TV domain data</ruledescription>
<ruledetaileddescription>Combination of Visit Name (VISIT) and Visit Number (VISITNUM) in subject-level domains should match that in the TV domain with the exception of Unscheduled and Unplanned visits</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>ALL</domain>
<rulexquery><![CDATA[
(: Rule SD1023 - VISIT/VISITNUM values do not match TV domain data
Combination of Visit Name (VISIT) and Visit Number (VISITNUM) in subject-level domains should match that in the TV domain with the exception of Unscheduled and Unplanned visits
:)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: Get the TV data :)
let $tvdataset := $definedoc//odm:ItemGroupDef[@Name='TV']
let $tvdatasetname := (
	if($defineversion = '2.1') then $tvdataset/def21:leaf/@xlink:href
	else $tvdataset/def:leaf/@xlink:href
)
let $tvdatasetlocation := concat($base,$tvdatasetname)
(: and the OID of the VISIT and VISITNUM in TV dataset :)
let $tvvisitnumoid := (
    for $a in $definedoc//odm:ItemDef[@Name='VISITNUM']/@OID 
    where $a = $definedoc//odm:ItemGroupDef[@Name='TV']/odm:ItemRef/@ItemOID
    return $a
)
let $tvvisitoid := (
    for $a in $definedoc//odm:ItemDef[@Name='VISIT']/@OID 
    where $a = $definedoc//odm:ItemGroupDef[@Name='TV']/odm:ItemRef/@ItemOID
    return $a
)
(: iterate over all other datasets :)
for $dataset in $definedoc//odm:ItemGroupDef[not(@Name='TV')]
    let $name := $dataset/@Name
    let $datasetname := (
		if($defineversion = '2.1') then $dataset/def21:leaf/@xlink:href
		else $dataset/def:leaf/@xlink:href
	)
    let $datasetdoc := (
		if($datasetname) then doc(concat($base,$datasetname))
		else ()
	)
    (: Get the OID of the VISITNUM and VISIT variables, if any :)
    let $visitnumoid := (
    for $a in $definedoc//odm:ItemDef[@Name='VISITNUM']/@OID 
    where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
    return $a
    )
    let $visitoid := (
        for $a in $definedoc//odm:ItemDef[@Name='VISIT']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
    )
    (: iterate over all records in the dataset that have a VISIT and VISITNUM data point :)
    for $record in $datasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$visitnumoid] and odm:ItemData[@ItemOID=$visitoid]]
        let $recnum := $record/@data:ItemGroupDataSeq
        (: get the value of VISIT and VISITNUM :)
        let $visitnumvalue := $record/odm:ItemData[@ItemOID=$visitnumoid]/@Value
        let $visitvalue := $record/odm:ItemData[@ItemOID=$visitoid]/@Value
        (: and check whether there is such a value in TV, except when VISITNUM contains a "." (unplanned visit) :)
        let $isunplannedvisit := contains($visitnumvalue,'.') 
        (: count the number of records in TV for this combination of VISIT and VISITNUM :)
        let $recordsintv := doc($tvdatasetlocation)//odm:ItemGroupData[odm:ItemData[@ItemOID=$tvvisitnumoid][@Value=$visitnumvalue] and odm:ItemData[@ItemOID=$tvvisitoid][@Value=$visitvalue]]       
        let $recordsintvcount := count($recordsintv)
        (: no records found and not an unplanned visit :)
        where not($isunplannedvisit) and $recordsintvcount < 1
        return <warning rule="SD1023" dataset="{data($name)}" variable="VISITNUM" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">VISIT={data($visitvalue)}, VISITUM={data($visitnumvalue)} in dataset {data($datasetname)} does not match TV domain data in dataset TV</warning>
]]></rulexquery>
</sdsrule>

<!-- TODO: add check on SVUPDES  -->
<sdsrule id="SD1023-SD" last-update="2020-08-08" originator="FDA" requiresDomainOrDataset="Yes" standard="SDTM">
<ruledescription>VISIT/VISITNUM values must match TV domain data</ruledescription>
<ruledetaileddescription>Combination of Visit Name (VISIT) and Visit Number (VISITNUM) in subject-level domains should match that in the TV domain with the exception of Unscheduled and Unplanned visits</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>ALL</domain>
<rulexquery><![CDATA[
(: Rule SD1023 - VISIT/VISITNUM values do not match TV domain data
Combination of Visit Name (VISIT) and Visit Number (VISITNUM) in subject-level domains should match that in the TV domain with the exception of Unscheduled and Unplanned visits
:)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
declare variable $datasetname external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: Get the TV data :)
let $tvdataset := $definedoc//odm:ItemGroupDef[@Name='TV']
let $tvdatasetname := (
	if($defineversion = '2.1') then $tvdataset/def21:leaf/@xlink:href
	else $tvdataset/def:leaf/@xlink:href
)
let $tvdatasetlocation := concat($base,$tvdatasetname)
(: and the OID of the VISIT and VISITNUM in TV dataset :)
let $tvvisitnumoid := (
    for $a in $definedoc//odm:ItemDef[@Name='VISITNUM']/@OID 
    where $a = $definedoc//odm:ItemGroupDef[@Name='TV']/odm:ItemRef/@ItemOID
    return $a
)
let $tvvisitoid := (
    for $a in $definedoc//odm:ItemDef[@Name='VISIT']/@OID 
    where $a = $definedoc//odm:ItemGroupDef[@Name='TV']/odm:ItemRef/@ItemOID
    return $a
)
(: iterate over all other datasets :)
for $dataset in $definedoc//odm:ItemGroupDef[not(@Name='TV') and @Name=$datasetname]
    let $name := $dataset/@Name
    let $dsname := (
		if($defineversion = '2.1') then $dataset/def21:leaf/@xlink:href
		else $dataset/def:leaf/@xlink:href
	)
    let $datasetdoc := (
		if($dsname) then doc(concat($base,$dsname))
		else ()
	)
    (: Get the OID of the VISITNUM and VISIT variables, if any :)
    let $visitnumoid := (
    for $a in $definedoc//odm:ItemDef[@Name='VISITNUM']/@OID 
    where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
    return $a
    )
    let $visitoid := (
        for $a in $definedoc//odm:ItemDef[@Name='VISIT']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
    )
    (: iterate over all records in the dataset that have a VISIT and VISITNUM data point :)
    for $record in $datasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$visitnumoid] and odm:ItemData[@ItemOID=$visitoid]]
        let $recnum := $record/@data:ItemGroupDataSeq
        (: get the value of VISIT and VISITNUM :)
        let $visitnumvalue := $record/odm:ItemData[@ItemOID=$visitnumoid]/@Value
        let $visitvalue := $record/odm:ItemData[@ItemOID=$visitoid]/@Value
        (: and check whether there is such a value in TV, except when VISITNUM contains a "." (unplanned visit) :)
        let $isunplannedvisit := contains($visitnumvalue,'.') 
        (: count the number of records in TV for this combination of VISIT and VISITNUM :)
        let $recordsintv := doc($tvdatasetlocation)//odm:ItemGroupData[odm:ItemData[@ItemOID=$tvvisitnumoid][@Value=$visitnumvalue] and odm:ItemData[@ItemOID=$tvvisitoid][@Value=$visitvalue]]       
        let $recordsintvcount := count($recordsintv)
        (: no records found and not an unplanned visit :)
        where not($isunplannedvisit) and $recordsintvcount < 1
        return <warning rule="SD1023" dataset="{data($name)}" variable="VISITNUM" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">VISIT={data($visitvalue)}, VISITUM={data($visitnumvalue)} in dataset {data($datasetname)} does not match TV domain data in dataset TV </warning>
]]></rulexquery>
</sdsrule>

<sdsrule id="SD1018" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>VISITNUM/VISIT/VISITDY values must match TV domain data</ruledescription>
<ruledetaileddescription>The combination of Visit Number (VISITNUM), Visit Name (VISIT), and Planned Study Day of Visit (VISITDY) values should match entries in the Trial Visits (TV) dataset, when they are planned visits (SVUPDES = NULL)</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>SV</domain>
<rulexquery><![CDATA[	
(: Rule SD1018 - VISITNUM/VISIT/VISITDY values do not match TV domain data:
The combination of Visit Number (VISITNUM), Visit Name (VISIT), and Planned Study Day of Visit (VISITDY) values should match entries in the Trial Visits (TV) dataset, when they are planned visits (SVUPDES = NULL)
SV dataset
:)
(: REMARK that VISIT and VISITDY are permissible (in SDTMIG-3.2) :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
declare namespace xs="http://www.w3.org/2001/XMLSchema";
declare namespace functx = "http://www.functx.com";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
declare function functx:is-value-in-sequence
  ($value as xs:anyAtomicType? ,
    $seq as xs:anyAtomicType* )  as xs:boolean {
   $value = $seq
} ;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: get the TV dataset :)
let $tvdataset := $definedoc//odm:ItemGroupDef[@Name='TV']
let $tvdatasetname := (
	if($defineversion = '2.1') then $tvdataset/def21:leaf/@xlink:href
	else $tvdataset/def:leaf/@xlink:href
)
let $tvdatasetdoc := doc(concat($base,$tvdatasetname))
(: get the OID of the VISITNUM, VISIT and VISITDY in the TV daatset :)
let $tvvisitnumoid := (
    for $a in $definedoc//odm:ItemDef[@Name='VISITNUM']/@OID 
    where $a = $definedoc//odm:ItemGroupDef[@Name='TV']/odm:ItemRef/@ItemOID
    return $a 
)
let $tvvisitoid := (
    for $a in $definedoc//odm:ItemDef[@Name='VISIT']/@OID 
    where $a = $definedoc//odm:ItemGroupDef[@Name='TV']/odm:ItemRef/@ItemOID
    return $a 
)
let $tvvisitdyoid := (
    for $a in $definedoc//odm:ItemDef[@Name='VISITDY']/@OID 
    where $a = $definedoc//odm:ItemGroupDef[@Name='TV']/odm:ItemRef/@ItemOID
    return $a 
)
(: get the SV dataset :)
let $svdataset := $definedoc//odm:ItemGroupDef[@Name='SV']
let $svdatasetname := (
	if($defineversion = '2.1') then $svdataset/def21:leaf/@xlink:href
	else $svdataset/def:leaf/@xlink:href
)
let $svdatasetdoc := (
	if($svdatasetname) then doc(concat($base,$svdatasetname))
	else ()
)
(: get the OID of ISITNUM, VISIT and VISITDY and SVUPDES in the SV dataset
 (these may be different from within TV) :)
(: iterate over all the records in the SV dataset :)
let $svvisitnumoid := (
    for $a in $definedoc//odm:ItemDef[@Name='VISITNUM']/@OID 
    where $a = $definedoc//odm:ItemGroupDef[@Name='SV']/odm:ItemRef/@ItemOID
    return $a 
)
let $svvisitoid := (
    for $a in $definedoc//odm:ItemDef[@Name='VISIT']/@OID 
    where $a = $definedoc//odm:ItemGroupDef[@Name='SV']/odm:ItemRef/@ItemOID
    return $a 
)
let $svvisitdyoid := (
    for $a in $definedoc//odm:ItemDef[@Name='VISITDY']/@OID 
    where $a = $definedoc//odm:ItemGroupDef[@Name='SV']/odm:ItemRef/@ItemOID
    return $a 
)
let $svupdesoid := (
    for $a in $definedoc//odm:ItemDef[@Name='SVUPDES']/@OID 
    where $a = $definedoc//odm:ItemGroupDef[@Name='SV']/odm:ItemRef/@ItemOID
    return $a 
)
(: iterate over all the records in the SV dataset that have SVUPDES=null :)
(: but only when SV-VISIT and SV-VISITDY have been defined, as these are permissible :)
for $record in $svdatasetdoc[$svvisitoid and $svvisitdyoid]//odm:ItemGroupData[not(odm:ItemData[@ItemOID=$svupdesoid])]
    let $recnum := $record/@data:ItemGroupDataSeq
    (: and get the value of VISITNUM, VISIT and VISITDY :)
    let $svvisitnumvalue := $record/odm:ItemData[@ItemOID=$svvisitnumoid]/@Value
    let $svvisitvalue := $record/odm:ItemData[@ItemOID=$svvisitoid]/@Value
    let $svvisitdyvalue := $record/odm:ItemData[@ItemOID=$svvisitdyoid]/@Value
    (: now find the record in the TV dataset that has the same VISITNUM :)
    let $tvrecord := tvdatasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$tvvisitnumoid and @Value=$svvisitnumvalue]]
    (: and get the VISIT and VISITDY values :)
    let $tvvisitvalue := $tvrecord/odm:ItemData[@ItemOID=$tvvisitoid]/@Value
    let $tvvisitdyvalue := $tvrecord/odm:ItemData[@ItemOID=$tvvisitdyoid]/@Value
    (: there must be a 1:1 match :)
    where not($tvrecord) or not($svvisitvalue=$tvvisitvalue) or not($svvisitdyvalue=$tvvisitdyvalue)
    return <warning rule="SD1018" dataset="SV" variable="VISITNUM" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">VISITNUM/VISIT/VISITDY values in SV do not match TV domain data, SV VISITNUM='{data($svvisitnumvalue)}', VISIT='{data($svvisitvalue)}', VISITDY='{data($svvisitdyvalue)}'</warning>	
]]></rulexquery>
</sdsrule>


<sdsrule id="SD0065" last-update="2020-08-08" originator="FDA" standard="SDTM" timeintensive="Yes">
<ruledescription>USUBJID/VISIT/VISITNUM values must match SV domain data</ruledescription>
<ruledetaileddescription>All Unique Subject Identifier (USUBJID) + Visit Name (VISIT) + Visit Number (VISITNUM) combination values in data should be present in the Subject Visits (SV) domain. There is an exception for records, where Status (--STAT) variable value is populated. E.g., --STAT = 'NOT DONE'</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>ALL</domain>
<rulexquery><![CDATA[
(: Rule SD0065: USUBJID/VISIT/VISITNUM values do not match SV domain data - 
All Unique Subject Identifier (USUBJID) + Visit Name (VISIT) + Visit Number (VISITNUM) combination values in data should be present in the Subject Visits (SV) domain. There is an exception for records, where Status (--STAT) variable value is populated. E.g., --STAT = 'NOT DONE' :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external;
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' 
let $define := 'define2-0-0-example-sdtm.xml'  :)
(: get the define.xml document :)
let $definedoc := doc(concat($base,$define))
(: get the SV dataset definition :)
let $svdatasetdef := $definedoc//odm:ItemGroupDef[@Name='SV']
(: and the OIDs of USUBJID, VISIT and VISITNUM :)
let $svusubjudoid := (
    for $a in $definedoc//odm:ItemDef[@Name='USUBJID']/@OID 
            where $a = $svdatasetdef/odm:ItemRef/@ItemOID
        return $a
)
let $svvisitoid := (
    for $a in $definedoc//odm:ItemDef[@Name='VISIT']/@OID 
            where $a = $svdatasetdef/odm:ItemRef/@ItemOID
        return $a
)
let $svvisitnumoid := (
    for $a in $definedoc//odm:ItemDef[@Name='VISITNUM']/@OID 
            where $a = $svdatasetdef/odm:ItemRef/@ItemOID
        return $a
)
(: and the location of the SV dataset :)
 let $svdatasetloc := (
	if($defineversion = '2.1') then $svdatasetdef/def21:leaf/@xlink:href
	else $svdatasetdef/def:leaf/@xlink:href
)
 let $svdatasetdoc := doc(concat($base,$svdatasetloc))
(: create a temporary structure containing triples USUBJID-VISITNUM-VISIT from SV :)
let $svtriples := (
    for $record in $svdatasetdoc//odm:ItemGroupData
        let $usubjidvalue := $record/odm:ItemData[@ItemOID=$svusubjudoid]/@Value
        let $visitvalue := $record/odm:ItemData[@ItemOID=$svvisitoid]/@Value
        let $visitnumvalue := $record/odm:ItemData[@ItemOID=$svvisitnumoid]/@Value
        return <svrecord usubjid="{$usubjidvalue}" visit="{$visitvalue}" visitnum="{$visitnumvalue}" />
)
(: return <test>{$svtriples}</test> :)
(: iterate over all other datasets except for SV :)
for $datasetdef in $definedoc//odm:ItemGroupDef[not(@Name='SV')]
    (: get the dataset name :)
    let $name := $datasetdef/@Name
    (: also here, we need the OIDs of USUBJID, VISIT and VISITNUM . these can be different from those in SV :)
    let $usubjudoid := (
    for $a in $definedoc//odm:ItemDef[@Name='USUBJID']/@OID 
            where $a = $datasetdef/odm:ItemRef/@ItemOID
        return $a
    )
    let $visitoid := (
        for $a in $definedoc//odm:ItemDef[@Name='VISIT']/@OID 
                where $a = $datasetdef/odm:ItemRef/@ItemOID
            return $a
    )
    let $visitnumoid := (
        for $a in $definedoc//odm:ItemDef[@Name='VISITNUM']/@OID 
                where $a = $datasetdef/odm:ItemRef/@ItemOID
            return $a
    )
	(: and, not to forget, the OID of the --STAT variable  :)
	let $statoid := (
		for $a in $definedoc//odm:ItemDef[ends-with(@Name,'STAT')]/@OID 
                where $a = $datasetdef/odm:ItemRef/@ItemOID
            return $a
	)
    (: get the location of the dataset :)
    let $datasetloc := (
		if($defineversion = '2.1') then $datasetdef/def21:leaf/@xlink:href
		else $datasetdef/def:leaf/@xlink:href
	)
    (: and the document itself :)
    let $datasetdoc := (
		if($datasetloc) then doc(concat($base,$datasetloc))
		else ()
	)
    (: iterate over all the records in the dataset, 
    but only if USUBJID, VISIT and VISITNUM have been defined for the dataset, 
    AND there is a value for all three of them :)
    for $record in $datasetdoc[$usubjudoid and $visitoid and $visitnumoid]//odm:ItemGroupData[odm:ItemData[@ItemOID=$usubjudoid]/@Value and odm:ItemData[@ItemOID=$visitoid]/@Value and odm:ItemData[@ItemOID=$visitnumoid]/@Value]
        let $recnum := $record/@data:ItemGroupDataSeq
    (: get the values of USUBJID, VISIT and VISITNUM :)
        let $usubjidvalue := $record/odm:ItemData[@ItemOID=$usubjudoid]/@Value
        let $visitvalue := $record/odm:ItemData[@ItemOID=$visitoid]/@Value
        let $visitnumvalue := $record/odm:ItemData[@ItemOID=$visitnumoid]/@Value
		let $statvalue := $record/odm:ItemData[@ItemOID=$statoid]/@Value
        (: this triple must also appear in the list of triples :)
        let $count := count($svtriples[@usubjid=$usubjidvalue and @visit=$visitvalue and @visitnum=$visitnumvalue])
        (: there must be at least one such record in SV, except for when --STAT is populated :)
        where $count = 0 and not($statvalue)
        return <warning rule="SD0065" dataset="{$name}" variable="VISIT" rulelastupdate="2020-08-08" recordnumber="{$recnum}">The value of USUBJID='{data($usubjidvalue)}', VISIT='{data($visitvalue)}', VISITNUM='{data($visitnumvalue)}' in dataset '{data($name)}' could not be found in the SV dataset </warning> 
]]>
</rulexquery>
</sdsrule>

<sdsrule id="SD0065-SD" last-update="2020-08-08" originator="FDA" standard="SDTM" requiresDomainOrDataset="Yes">
<ruledescription>USUBJID/VISIT/VISITNUM values must match SV domain data</ruledescription>
<ruledetaileddescription>All Unique Subject Identifier (USUBJID) + Visit Name (VISIT) + Visit Number (VISITNUM) combination values in data should be present in the Subject Visits (SV) domain. There is an exception for records, where Status (--STAT) variable value is populated. E.g., --STAT = 'NOT DONE'</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>ALL</domain>
<rulexquery><![CDATA[
(: Rule SD0065: USUBJID/VISIT/VISITNUM values do not match SV domain data - 
All Unique Subject Identifier (USUBJID) + Visit Name (VISIT) + Visit Number (VISITNUM) combination values in data should be present in the Subject Visits (SV) domain. There is an exception for records, where Status (--STAT) variable value is populated. E.g., --STAT = 'NOT DONE' :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external;
declare variable $defineversion external;
declare variable $datasetname external;
(: let $base := '/db/fda_submissions/cdisc01/' 
let $define := 'define2-0-0-example-sdtm.xml'  :)
(: get the define.xml document :)
let $definedoc := doc(concat($base,$define))
(: get the SV dataset definition :)
let $svdatasetdef := $definedoc//odm:ItemGroupDef[@Name='SV']
(: and the OIDs of USUBJID, VISIT and VISITNUM :)
let $svusubjudoid := (
    for $a in $definedoc//odm:ItemDef[@Name='USUBJID']/@OID 
            where $a = $svdatasetdef/odm:ItemRef/@ItemOID
        return $a
)
let $svvisitoid := (
    for $a in $definedoc//odm:ItemDef[@Name='VISIT']/@OID 
            where $a = $svdatasetdef/odm:ItemRef/@ItemOID
        return $a
)
let $svvisitnumoid := (
    for $a in $definedoc//odm:ItemDef[@Name='VISITNUM']/@OID 
            where $a = $svdatasetdef/odm:ItemRef/@ItemOID
        return $a
)
(: and the location of the SV dataset :)
let $svdatasetloc := (
	if($defineversion = '2.1') then $svdatasetdef/def21:leaf/@xlink:href
	else $svdatasetdef/def:leaf/@xlink:href
)
 let $svdatasetdoc := (
	if($svdatasetloc) then doc(concat($base,$svdatasetloc))
	else ()
)
(: create a temporary structure containing triples USUBJID-VISITNUM-VISIT from SV :)
let $svtriples := (
    for $record in $svdatasetdoc//odm:ItemGroupData
        let $usubjidvalue := $record/odm:ItemData[@ItemOID=$svusubjudoid]/@Value
        let $visitvalue := $record/odm:ItemData[@ItemOID=$svvisitoid]/@Value
        let $visitnumvalue := $record/odm:ItemData[@ItemOID=$svvisitnumoid]/@Value
        return <svrecord usubjid="{$usubjidvalue}" visit="{$visitvalue}" visitnum="{$visitnumvalue}" />
)
(: return <test>{$svtriples}</test> :)
(: iterate over all other provided datasets except for SV :)
for $datasetdef in $definedoc//odm:ItemGroupDef[@Name=$datasetname][not(@Name='SV')]
    (: get the dataset name :)
    let $name := $datasetdef/@Name
    (: also here, we need the OIDs of USUBJID, VISIT and VISITNUM . these can be different from those in SV :)
    let $usubjudoid := (
    for $a in $definedoc//odm:ItemDef[@Name='USUBJID']/@OID 
            where $a = $datasetdef/odm:ItemRef/@ItemOID
        return $a
    )
    let $visitoid := (
        for $a in $definedoc//odm:ItemDef[@Name='VISIT']/@OID 
                where $a = $datasetdef/odm:ItemRef/@ItemOID
            return $a
    )
    let $visitnumoid := (
        for $a in $definedoc//odm:ItemDef[@Name='VISITNUM']/@OID 
                where $a = $datasetdef/odm:ItemRef/@ItemOID
            return $a
    )
	(: and, not to forget, the OID of the --STAT variable  :)
	let $statoid := (
		for $a in $definedoc//odm:ItemDef[ends-with(@Name,'STAT')]/@OID 
                where $a = $datasetdef/odm:ItemRef/@ItemOID
            return $a
	)
    (: get the location of the dataset :)
    let $datasetloc := (
		if($defineversion = '2.1') then $datasetdef/def21:leaf/@xlink:href
		else $datasetdef/def:leaf/@xlink:href
	)
    (: and the document itself :)
    let $datasetdoc := (
		if($datasetloc) then doc(concat($base,$datasetloc))
		else ()
	)
    (: iterate over all the records in the dataset, 
    but only if USUBJID, VISIT and VISITNUM have been defined for the dataset, 
    AND there is a value for all three of them :)
    for $record in $datasetdoc[$usubjudoid and $visitoid and $visitnumoid]//odm:ItemGroupData[odm:ItemData[@ItemOID=$usubjudoid]/@Value and odm:ItemData[@ItemOID=$visitoid]/@Value and odm:ItemData[@ItemOID=$visitnumoid]/@Value]
        let $recnum := $record/@data:ItemGroupDataSeq
    (: get the values of USUBJID, VISIT and VISITNUM :)
        let $usubjidvalue := $record/odm:ItemData[@ItemOID=$usubjudoid]/@Value
        let $visitvalue := $record/odm:ItemData[@ItemOID=$visitoid]/@Value
        let $visitnumvalue := $record/odm:ItemData[@ItemOID=$visitnumoid]/@Value
		let $statvalue := $record/odm:ItemData[@ItemOID=$statoid]/@Value
        (: this triple must also appear in the list of triples :)
        let $count := count($svtriples[@usubjid=$usubjidvalue and @visit=$visitvalue and @visitnum=$visitnumvalue])
        (: there must be at least one such record in SV, except for when --STAT is populated :)
        where $count = 0 and not($statvalue)
        return <warning rule="SD0065" dataset="{$name}" variable="VISIT" rulelastupdate="2020-08-08" recordnumber="{$recnum}">The value of USUBJID='{data($usubjidvalue)}', VISIT='{data($visitvalue)}', VISITNUM='{data($visitnumvalue)}' in dataset '{data($name)}' could not be found in the SV dataset </warning> 
]]>
</rulexquery>
</sdsrule>

<sdsrule id="SD0051" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>Inconsistent Value for VISIT must be consistent within VISITNUM in SV and TV</ruledescription>
<ruledetaileddescription>All values of Visit Name (VISIT) should be the same for a given value of Visit Number (VISITNUM) in domains TV and SV</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>SV</domain>
<domain>TV</domain>
<rulexquery><![CDATA[	
(: Rule SD0051 - Inconsistent value for VISIT within VISITNUM
All values of Visit Name (VISIT) should be the same for a given value of Visit Number (VISITNUM) in domains TV and SV
:)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;

(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: Iterate over the TV and VS datasets :)
for $dataset in $definedoc//odm:ItemGroupDef[@Name='TV' or @Name='SV']
    let $name := $dataset/@Name
    let $datasetname := (
		if($defineversion = '2.1') then $dataset/def21:leaf/@xlink:href
		else $dataset/def:leaf/@xlink:href
	)
    let $datasetdoc := (
		if($datasetname) then doc(concat($base,$datasetname))
		else ()
	)
    (: and get the OID of VISITNUM and VISIT :)
    let $visitnumoid := (
        for $a in $definedoc//odm:ItemDef[@Name='VISITNUM']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a 
    )
    let $visitoid := (
        for $a in $definedoc//odm:ItemDef[@Name='VISIT']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a 
    )
    (: get all unique VISITNUM values within the dataset :)
    let $uniquevisitnumvalues := distinct-values($datasetdoc//odm:ItemGroupData/odm:ItemData[@ItemOID=$visitnumoid]/@Value)
        (: iterate over these unique VISITNUM values and for each of them, find all records - also all with the same value for VISITNUM :)
        for $uniquevisitnum in $uniquevisitnumvalues
            let $records := $datasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$visitnumoid][@Value=$uniquevisitnum]]
            (: now get the distinct values for VISIT within each VISITNUM :)
            let $uniquevisitvalues := distinct-values($records/odm:ItemData[@ItemOID=$visitoid]/@Value)
            (: the number of unique VISIT values for each VISITNUM must be 1 :)
            let $uniquevisitvaluescount := count($uniquevisitvalues)
            where $uniquevisitvaluescount != 1
            return <error rule="SD0051" dataset="{data($name)}" variable="VISIT" rulelastupdate="2020-08-08">Inconsistent value for VISIT within VISITNUM={data($uniquevisitnum)} in dataset {data($datasetname)}. {data($uniquevisitvaluescount)} different combinations were found</error>
]]></rulexquery>
</sdsrule>

<sdsrule id="SD0052" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>Values for VISITNUM must be consistent within VISIT</ruledescription>
<ruledetaileddescription>All values of Visit Number (VISITNUM) should be the same for a given value of Visit Name (VISIT)</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>SV</domain>
<domain>TV</domain>
<rulexquery><![CDATA[	
(: Rule SD0052 - Inconsistent value for VISITNUM within VISIT
All values of Visit Number (VISITNUM) should be the same for a given value of Visit Name (VISIT)
:)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: Iterate over the TV and VS datasets :)
for $dataset in $definedoc//odm:ItemGroupDef[@Name='TV' or @Name='SV']
    let $name := $dataset/@Name
	let $datasetname := (
		if($defineversion = '2.1') then $dataset/def21:leaf/@xlink:href
		else $dataset/def:leaf/@xlink:href
	)
    let $datasetdoc := (
		if($datasetname) then doc(concat($base,$datasetname))
		else ()
	)
    (: and get the OID of VISITNUM and VISIT :)
    let $visitnumoid := (
        for $a in $definedoc//odm:ItemDef[@Name='VISITNUM']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a 
    )
    let $visitoid := (
        for $a in $definedoc//odm:ItemDef[@Name='VISIT']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a 
    )
    (: get all unique VISIT values within the dataset :)
    let $uniquevisitvalues := distinct-values($datasetdoc//odm:ItemGroupData/odm:ItemData[@ItemOID=$visitoid]/@Value)
        (: iterate over these unique VISIT values and for each of them, find all records - also all with the same value for VISIT :)
        for $uniquevisit in $uniquevisitvalues
            let $records := $datasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$visitoid][@Value=$uniquevisit]]
            (: now get the distinct values for VISIT within each VISITNUM :)
            let $uniquevisitnumvalues := distinct-values($records/odm:ItemData[@ItemOID=$visitnumoid]/@Value)
            (: the number of unique VISITNUM values for each VISIT must be 1 :)
            let $uniquevisitnumvaluescount := count($uniquevisitnumvalues)
            where $uniquevisitnumvaluescount != 1
            return <error rule="SD0052" dataset="{data($name)}" variable="VISITNUM" rulelastupdate="2020-08-08">Inconsistent value for VISITNUM within VISIT={data($uniquevisit)} in dataset {data($datasetname)}. {data($uniquevisitnumvaluescount)} different combinations were found</error>
			]]></rulexquery>
			</sdsrule>

<!-- Rule SD1073: "Variables described in IG as inappropriate for usage must be not included in the dataset." 
has not been implemented here as is a nonsense rule, and should not lead to an error or warning.
It could be implemented as an "INFO". 
TODO: can this be done using the CDISC Library API? - I made a formal request -->


<sdsrule id="SD1062" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>AESER must be 'Y', when AESOD equals 'Y'</ruledescription>
<ruledetaileddescription>Serious Event (AESER) variable value is expected to be 'Y', when Occurred with Overdose (AESOD) variable value equals 'Y'</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>AE</domain>
<rulexquery><![CDATA[	
(: Rule SD1062 - AESER is not 'Y', when AESOD equals 'Y':
Serious Event (AESER) variable value is expected to be 'Y', when Occurred with Overdose (AESOD) variable value equals 'Y'
:)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: Get the AE dataset(s) :)
for $aedataset in $definedoc//odm:ItemGroupDef[@Name='AE']
    let $aename := $aedataset/@Name
    let $aedatasetname := (
		if($defineversion = '2.1') then $aedataset/def21:leaf/@xlink:href
		else $aedataset/def:leaf/@xlink:href
	)
    let $aedatasetdoc := (
		if($aedatasetname) then doc(concat($base,$aedatasetname))
		else ()
	)
    (: and get the OID of the AESER and AESOD variables :)
    let $aeseroid := (
        for $a in $definedoc//odm:ItemDef[@Name='AESER']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$aename]/odm:ItemRef/@ItemOID
        return $a
    )
    let $aesodoid := (
        for $a in $definedoc//odm:ItemDef[@Name='AESOD']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$aename]/odm:ItemRef/@ItemOID
        return $a
    )
    (: iterate over all records that have AESOD=Y :)
    for $record in $aedatasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$aesodoid and @Value='Y']]
        let $recnum := $record/@data:ItemGroupDataSeq
        (: get the value of AESER (if any) :)
        let $aeservalue := $record/odm:ItemData[@ItemOID=$aeseroid]/@Value
        (: AESER must have the value 'Y' :)
        where not($aeservalue) or not($aeservalue='Y')
        return <error rule="SD1062" dataset="AE" variable="AESER" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">AESER is not 'Y', when AESOD equals 'Y', value found for AESER={data($aeservalue)}</error>	
]]></rulexquery>
</sdsrule>


<sdsrule id="SD0009" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>At least one qualifier must be set to 'Y', when AE is Serious</ruledescription>
<ruledetaileddescription>When Serious Event (AESER) variable value is 'Y', then at least one of seriousness criteria variables is expected to have value 'Y' (Involves Cancer (AESCAN), Congenital Anomaly or Birth Defect (AESCONG), Persist or Signif Disability/Incapacity (AESDISAB), Results in Death (AESDTH), Requires or Prolongs Hospitalization (AESHOSP), Is Life Threatening (AESLIFE), or Other Medicaly Important Serious Event (AESMIE))</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>AE</domain>
<rulexquery><![CDATA[	
(: Rule SD0009 - No qualifiers set to 'Y', when AE is Serious:
When Serious Event (AESER) variable value is 'Y', then at least one of seriousness criteria variables is expected to have value 'Y' (Involves Cancer (AESCAN), Congenital Anomaly or Birth Defect (AESCONG), Persist or Signif Disability/Incapacity (AESDISAB), Results in Death (AESDTH), Requires or Prolongs Hospitalization (AESHOSP), Is Life Threatening (AESLIFE), or Other Medicaly Important Serious Event (AESMIE))
:)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: iterate over the AE datasets (for the case there is more than 1) :)
for $dataset in $definedoc//odm:ItemGroupDef[@Domain='AE']
    let $name := $dataset/@Name
	let $datasetname := (
		if($defineversion = '2.1') then $dataset/def21:leaf/@xlink:href
		else $dataset/def:leaf/@xlink:href
	)
    let $datasetdoc := (
		if($datasetname) then doc(concat($base,$datasetname))
		else ()
	)
    (: and get the OIDs of AESER, AESCAN, AESCONG, AESDISAB, AESDTH, AESHOSP, AESLIFE and AESMIE :)
    let $aeseroid := (
        for $a in $definedoc//odm:ItemDef[@Name='AESER']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
    )
    let $aescanoid := (
        for $a in $definedoc//odm:ItemDef[@Name='AESCAN']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
    )
    let $aescongoid := (
        for $a in $definedoc//odm:ItemDef[@Name='AESCONG']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a    
    )
    let $aesdisaboid := (
        for $a in $definedoc//odm:ItemDef[@Name='AESDISAB']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a 
    )
    let $aesdthoid := (
        for $a in $definedoc//odm:ItemDef[@Name='AESDTH']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a 
    )
    let $aeshospoid := (
        for $a in $definedoc//odm:ItemDef[@Name='AESHOSP']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a 
    )
    let $aeslifeoid := (
        for $a in $definedoc//odm:ItemDef[@Name='AESLIFE']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a 
    )
    let $aesmieoid := (
        for $a in $definedoc//odm:ItemDef[@Name='AESMIE']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a 
    )
    (: iterate over all the records that have AESER=Y :)
    for $record in $datasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$aeseroid and @Value='Y']]
        let $recnum := $record/@data:ItemGroupDataSeq
        (: get the values of AESCAN, AESCONG, AESDISAB, AESDTH, AESHOSP, AESLIFE and AESMIE (if any) :)
        let $aescanvalue := $record/odm:ItemData[@ItemOID=$aescanoid]/@Value
        let $aescongvalue := $record/odm:ItemData[@ItemOID=$aescongoid]/@Value
        let $aesdisabvalue := $record/odm:ItemData[@ItemOID=$aesdisaboid]/@Value
        let $aesdthvalue := $record/odm:ItemData[@ItemOID=$aesdthoid]/@Value
        let $aeshospvalue := $record/odm:ItemData[@ItemOID=$aeshospoid]/@Value
        let $aeslifevalue := $record/odm:ItemData[@ItemOID=$aeslifeoid]/@Value
        let $aesmievalue := $record/odm:ItemData[@ItemOID=$aesmieoid]/@Value
        (: at least one of AESCAN, AESCONG, AESDISAB, AESDTH, AESHOSP, AESLIFE and AESMIE must have the value 'Y' :)
        where not($aescanvalue='Y' or $aescongvalue='Y' or $aesdisabvalue='Y' or $aesdthvalue='Y' or $aeshospvalue='Y' or $aeslifevalue='Y' or $aesmievalue='Y')
        return <error rule="SD0009" dataset="{data($name)}" variable="AESER" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">No qualifiers set to 'Y', when AE is Serious in dataset {data($datasetname)}</error>	
	]]></rulexquery>
</sdsrule>

<!-- Rule SD1143: No Details info for AESMIE Adverse Event in SUPPAE domain - ONLY for IG 3.2-->
<sdsrule id="SD1143" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>Details info for AESMIE Adverse Event must be provided in SUPPAE domain</ruledescription>
<ruledetaileddescription>When a description of Other Medically Important Serious Adverse Events category is collected on a CRF, sponsors should place the description in the SUPPAE dataset using the standard supplemental qualifier name code AESOSP</ruledetaileddescription>
<igversion>3.2</igversion>
<domain>AE</domain>
<rulexquery><![CDATA[
(: Rule SD1143: No Details info for AESMIE Adverse Event in SUPPAE domain :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external;
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' 
let $define := 'define2-0-0-example-sdtm.xml' :)
(: get the define.xml document :)
let $definedoc := doc(concat($base,$define))
(: get the AE dataset definition :)
let $aedatasetdef := $definedoc//odm:ItemGroupDef[@Name='AE'] (: we assume there is only one AE dataset :)
(: get its location and the document itself :)
let $aedatasetloc := (
	if($defineversion = '2.1') then $aedatasetdef/def21:leaf/@xlink:href
	else $aedatasetdef/def:leaf/@xlink:href
)
let $aedatasetdoc := (
	if($aedatasetloc) then doc(concat($base,$aedatasetloc))
	else ()
)
(: get the OID of AESMIE :)
let $aesmieoid := (
    for $a in $definedoc//odm:ItemDef[@Name='AESMIE']/@OID 
            where $a = $aedatasetdoc/odm:ItemRef/@ItemOID
        return $a
)
(: and the OID of USUBJID and AESEQ, as we assume that this will be the identifier in SUPPAE :)
(: get the SUPPAE dataset location and document :)
let $aeusubjidoid := (
    for $a in $definedoc//odm:ItemDef[@Name='USUBJID']/@OID 
            where $a = $aedatasetdoc/odm:ItemRef/@ItemOID
        return $a
)
let $aeaeseqoid := (
    for $a in $definedoc//odm:ItemDef[@Name='AESEQ']/@OID 
            where $a = $aedatasetdoc/odm:ItemRef/@ItemOID
        return $a
)
let $suppaedataset := $definedoc//odm:ItemGroupDef[@Name='SUPPAE']
let $suppaedatasetloc := (
	if($defineversion = '2.1') then $suppaedataset/def21:leaf/@xlink:href
	else $suppaedataset/def:leaf/@xlink:href
)
let $suppaedatasetdoc := (
	if($suppaedatasetloc) then doc(concat($base,$suppaedatasetloc))
	else ()
)
(: we need the OIDs of USUBJID, RDOMAIN, IDVAR, IDVARVAL, QNAM and QVAL :)
let $suppaeusubjidoid := (
    for $a in $definedoc//odm:ItemDef[@Name='USUBJID']/@OID 
            where $a = $suppaedataset/odm:ItemRef/@ItemOID
        return $a
)
let $suppaerdomainoid := (
    for $a in $definedoc//odm:ItemDef[@Name='RDOMAIN']/@OID 
            where $a = $suppaedataset/odm:ItemRef/@ItemOID
        return $a
)
let $suppaeidvaroid := (
    for $a in $definedoc//odm:ItemDef[@Name='IDVAR']/@OID 
            where $a = $suppaedataset/odm:ItemRef/@ItemOID
        return $a
)
let $suppaeidvarvaloid := (
    for $a in $definedoc//odm:ItemDef[@Name='IDVARVAL']/@OID 
            where $a = $suppaedataset/odm:ItemRef/@ItemOID
        return $a
)
let $suppaeqnamoid := (
    for $a in $definedoc//odm:ItemDef[@Name='QNAM']/@OID 
            where $a = $suppaedataset/odm:ItemRef/@ItemOID
        return $a
)
let $suppaeqvaloid := (
    for $a in $definedoc//odm:ItemDef[@Name='VAL']/@OID 
            where $a = $suppaedataset/odm:ItemRef/@ItemOID
        return $a
)
(: iterate over all the records in AE that have AESMIE populated :)
for $record in $aedatasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$aesmieoid]/@Value]
    let $recnum := $record/@data:ItemGroupDataSeq
    (: get the values of USUBJID and AESEQ :)
    let $aeusubjid := $record/odm:ItemData[@ItemOID=$aeusubjidoid]/@Value
    let $aeaeseq := $record/odm:ItemData[@ItemOID=$aeaeseqoid]/@Value
    (: now count the number of records in SUPPAE for which USUBJID and AESEQ match,
    and having the standard supplemental qualifier name (AEVAR) code AESOSP, and IDVARVAL populated :)
    let $count := count($suppaedatasetdoc//odm:ItemGroupData[odm:ItemData[ItemOID=$suppaeusubjidoid]/@Value=$aeusubjid and odm:ItemData[ItemOID=$suppaeidvarvaloid]/@Value=$aeaeseq and odm:ItemData[ItemOID=$suppaerdomainoid]/@Value='AE' and odm:ItemData[ItemOID=$suppaeidvaroid]/@Value='AESEQ' and odm:ItemData[ItemOID=$suppaeidvaroid]/@Value='AESOSP' and odm:ItemData[ItemOID=$suppaeidvarvaloid]/@Value])
    (: there must be one such record :)
    where $count = 0
    return <warning rule="SD1143" dataset="AE" variable="AESMIE" rulelastupdate="2020-08-08" recordnumber="{$recnum}">No record with IDVAR=AESOSP could be found in SUPPAE or SUPPAE is missing</warning> 
]]>
</rulexquery>
</sdsrule>

<!-- Rule SD1291 applies to ALL datasets -->
<!-- This rule is similar (ENTPT and ENRTPT interchanged) to SD0050 -->
<sdsrule id="SD1291" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>--ENRTPT must be populated, when --ENTPT is populated - </ruledescription>
<ruledetaileddescription>End Relative to Reference Time Point (--ENRTPT) must be populated when End Reference Time Point (--ENTPT) is populated</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>ALL</domain>
<rulexquery><![CDATA[	
(: Rule SD1291 - Missing value for --ENRTPT, when --ENTPT is populated - 
End Relative to Reference Time Point (--ENRTPT) must be populated when End Reference Time Point (--ENTPT) is populated
:)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: iterate over all the datasets :)
for $dataset in $definedoc//odm:ItemGroupDef
    let $name := $dataset/@Name
    let $datasetname := (
		if($defineversion = '2.1') then $dataset/def21:leaf/@xlink:href
		else $dataset/def:leaf/@xlink:href
	)
    let $datasetdoc := (
		if($datasetname) then doc(concat($base,$datasetname))
		else ()
	)
    (: get the OIDs of the ENTPT and ENRTPT variables (if any) :)
    let $entptoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'ENTPT')]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
    )
    let $entptname := $definedoc//odm:ItemDef[@OID=$entptoid]/@Name
    let $enrtptoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'ENRTPT')]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
    )
    let $enrtptname := $definedoc//odm:ItemDef[@OID=$enrtptoid]/@Name
    (: iterate over all the records in the dataset for which there is a ENTPT data point :)
    for $record in $datasetdoc//odm:ItemGroupData[odm:ItemData/@ItemOID=$entptoid]
    let $recnum := $record/@data:ItemGroupDataSeq
        (: and check whether there is ENRTPT data point :)
        let $enrtptvalue := $record/odm:ItemData[@ItemOID=$enrtptoid]/@Value
        let $entptvalue := $record/odm:ItemData[@ItemOID=$entptoid]/@Value
        where not($entptvalue)
        return <warning rule="SD1291" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">Missing value for {data($enrtptname)}, when {data($entptname)} is populated, value = {data($entptvalue)}</warning>
]]></rulexquery>
</sdsrule>

<!-- Rule SD1291 applies to ALL datasets -->
<!-- This rule is similar (ENTPT and ENRTPT interchanged) to SD0050 -->
<sdsrule id="SD1291-SD" last-update="2020-08-08" originator="FDA" standard="SDTM" requiresDomainOrDataset="Yes">
<ruledescription>--ENRTPT must be populated, when --ENTPT is populated - </ruledescription>
<ruledetaileddescription>End Relative to Reference Time Point (--ENRTPT) must be populated when End Reference Time Point (--ENTPT) is populated</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>ALL</domain>
<rulexquery><![CDATA[	
(: Rule SD1291 - Missing value for --ENTPT, when --ENRTPT is populated:
End Reference Time Point (--ENTPT) must not be NULL, when End Relative to Reference Time Point (--ENRTPT) is provided
:)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
declare variable $datasetname external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: iterate over all the provided datasets :)
for $dataset in $definedoc//odm:ItemGroupDef[@Name=$datasetname]
    let $name := $dataset/@Name
	let $dsname := (
		if($defineversion = '2.1') then $dataset/def21:leaf/@xlink:href
		else $dataset/def:leaf/@xlink:href
	)
    let $datasetdoc := (
		if($dsname) then doc(concat($base,$dsname))
		else ()
	)
    (: get the OIDs of the ENTPT and ENRTPT variables (if any) :)
    let $entptoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'ENTPT')]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
    )
    let $entptname := $definedoc//odm:ItemDef[@OID=$entptoid]/@Name
    let $enrtptoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'ENRTPT')]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
    )
    let $enrtptname := $definedoc//odm:ItemDef[@OID=$enrtptoid]/@Name
    (: iterate over all the records in the dataset for which there is a ENTPT data point :)
    for $record in $datasetdoc//odm:ItemGroupData[odm:ItemData/@ItemOID=$entptoid]
    let $recnum := $record/@data:ItemGroupDataSeq
        (: and check whether there is ENRTPT data point :)
        let $enrtptvalue := $record/odm:ItemData[@ItemOID=$enrtptoid]/@Value
        let $entptvalue := $record/odm:ItemData[@ItemOID=$entptoid]/@Value
        where not($entptvalue)
        return <warning rule="SD1291" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">Missing value for {data($enrtptname)}, when {data($entptname)} is populated, value = {data($entptvalue)}</warning>	
]]></rulexquery>
</sdsrule>

<!-- Was FFAC142 but now limited to INTERVENTIONS, EVENTS, FINDINGS, SE -->
<sdsrule id="SD1101" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>--ENTPT variable must be present, when --ENRTPT variable is present</ruledescription>
<ruledetaileddescription>End Reference Time Point (--ENTPT) variable must be included into the domain, when End Relative to Reference Time Point (--ENRTPT) variable is present</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>INTERVENTIONS</domain>
<domain>EVENTS</domain>
<domain>FINDINGS</domain>
<domain>SE</domain>
<rulexquery><![CDATA[
(: TODO: not well tested yet :)        
(: Rule SD1101 - Missing --ENTPT variable, when --ENRTPT variable is present:
End Reference Time Point (--ENTPT) variable must be included into the domain, when End Relative to Reference Time Point (--ENRTPT) variable is present
:) 
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink"; 
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: iterate over all datasets within INTERVENTIONS, EVENTS, FINDINGS, SE :)
for $dataset in $definedoc//odm:ItemGroupDef[upper-case(@def:Class)='INTERVENTIONS' or upper-case(@def:Class)='EVENTS' or upper-case(@def:Class)='FINDINGS' 
		or upper-case(./def21:Class/@Name)='INTERVENTIONS' or upper-case(./def21:Class/@Name)='EVENTS' or upper-case(./def21:Class/@Name)='FINDINGS'
		or @Domain='SE']
    let $name := $dataset/@Name
	let $domain := (
		if($dataset/@Domain) then $dataset/@Domain
		else substring($name,1,2)
	)
	let $datasetname := (
		if($defineversion = '2.1') then $dataset/def21:leaf/@xlink:href
		else $dataset/def:leaf/@xlink:href
	)
    let $datasetlocation := concat($base,$datasetname)
    (: Get the OID of the ENRTPT and ENTPT variable (when present) :)
    let $enrtptoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'ENRTPT')]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
    )
    let $enrtptname := $definedoc//odm:ItemDef[@OID=$enrtptoid]/@Name
    let $entptoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'ENTPT')]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
    )
    let $entptname := concat($domain,'ENTPT')  (: it might be that is not in the dataset :)
    (: when ENRTPT is present, also ENTPT must be present :)
    where $enrtptoid and not($entptoid)
    return <error rule="SD1101" variable="{data($enrtptname)}" dataset="{data($name)}"  rulelastupdate="2020-08-08">Missing {data($entptname)} variable, when {data($enrtptname)} variable is present</error>	
]]></rulexquery>
</sdsrule>

<sdsrule id="SD1287" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>Value for --DECOD must be consistent within --PTCD</ruledescription>
<ruledetaileddescription>All values of Dictionary-Derived Term (--DECOD) should be the same for a given value of Preferred Term Code (--PTCD)</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>EVENTS</domain>
<rulexquery><![CDATA[
(: Rule CG0049/FDA-SD1287 - Inconsistent value for --DECOD within --PTCD :)
(: Made faster using "GROUP BY" :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' 
let $define := 'define2-0-0-example-sdtm.xml' :) 
let $definedoc := doc(concat($base,$define)) 
(: According to rule CG0049, the rule applies to all datasets. Is this correct? Shouldn't it be limited to EVENTS? :)
(: Iterate over the EVENTS datasets :)
for $itemgroupdef in $definedoc//odm:ItemGroupDef[@def:Class='EVENTS' or ./def21:Class/@Name='EVENTS']
    let $name := $itemgroupdef/@Name
	let $datasetname := (
		if($defineversion = '2.1') then $itemgroupdef/def21:leaf/@xlink:href
		else $itemgroupdef/def:leaf/@xlink:href
	)
    let $datasetdoc := (
		if($datasetname) then doc(concat($base,$datasetname))
		else ()
	)
    (: and get the OID of --DECOD and --PTCD :)
    let $decodoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'DECOD')]/@OID 
        where $a = $itemgroupdef/odm:ItemRef/@ItemOID
        return $a 
    )
    let $decodname := $definedoc//odm:ItemDef[@OID=$decodoid]/@Name (: and the name (for reporting) :)
    let $ptcdoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'PTCD')]/@OID 
        where $a = $itemgroupdef/odm:ItemRef/@ItemOID
        return $a 
    )
    let $ptcdname := $definedoc//odm:ItemDef[@OID=$ptcdoid]/@Name (: and the name (for reporting) :)
    (: group all the records in the dataset (at least when -PTCD and -DECOD are present by -PTCD. :)
    (: all the records in the same group then have the same values for -PTCD :)
    let $orderedrecords := (
        for $record in $datasetdoc//odm:ItemGroupData
            group by 
            $b := $record/odm:ItemData[@ItemOID=$ptcdoid]/@Value
            return element group {  
                $record
            }
    )
    (: we only need to iterate over the groups, and only take the first record in the group,
    as within the group, all the records have the same values for -PTCD :)
    for $group in $orderedrecords[$ptcdoid and $decodoid]
        let $decod1 := $group/odm:ItemGroupData[1]/odm:ItemData[@ItemOID=$decodoid]/@Value
        let $ptcd := $group/odm:ItemGroupData[1]/odm:ItemData[@ItemOID=$ptcdoid]/@Value
        let $recnum1 := $group/odm:ItemGroupData[1]/@data:ItemGroupDataSeq
		(: We must also take the case into account that -DECOD is not provided  :)
        for $record in $group[$decod1]/odm:ItemGroupData[position()>1]  (: start from the second one  :)
            let $recnum2 := $record/@data:ItemGroupDataSeq
            (: as we grouped, the value of PTCD is the same, but we need to check on -DECOD :)
            let $decod2 := $record/odm:ItemData[@ItemOID=$decodoid]/@Value
            (: return <test>{data($ptcd)} - {data($decod)} - {data($decod2)}</test> :)
            (: the two -DECOD values must be identical, as they have the same value for -PTCD :)
            where not($decod1 = $decod2)
            return <error rule="SD1287" dataset="{$name}" variable="{data($decodname)}" rulelastupdate="2020-08-08" recordnumber="{$recnum2}">Inconsistent value for {data($decodname)} within {data($ptcdname)}: Record {data($recnum2)} has {data($decodname)}={data($decod2)} whereas recordnumber={data($recnum1)} has {data($decodname)}={data($decod1)} for the same value of {data($ptcdname)}={data($ptcd)}</error>  
]]>
</rulexquery>
</sdsrule>

<sdsrule id="SD1288" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>Value for --PTCD must be consistent within --DECOD</ruledescription>
<ruledetaileddescription>All values of Preferred Term Code (--PTCD) should be the same for a given value of Dictionary-Derived Term (--DECOD)</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>EVENTS</domain>
<rulexquery><![CDATA[
(: Rule CG0050/FDA-SD1288 - Inconsistent value for --PTCD within --DECOD - 
 All values of Preferred Term Code (--PTCD) should be the same for a given value of Dictionary-Derived Term (--DECOD) :)
(: Made faster using "GROUP BY" :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' 
let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: Iterate over the EVENTS datasets :)
for $itemgroupdef in $definedoc//odm:ItemGroupDef[@def:Class='EVENTS' or ./def21:Class/@Name='EVENTS']
    let $name := $itemgroupdef/@Name
	let $datasetname := (
		if($defineversion = '2.1') then $itemgroupdef/def21:leaf/@xlink:href
		else $itemgroupdef/def:leaf/@xlink:href
	)
    let $datasetdoc := (
		if($datasetname) then doc(concat($base,$datasetname))
		else ()
	)
    (: and get the OID of --DECOD and --PTCD :)
    let $decodoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'DECOD')]/@OID 
        where $a = $itemgroupdef/odm:ItemRef/@ItemOID
        return $a 
    )
    let $decodname := $definedoc//odm:ItemDef[@OID=$decodoid]/@Name (: and the name (for reporting) :)
    let $ptcdoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'PTCD')]/@OID 
        where $a = $itemgroupdef/odm:ItemRef/@ItemOID
        return $a 
    )
    let $ptcdname := $definedoc//odm:ItemDef[@OID=$ptcdoid]/@Name (: and the name (for reporting) :)
    (: group all the records in the dataset (at least when -PTCD and -DECOD are present by -DECOD. :)
    (: all the records in the same group then have the same values for -DECOD :)
    let $orderedrecords := (
        for $record in $datasetdoc//odm:ItemGroupData
            group by 
            $b := $record/odm:ItemData[@ItemOID=$decodoid]/@Value
            return element group {  
                $record
            }

    )
    (: we only need to iterate over the groups, and only take the first record in the group,
    as within the group, all the records have the same values for -DECOD :)
    for $group in $orderedrecords[$ptcdoid and $decodoid]
        let $decod := $group/odm:ItemGroupData[1]/odm:ItemData[@ItemOID=$decodoid]/@Value
        let $ptcd1 := $group/odm:ItemGroupData[1]/odm:ItemData[@ItemOID=$ptcdoid]/@Value
        let $recnum1 := $group/odm:ItemGroupData[1]/@data:ItemGroupDataSeq
        for $record in $group/odm:ItemGroupData[position()>1]  (: start from the second one  :)
            let $recnum2 := $record/@data:ItemGroupDataSeq
            (: as we grouped, the value of PTCD is the same, but we need to check on -DECOD :)
            let $ptcd2 := $record/odm:ItemData[@ItemOID=$ptcdoid]/@Value
            (: the two -PTCD values must be identical, as they have the same value for -DECOD :)
            where not($ptcd1 = $ptcd2)
            return <error rule="SD1288" dataset="{$name}" variable="{data($decodname)}" rulelastupdate="2020-08-08" recordnumber="{$recnum2}">Inconsistent value for {data($ptcdname)} within {data($decodname)}: Record {data($recnum2)} has {data($ptcdname)}={data($ptcd2)} whereas recordnumber={data($recnum1)} has {data($ptcdname)}={data($ptcd1)} for the same value of {data($decodname)}={data($decod)}</error>  
]]>
</rulexquery>
</sdsrule>

<sdsrule id="SD1289" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>--DECOD must be populated, when --PTCD is populated</ruledescription>
<ruledetaileddescription>Dictionary-Derived Term (--DECOD) should be populated when Preferred Term Code (--PTCD) is populated</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>EVENTS</domain>
<rulexquery><![CDATA[
(: Rule SD1289 - Missing value for --DECOD, when --PTCD is populated - 
Dictionary-Derived Term (-DECOD) should be populated when Preferred Term Code (-PTCD) is populated :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' 
let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: Iterate over the EVENTS datasets :)
for $itemgroupdef in $definedoc//odm:ItemGroupDef[@def:Class='EVENTS' or ./def21:Class/@Name='EVENTS']
    let $name := $itemgroupdef/@Name
	let $datasetname := (
		if($defineversion = '2.1') then $itemgroupdef/def21:leaf/@xlink:href
		else $itemgroupdef/def:leaf/@xlink:href
	)
    let $datasetdoc := (
		if($datasetname) then doc(concat($base,$datasetname))
		else ()
	)
    (: and get the OID of --DECOD and --PTCD :)
    let $decodoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'DECOD')]/@OID 
        where $a = $itemgroupdef/odm:ItemRef/@ItemOID
        return $a 
    )
    let $decodname := $definedoc//odm:ItemDef[@OID=$decodoid]/@Name (: and the name (for reporting) :)
    let $ptcdoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'PTCD')]/@OID 
        where $a = $itemgroupdef/odm:ItemRef/@ItemOID
        return $a 
    )
    let $ptcdname := $definedoc//odm:ItemDef[@OID=$ptcdoid]/@Name (: and the name (for reporting) :)
    (: iterate over all records that have a value for -PTCD :)
    for $record in $datasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$ptcdoid]/@Value]
        let $recnum := $record/@data:ItemGroupDataSeq
        (: and check whether there is a -DECOD datapoint :)
        let $decod := $record/odm:ItemData[@ItemOID=$decodoid]/@Value
        (: DECOD must be populated :)
        where not($decod)
        return <warning rule="SD1289" dataset="{$name}" variable="{data($decodname)}" rulelastupdate="2020-08-08" recordnumber="{$recnum}">Missing value for {data($decodname)} with {data($ptcdname)}='{data($decod)}' is populated</warning>  
]]>
</rulexquery>
</sdsrule>

<sdsrule originator="FDA" id="SD1293" standard="SDTM" last-update="2020-08-08">
	<ruledescription>--REASND may not be present in dataset when --PRESP not present in dataset then (Interventions, Events, except for DS,DV,EX)</ruledescription>
	<ruledetaileddescription>Reason Not Done (--REASND) variable should not be present in the domain, when the Pre-specified (--PRESP) variable is missing</ruledetaileddescription>
	<igversion>3.1.2</igversion>
	<igversion>3.1.3</igversion>
	<igversion>3.2</igversion>
	<domain>INTERVENTIONS</domain>
	<domain>EVENTS</domain>
	<synonym context="CDISC">CG0053</synonym>
	<rulexquery><![CDATA[
(: Rule CG0053/FDA-SD1289: Reason Not Done (--REASND) variable should not be present in the domain, when the Pre-specified (--PRESP) variable is missing
 Applies to: EVENTS,INTERVENTIONS, NOT(DS, DV, EX) :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: Applies to: EVENTS,INTERVENTIONS, NOT(DS, DV, EX) :)
for $itemgroupdef in $definedoc//odm:ItemGroupDef[upper-case(@def:Class)='INTERVENTIONS' or upper-case(@def:Class)='EVENTS' or upper-case(./def21:Class/@Name)='INTERVENTIONS' or upper-case(./def21:Class/@Name)='EVENTS'][not(@Domain='DS') and not(@Domain='DV') and not(@Domain='EX')]
    let $name := $itemgroupdef/@Name
    (: find the OID and name of the --PRESP variable /(if any) :)
    let $prespoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'PRESP')]/@OID 
        where $a = $itemgroupdef/odm:ItemRef/@ItemOID
        return $a
    )
    let $prespname := $definedoc//odm:ItemDef[OID=$prespoid]/@Name
    (: and of the --REASND variable :)
    let $reasndoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'REASND')]/@OID 
        where $a = $itemgroupdef/odm:ItemRef/@ItemOID
        return $a
    )
    let $reasndname := $definedoc//odm:ItemDef[@OID=$reasndoid]/@Name
    (: when --PRESP is absent, --REASND must be absent :)
    where not($prespoid) and $reasndoid
    return <error rule="SD1293" variable="{data($reasndname)}" dataset="{$name}" rulelastupdate="2020-08-08">{data($reasndname)} is present although {data($prespname)} is absent</error>			
	]]></rulexquery>
</sdsrule> 

<sdsrule originator="FDA" standard="SDTM" id="SD1294" last-update="2020-08-08">
	<ruledescription>--STAT may not present in dataset when --PRESP not present in dataset then (Interventions,Events, except for DS,DV,EX)</ruledescription>
	<ruledetaileddescription>Completion Status (--STAT) variable should not be present in the domain, when the Pre-specified (--PRESP) variable is missing</ruledetaileddescription>
	<igversion>3.1.2</igversion>
	<igversion>3.1.3</igversion>
	<igversion>3.2</igversion>
	<domain>INTERVENTIONS</domain>
	<domain>EVENTS</domain>
	<synonym context="CDISC">CG0056</synonym>
	<rulexquery><![CDATA[
(: Rule CG0056/FDA-SD1294 when --PRESP not present in dataset then --STAT not present in dataset
 Applies to: EVENTS,INTERVENTIONS, NOT(DS, DV, EX) :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: Applies to: EVENTS,INTERVENTIONS, NOT(DS, DV, EX) :)
for $itemgroupdef in $definedoc//odm:ItemGroupDef[upper-case(@def:Class)='INTERVENTIONS' or upper-case(@def:Class)='EVENTS' or upper-case(./def21:Class/@Name)='INTERVENTIONS' or upper-case(./def21:Class/@Name)='EVENTS'][not(@Domain='DS') and not(@Domain='DV') and not(@Domain='EX')]
    let $name := $itemgroupdef/@Name
    (: find the OID and name of the --PRESP variable /(if any) :)
    let $prespoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'PRESP')]/@OID 
        where $a = $itemgroupdef/odm:ItemRef/@ItemOID
        return $a
    )
    let $prespname := $definedoc//odm:ItemDef[OID=$prespoid]/@Name
    (: and of the --STAT variable :)
    let $statoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'STAT')]/@OID 
        where $a = $itemgroupdef/odm:ItemRef/@ItemOID
        return $a
    )
    let $statname := $definedoc//odm:ItemDef[@OID=$statoid]/@Name
    (: when --PRESP is absent, --REASND must be absent :)
    where not($prespoid) and $statoid
    return <error rule="SD1294" variable="{data($statname)}" dataset="{$name}" rulelastupdate="2020-08-08">{data($statname)} is present although {data($prespname)} is absent</error>				
	]]></rulexquery>
</sdsrule>

<sdsrule id="SD1102" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>--ENRTPT variable must be present, when --ENTPT variable is present</ruledescription>
<ruledetaileddescription>End Reference to Reference Time Point (--ENRTPT) variable must be included into the domain, when End Relative Time Point (--ENTPT) variable is present</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>ALL</domain>
<rulexquery><![CDATA[	
(: TODO: not well tested yet :)       
(: Rule SD1102 - Missing --ENRTPT variable, when --ENTPT variable is present
End Reference to Reference Time Point (--ENRTPT) variable must be included into the domain, when End Relative Time Point (--ENTPT) variable is present
:) 
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink"; 
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/'  :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: iterate over all datasets except for DM :)
for $dataset in $definedoc//odm:ItemGroupDef[not(@Name='DM')]
    let $name := $dataset/@Name
    (: Get the OID of the ENRTPT and ENTPT variable (when present) :)
    let $enrtptoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'ENRTPT')]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
    )
    let $enrtptname := concat($name,'ENRTPT')
    let $entptoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'ENTPT')]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
    )
    let $entptname := $definedoc//odm:ItemDef[@OID=$entptoid]/@Name
    (: when ENTPT is present, also ENRTPT must be present :)
    where $entptoid and not($enrtptoid)
    return <error rule="SD1102" variable="{data($enrtptname)}" dataset="{data($name)}" rulelastupdate="2020-08-08">Missing {data($enrtptname)}  variable, when {data($entptname)} variable is present</error>
]]></rulexquery>
</sdsrule>

<sdsrule originator="FDA" id="SD1292" standard="SDTM" last-update="2020-08-08">
	<ruledescription>--STRTPT must be populated, when --STTPT is populated</ruledescription>
	<ruledetaileddescription>Start Relative to Reference Time Point (--STRTPT) must be populated when Start Reference Time Point (--STTPT) is populated</ruledetaileddescription>
	<igversion>3.1.2</igversion>
	<igversion>3.1.3</igversion>
	<igversion>3.2</igversion>
	<domain>ALL</domain>
	<rulexquery><![CDATA[
(: Rule CG0059/FDA-SD1292 Missing value for --STRTPT, when --STTPT is populated :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external; 
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: Iterate over all datasets defined in the define.xml :)
for $itemgroupdef in $definedoc//odm:ItemGroupDef
    let $name := $itemgroupdef/@Name
    let $domain := (
        if($itemgroupdef/@Domain) then $itemgroupdef/@Domain
        else $name
    )
    (: find the OID and name of the --STTPT variable /(if any) :)
    let $sttptoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'STTPT')]/@OID 
        where $a = $itemgroupdef/odm:ItemRef/@ItemOID
        return $a
    )
    let $sttptname := $definedoc//odm:ItemDef[@OID=$sttptoid]/@Name
    (: and of the --STRTPT variable :)
    let $strtptoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'STRTPT')]/@OID 
        where $a = $itemgroupdef/odm:ItemRef/@ItemOID
        return $a
    )
    let $strtptname := $definedoc//odm:ItemDef[@OID=$strtptoid]/@Name
    (: get the location of the dataset :)
	let $dataset := (
		if($defineversion = '2.1') then $itemgroupdef/def21:leaf/@xlink:href
		else $itemgroupdef/def:leaf/@xlink:href
	)
    let $datasetdoc := (
		if($dataset) then doc(concat($base,$dataset))
		else ()
	)
    (: iterate over all the records that do have an --STTPT record, but only for those datasets for which --STTPT and --STRTPT have been defined :)
    for $record in $datasetdoc[$sttptoid and $strtptoid]//odm:ItemGroupData[odm:ItemData[@ItemOID=$sttptoid]]
        let $recnum := $record/@data:ItemGroupDataSeq
        (: get the value of the --STTPT variable :)
        let $sttptvalue := $record/odm:ItemData[@ItemOID=$sttptoid]/@Value
        (: get the value of the --STRTPT variable (if any) :)
        let $strtptvalue := $record/odm:ItemData[@ItemOID=$strtptoid]/@Value
        (: give an error when there is a --STTPT value but no --STRTPT value :)
        where $sttptvalue and not($strtptvalue)
    return <error rule="SD1292" variable="{data($strtptname)}" dataset="{$name}" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">No value for {data($strtptname)} was found although there is a value for {data($sttptname)}={data($sttptvalue)}</error>			
	]]></rulexquery>
</sdsrule>

<sdsrule originator="FDA" id="SD2238" standard="SDTM" last-update="2020-08-08">
	<ruledescription>Value for --STTPT value must be consistent within --STDTC and USUBJID</ruledescription>
	<ruledetaileddescription>Start Reference Time Point (--STTPT) value must be consistent for all records with same Subject (USUBJID) and Start Date/Time (--STDTC) values</ruledetaileddescription>
	<igversion>3.1.2</igversion>
	<igversion>3.1.3</igversion>
	<igversion>3.2</igversion>
	<domain>INTERVENTIONS</domain>
	<domain>EVENTS</domain>
	<rulexquery><![CDATA[
(:  Rule SD2238: Inconsistent value for --STTPT - Start Reference Time Point (--STTPT) value must be consistent for all records with same Subject (USUBJID) and Start Date/Time (--STDTC) values :)
(: Applies to INTERVENTIONS, EVENTS :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external;
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' 
let $define := 'define2-0-0-example-sdtm.xml' :)
(: get the define.xml document :)
let $definedoc := doc(concat($base,$define))
(: iterate over all INTERVENTIONS and EVENTS datasets :)
for $datasetdef in $definedoc//odm:ItemGroupDef[upper-case(@def:Class)='INTERVENTIONS' or upper-case(@def:Class)='EVENTS' 
		or upper-case(./def21:Class/@Name)='INTERVENTIONS' or upper-case(./def21:Class/@Name)='EVENTS']
    (: get the name :)
    let $name := $datasetdef/@Name
    (: get the dataset location and the dataset document itself :)
    let $datasetloc := (
		if($defineversion = '2.1') then $datasetdef/def21:leaf/@xlink:href
		else $datasetdef/def:leaf/@xlink:href
	)
    let $datasetdoc := (
		if($datasetloc) then doc(concat($base,$datasetloc))
		else ()
	)
    (: we need the OID of USUBJID, --STTPT and --STDTC :)
    let $usubjidoid := (
        for $a in $definedoc//odm:ItemDef[@Name='USUBJID']/@OID 
            where $a = $datasetdef/odm:ItemRef/@ItemOID
        return $a
    )
    let $sttptoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'STTPT')]/@OID 
            where $a = $datasetdef/odm:ItemRef/@ItemOID
        return $a
    )
    let $sttptname := $definedoc//odm:ItemDef[@OID=$sttptoid]/@Name
    let $stdtcoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'STDTC')]/@OID 
            where $a = $datasetdef/odm:ItemRef/@ItemOID
        return $a
    )
    let $stdtcname := $definedoc//odm:ItemDef[@OID=$stdtcoid]/@Name
    (: return <test>{data($usubjidoid)} - {data($sttptoid)} - {data($stdtcoid)}</test> :)
    (: group the records by USUBJID and --STDTC - 
    all the records in each group will have the same value for USUBJID and --STDTC:)
    let $orderedrecords := (
        for $record in $datasetdoc//odm:ItemGroupData
            group by 
            $b := $record/odm:ItemData[@ItemOID=$usubjidoid]/@Value,
            $c := $record/odm:ItemData[@ItemOID=$stdtcoid]/@Value
            return element group {  
                $record
            }
    )
    (: Each group now has the same value for USUBJID and --STDTC - 
    iterate over all the groups what DO have a value for USUBJID, --STTPT and --STDTC:)
    for $group in $orderedrecords[$usubjidoid and $stdtcoid and $sttptoid]
        (: get the values of USUBJID and --STDTC :)
        let $usubjid := $group/odm:ItemGroupData[1]/odm:ItemData[@ItemOID=$usubjidoid]/@Value
        let $stdtc := $group/odm:ItemGroupData[1]/odm:ItemData[@ItemOID=$stdtcoid]/@Value
        (: and the value of --STTPT and then record number:)
        let $sttpt1 := $group/odm:ItemGroupData[1]/odm:ItemData[@ItemOID=$sttptoid]/@Value
        let $recnum1 := $group/odm:ItemGroupData[1]/@data:ItemGroupDataSeq
        (: now iterate over all other records in the same group: 
        the value of --STTPT should be the same as in the first record in the group :)
        for $record in $group/odm:ItemGroupData[position()>1]
            (: get the value of --STTPT and the record number :)
            let $sttpt2 := $record/odm:ItemData[@ItemOID=$sttptoid]/@Value
            let $recnum2 := $record/@data:ItemGroupDataSeq
            (: the two --STTPT values must be equal :)
            where not($sttpt1 = $sttpt2)
            return <error rule="SD2238" dataset="{$name}" variable="{data($sttptname)}" rulelastupdate="2020-08-08" recordnumber="{$recnum2}">Inconsistent value for {data($sttptname)}: Record {data($recnum2)} has {data($sttptname)}={data($sttpt2)} whereas recordnumber={data($recnum1)} has {data($sttptname)}={data($sttpt1)} for the same value of USUBJID='{data($usubjid)}' and {data($stdtcname)}='{data($stdtc)}'</error>  	
	]]>
	</rulexquery>
</sdsrule>

<sdsrule originator="FDA" id="SD1104" standard="SDTM" last-update="2020-08-08">
	<ruledescription>--STRTPT variable must be present, when --STTPT variable is present</ruledescription>
	<ruledetaileddescription>Start Relative to Reference Time Point (--STRTPT) variable must be included into the domain, when Start Relative Time Point (--STTPT) variable is present</ruledetaileddescription>
	<igversion>3.1.2</igversion>
	<igversion>3.1.3</igversion>
	<igversion>3.2</igversion>
	<domain>INTERVENTIONS</domain>
	<domain>EVENTS</domain>
	<domain>FINDINGS</domain>
	<domain>SE</domain>
	<synonym context="CDISC">CG0060</synonym>
	<rulexquery><![CDATA[
(: Rule CG0060/SD1104 Missing --STRTPT variable, when --STTPT variable is present - Start Relative to Reference Time Point (--STRTPT) variable must be included into the domain, when Start Relative Time Point (--STTPT) variable is present :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external; 
declare variable $define external;
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: Iterate over all datasets defined in the define.xml within INTERVENTIONS, EVENTS, FINDINGS, SE :)
for $itemgroupdef in $definedoc//odm:ItemGroupDef[upper-case(@def:Class)='INTERVENTIONS' or upper-case(@def:Class)='EVENTS' or upper-case(@def:Class)='FINDINGS' 
		or upper-case(./def21:Class/@Name)='INTERVENTIONS' or upper-case(./def21:Class/@Name)='EVENTS' or upper-case(./def21:Class/@Name)='FINDINGS'
		or @Domain='SE']
    let $name := $itemgroupdef/@Name
    let $domain := (
        if($itemgroupdef/@Domain) then $itemgroupdef/@Domain
        else $name
    )
    (: find the OID and name of the --STTPT variable /(if any) :)
    let $sttptoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'STTPT')]/@OID 
        where $a = $itemgroupdef/odm:ItemRef/@ItemOID
        return $a
    )
    let $sttptname := $definedoc//odm:ItemDef[@OID=$sttptoid]/@Name
    (: and of the --STRTPT variable :)
    let $strtptoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'STRTPT')]/@OID 
        where $a = $itemgroupdef/odm:ItemRef/@ItemOID
        return $a
    )
    let $strtptname := concat($domain,'STRTPT')  (: as when --TRTPT is absent, we also do now its name :)
    (: when --STTPT present in dataset then --STRTPT must present in dataset :)
    where $sttptoid and not($strtptoid)
    return <error rule="SD1104" variable="{data($strtptname)}" dataset="{$name}" rulelastupdate="2020-08-08">{data($strtptname)} is not present although {data($sttptname)} is present</error>			
	]]></rulexquery>
</sdsrule>

<sdsrule originator="FDA" id="SD1103" standard="SDTM" last-update="2020-08-08">
	<ruledescription>--STTPT variable must be present, when --STRTPT variable is present</ruledescription>
	<ruledetaileddescription>Start Reference Time Point (--STTPT) variable must be included into the domain, when Start Relative to Reference Time Point (--STRTPT) variable is present</ruledetaileddescription>
	<igversion>3.1.2</igversion>
	<igversion>3.1.3</igversion>
	<igversion>3.2</igversion>
	<domain>INTERVENTIONS</domain>
	<domain>EVENTS</domain>
	<domain>FINDINGS</domain>
	<domain>SE</domain>
	<synonym context="CDISC">CG0061</synonym>
	<rulexquery><![CDATA[
(: Rule SD1103 Missing --STTPT variable, when --STRTPT variable is present - Start Reference Time Point (--STTPT) variable must be included into the domain, when Start Relative to Reference Time Point (--STRTPT) variable is present :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external; 
declare variable $define external;
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: Iterate over all datasets defined in the define.xml within INTERVENTIONS, EVENTS, FINDINGS, SE :)
for $itemgroupdef in $definedoc//odm:ItemGroupDef[upper-case(@def:Class)='INTERVENTIONS' or upper-case(@def:Class)='EVENTS' or upper-case(@def:Class)='FINDINGS' or @Domain='SE']
    let $name := $itemgroupdef/@Name
    let $domain := (
        if($itemgroupdef/@Domain) then $itemgroupdef/@Domain
        else $name
    )
    (: find the OID and name of the --STTPT variable /(if any) :)
    let $sttptoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'STTPT')]/@OID 
        where $a = $itemgroupdef/odm:ItemRef/@ItemOID
        return $a
    )
    let $sttptname := $definedoc//odm:ItemDef[@OID=$sttptoid]/@Name
    (: and of the --STRTPT variable :)
    let $strtptoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'STRTPT')]/@OID 
        where $a = $itemgroupdef/odm:ItemRef/@ItemOID
        return $a
    )
    let $strtptname := concat($domain,'STRTPT')  (: as when --TRTPT is absent, we also do now its name :)
    (: when --STTPT present in dataset then --STRTPT must present in dataset :)
    where $strtptoid and not($sttptoid)
    return <error rule="SD1103" variable="{data($sttptname)}" dataset="{$name}" rulelastupdate="2020-08-08">{data($sttptname)} is not present although {data($strtptname)} is present</error>			
]]>
</rulexquery>
</sdsrule>	

<sdsrule originator="FDA" id="SD1313" standard="SDTM" last-update="2020-08-08">
	<ruledescription>EPOCH value must be provided, when DSCAT is 'DISPOSITION EVENT'</ruledescription>
	<ruledetaileddescription>Epoch (EPOCH) variable should be provided, when Category for Disposition Event (DSCAT) equals 'DISPOSITION EVENT'</ruledetaileddescription>
	<igversion>3.1.2</igversion>
	<igversion>3.1.3</igversion>
	<igversion>3.2</igversion>
	<domain>DS</domain>
	<rulexquery><![CDATA[
(: Rule SD1313: Missing EPOCH value, when DSCAT is 'DISPOSITION EVENT' - Epoch (EPOCH) variable should be provided, when Category for Disposition Event (DSCAT) equals 'DISPOSITION EVENT' :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external; 
declare variable $define external;
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' 
let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: get the DS dataset :)
for $dsdatasetdef in $definedoc//odm:ItemGroupDef[@Name='DS' or @Domain='DS']
    let $name := $dsdatasetdef/@Name (: for the ultimate case of DS splitted datasets :)
    (: get the location and the dataset document itself :)
    let $datasetlocation := (
		if($defineversion = '2.1') then $dsdatasetdef/def21:leaf/@xlink:href
		else $dsdatasetdef/def:leaf/@xlink:href
	)
    let $datasetdoc := (
		if($datasetlocation) then doc(concat($base,$datasetlocation))
		else ()
	)
    (: get the OID of DSCAT and of EPOCH in DS  :)
    let $dscatoid := (
        for $a in $definedoc//odm:ItemDef[@Name='DSCAT']/@OID 
        where $a = $dsdatasetdef/odm:ItemRef/@ItemOID
        return $a
    )
    let $epochoid := (
        for $a in $definedoc//odm:ItemDef[@Name='EPOCH']/@OID 
        where $a = $dsdatasetdef/odm:ItemRef/@ItemOID
        return $a
    )
    (: iterate over all records in the dataset for which DSCAT = 'DISPOSITION EVENT' :)
    for $record in $datasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$dscatoid and @Value='DISPOSITION EVENT']]
        let $recnum := $record/@data:ItemGroupDataSeq
        (: get the EPOCH value :)
        let $epoch := $record/odm:ItemData[@ItemOID=$epochoid]/@Value
        (: EPOCH value may not be null  :)
        where not($epoch) 
        return <error rule="SD1313" variable="EPOCH" dataset="{$name}" recordnumber="{$recnum}" rulelastupdate="2020-08-08">Missing EPOCH value, although DSCAT is 'DISPOSITION EVENT'</error>				
	]]>
	</rulexquery>
</sdsrule>

<sdsrule originator="FDA" id="SD1314" standard="SDTM" last-update="2020-08-08">
	<ruledescription>DSDECOD must be 'COMPLETED', when DSTERM='COMPLETED'</ruledescription>
	<ruledetaileddescription>When Reported Term for the Disposition Event (DSTERM) is 'COMPLETED', Standardized Disposition Term (DSDECOD) should have the same value 'COMPLETED'</ruledetaileddescription>
	<igversion>3.1.2</igversion>
	<igversion>3.1.3</igversion>
	<igversion>3.2</igversion>
	<domain>DS</domain>
	<rulexquery><![CDATA[
(: Rule SD1314: DSDECOD is not 'COMPLETED', when DSTERM='COMPLETED' - When Reported Term for the Disposition Event (DSTERM) is 'COMPLETED', Standardized Disposition Term (DSDECOD) should have the same value 'COMPLETED' :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external; 
declare variable $define external;
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' 
let $define := 'define2-0-0-example-sdtm.xml'  :)
let $definedoc := doc(concat($base,$define))
(: get the DS dataset :)
for $dsdatasetdef in $definedoc//odm:ItemGroupDef[@Name='DS' or @Domain='DS']
    let $name := $dsdatasetdef/@Name (: for the ultimate case of DS splitted datasets :)
    (: get the location and the dataset document itself :)
	let $datasetlocation := (
		if($defineversion = '2.1') then $dsdatasetdef/def21:leaf/@xlink:href
		else $dsdatasetdef/def:leaf/@xlink:href
	)
    let $datasetdoc := (
		if($datasetlocation) then doc(concat($base,$datasetlocation))
		else ()
	)
    (: get the OID of DSDECOD and of DSTERM in DS  :)
    let $dsdecodoid := (
        for $a in $definedoc//odm:ItemDef[@Name='DSDECOD']/@OID 
        where $a = $dsdatasetdef/odm:ItemRef/@ItemOID
        return $a
    )
    let $dstermoid := (
        for $a in $definedoc//odm:ItemDef[@Name='DSTERM']/@OID 
        where $a = $dsdatasetdef/odm:ItemRef/@ItemOID
        return $a
    )
    (: iterate over all records in the dataset for which DSTERM = 'COMPLETED' :)
    for $record in $datasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$dstermoid and @Value='COMPLETED']]
        let $recnum := $record/@data:ItemGroupDataSeq
        (: get the DSDECOD value :)
        let $dsdecod := $record/odm:ItemData[@ItemOID=$dsdecodoid]/@Value
        (: DSDECOD must also be "COMPLETED"  :)
        where not($dsdecod = 'COMPLETED')
        return <error rule="SD1314" variable="DSDECOD" dataset="{$name}" recordnumber="{$recnum}" rulelastupdate="2020-08-08">Value for DSDECOD='{data($dsdecod)}' must be 'COMPLETED', as DSTERM='COMPLETED'</error>				
	]]>
	</rulexquery>
</sdsrule>

<sdsrule originator="FDA" id="SD1315" standard="SDTM" last-update="2020-08-08">
	<ruledescription>DSDECOD must be the same as DSTERM, when DSCAT='PROTOCOL MILESTONE'</ruledescription>
	<ruledetaileddescription>Reported Term for the Disposition Event (DSTERM) and Standardized Disposition Term (DSDECOD) should contain the same value, when Category for Disposition Event (DSCAT) is 'PROTOCOL MILESTONE'</ruledetaileddescription>
	<domain>DS</domain>
	<igversion>3.1.2</igversion>
	<igversion>3.1.3</igversion>
	<igversion>3.2</igversion>
	<rulexquery><![CDATA[
(: Rule SD1315: DSDECOD is not the same as DSTERM, when DSCAT='PROTOCOL MILESTONE' - Reported Term for the Disposition Event (DSTERM) and Standardized Disposition Term (DSDECOD) should contain the same value, when Category for Disposition Event (DSCAT) is 'PROTOCOL MILESTONE' :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external; 
declare variable $define external;
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' 
let $define := 'define2-0-0-example-sdtm.xml'  :)
let $definedoc := doc(concat($base,$define))
(: get the DS dataset :)
for $dsdatasetdef in $definedoc//odm:ItemGroupDef[@Name='DS' or @Domain='DS']
    let $name := $dsdatasetdef/@Name (: for the ultimate case of DS splitted datasets :)
    (: get the location and the dataset document itself :)
	let $datasetlocation := (
		if($defineversion = '2.1') then $dsdatasetdef/def21:leaf/@xlink:href
		else $dsdatasetdef/def:leaf/@xlink:href
	)
    let $datasetdoc := (
		if($datasetlocation) then doc(concat($base,$datasetlocation))
		else ()
	)
    (: get the OID of DSCAT, DSDECOD and of DSTERM in DS  :)
    let $dscatoid := (
        for $a in $definedoc//odm:ItemDef[@Name='DSCAT']/@OID 
        where $a = $dsdatasetdef/odm:ItemRef/@ItemOID
        return $a
    )
    let $dsdecodoid := (
        for $a in $definedoc//odm:ItemDef[@Name='DSDECOD']/@OID 
        where $a = $dsdatasetdef/odm:ItemRef/@ItemOID
        return $a
    )
    let $dstermoid := (
        for $a in $definedoc//odm:ItemDef[@Name='DSTERM']/@OID 
        where $a = $dsdatasetdef/odm:ItemRef/@ItemOID
        return $a
    )
    (: iterate over all records in the dataset for which DSCAT = 'PROTOCOL MILESTONE' :)
    for $record in $datasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$dscatoid and @Value='PROTOCOL MILESTONE']]
        let $recnum := $record/@data:ItemGroupDataSeq
        (: get the DSDECOD value :)
        let $dsdecod := $record/odm:ItemData[@ItemOID=$dsdecodoid]/@Value
        (: and the DSTERM value :)
        let $dsterm := $record/odm:ItemData[@ItemOID=$dstermoid]/@Value
        (: DSDECOD must be eqaul to DSTERM :)
        where not($dsdecod = $dsterm)
        return <error rule="SD1315" variable="DSDECOD" dataset="{$name}" recordnumber="{$recnum}" rulelastupdate="2020-08-08">Value for DSDECOD='{data($dsdecod)}' is not equal to value for DSTERM='{data($dsterm)}', as DSCAT='PROTOCOL MILESTONE'</error>				
	]]>
	</rulexquery>
</sdsrule>

<!-- TODO: further testing - no good testing was possible due to lack of a suitable example dataset -->
<sdsrule originator="FDA" id="SD1316" standard="SDTM" last-update="2020-08-08">
	<ruledescription>'DEATH' record for subject msut be present in DS domain when SSSTRESC='DEAD'</ruledescription>
	<ruledetaileddescription>All subjects who have Death records (SSSTRESC = 'DEAD') in Survival Status (SS) domain should also have death records (DSDECOD='DEATH') in Disposition (DS) domain</ruledetaileddescription>
	<igversion>3.2</igversion>
	<domain>SS</domain>
	<domain>DS</domain>
	<rulexquery><![CDATA[
(: Rule SD1316: Missing 'DEATH' record for subject in DS domain - All subjects who have Death records (SSSTRESC = 'DEAD') in Survival Status (SS) domain should also have death records (DSDECOD='DEATH') in Disposition (DS) domain :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external; 
declare variable $define external;
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' 
let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: get the DS dataset :)
let $dsdatasetdef := $definedoc//odm:ItemGroupDef[@Name='DS' or @Domain='DS']
let $name := $dsdatasetdef/@Name (: for the ultimate case of DS splitted datasets :)
(: get the location and the dataset document itself :)
let $dsdatasetlocation := (
	if($defineversion = '2.1') then $dsdatasetdef/def21:leaf/@xlink:href
	else $dsdatasetdef/def:leaf/@xlink:href
)
let $dsdatasetdoc := doc(concat($base,$dsdatasetlocation))
(: get the OID of USUBJID and DSDECOD :)
let $dsusubjidoid := (
    for $a in $definedoc//odm:ItemDef[@Name='USUBJID']/@OID 
    where $a = $dsdatasetdef/odm:ItemRef/@ItemOID
    return $a
)
let $dsdecodoid := (
    for $a in $definedoc//odm:ItemDef[@Name='DSDECOD']/@OID 
    where $a = $dsdatasetdef/odm:ItemRef/@ItemOID
    return $a
)
(: get the SS (Survival Status) dataset(s) :)
for $ssdatasetdef in $definedoc//odm:ItemGroupDef[@Domain='SS']
    let $ssname := $ssdatasetdef/@Name
    (: get the dataset location and the document itself :)
    let $ssdatasetloc := (
		if($defineversion = '2.1') then $ssdatasetdef/def21:leaf/@xlink:href
		else $ssdatasetdef/def:leaf/@xlink:href
	)
    let $ssdatasetdoc := (
		if($ssdatasetloc) then doc(concat($base,$ssdatasetloc))
		else ()
	)
    (: we need the OID of USUBJID and SSSTRESC in SS :)
    let $ssusubjidoid := (
        for $a in $definedoc//odm:ItemDef[@Name='USUBJID']/@OID 
        where $a = $ssdatasetdef/odm:ItemRef/@ItemOID
        return $a
    )
    let $ssstrescoid := (
        for $a in $definedoc//odm:ItemDef[@Name='SSSTRESC']/@OID 
        where $a = $ssdatasetdef/odm:ItemRef/@ItemOID
        return $a
    )
    (: iterate over all records in the SS dataset for which SSSTRESC = 'DEAD' :)
    for $record in $ssdatasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$ssstrescoid and @Value='DEAD']]
        let $recnum := $record/@data:ItemGroupDataSeq
        (: get the USUBJID value :)
        let $ssusubjid := $record/odm:ItemData[@ItemOID=$ssusubjidoid]/@Value
        (: count the number of records in DS for which DSDECOD='DEATH' :)
        let $count := count($dsdatasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$ssusubjidoid and @Value=$ssusubjid] and odm:ItemData[@ItemOID=$dsdecodoid and @Value='DEATH']])
        (: there must be such a record :)
        where $count = 0
        return <error rule="SD1316" variable="SSSTRESC" dataset="{$ssname}" recordnumber="{$recnum}" rulelastupdate="2020-08-08">No record was found in DS with DSDECOD='DEATH' for the record in dataset {data($ssname)} with SSSTRESC='DEAD' for subject with USUBJID='{data($ssusubjid)}'</error>				
	]]>
	</rulexquery>
</sdsrule>

<!-- TODO: further testing - no good testing was possible due to lack of a suitable example dataset -->
<sdsrule originator="FDA" id="SD1319" standard="SDTM" last-update="2020-08-08">
	<ruledescription>--STDTC may not be before RFICDTC</ruledescription>
	<ruledetaileddescription>'All records in Disposition Event (DS) and Protocol Diviation (DV) domains should have Start Date/Time of  Event (--STDTC) equal or after subject's Date/Time of Informed Consent (RFICDTC) in Demographics (DM) domain</ruledetaileddescription>
	<igversion>3.1.3</igversion>
	<igversion>3.2</igversion>
	<domain>DS</domain>
	<domain>DV</domain>
	<rulexquery><![CDATA[
(: Rule SD1319: --STDTC is before RFICDTC - 'All records in Disposition Event (DS) and Protocol Diviation (DV) domains should have Start Date/Time of  Event (--STDTC) equal or after subject's Date/Time of Informed Consent (RFICDTC) in Demographics (DM) domain :)
(: ONLY applies to DV and DS :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external; 
declare variable $define external;
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' 
let $define := 'define2-0-0-example-sdtm.xml'  :)
let $definedoc := doc(concat($base,$define))
(: get the DM dataset :)
let $dmdatasetdef := $definedoc//odm:ItemGroupDef[@Name='DM']
(: and the location and the document itself :)
let $dmdatasetloc := (
	if($defineversion = '2.1') then $dmdatasetdef/def21:leaf/@xlink:href
	else $dmdatasetdef/def:leaf/@xlink:href
)
let $dmdatasetdoc := doc(concat($base,$dmdatasetloc))
(:  get the OIDs of USUBJID and RFICDTC in DM :)
let $dmusubjidoid := (
    for $a in $definedoc//odm:ItemDef[@Name='USUBJID']/@OID 
        where $a = $dmdatasetdef/odm:ItemRef/@ItemOID
        return $a
)
let $dmrficdtcoid := (
    for $a in $definedoc//odm:ItemDef[@Name='RFICDTC']/@OID 
        where $a = $dmdatasetdef/odm:ItemRef/@ItemOID
        return $a
)
(: now create a temporary structure containing the USUBJID-RFICDTC pairs :)
let $rficdtcpairs := (
    for $record in $dmdatasetdoc//odm:ItemGroupData
        let $dmusubjid := $record/odm:ItemData[@ItemOID=$dmusubjidoid]/@Value
        let $rficdtc := $record/odm:ItemData[@ItemOID=$dmrficdtcoid]/@Value
        (: make it a datetime in the case it isn't one yet,
        as -STDTC variables in DS and DV may be datetimes or partialdatetimes:)
        let $rficdtcdt := (
            (: only the year is given - essentially, this should not be allowed :)
            if(string-length($rficdtc) = 4) then concat($rficdtc,'-01-01T00:00:00')
            (: only the year and month is given - essentially, this should not be allowed :)
            else if(string-length($rficdtc) = 7) then concat($rficdtc,'-01T00:00:00')
            (: complete date is given but no time :)
             else if(string-length($rficdtc) = 10) then concat($rficdtc,'T00:00:00')
            (: T is given but no hours - might bei invalid anyway :)
            else if(string-length($rficdtc) = 11) then concat($rficdtc,'00:00:00')
            (: only hour is given :)
            else if(string-length($rficdtc) = 13) then concat($rficdtc,':00:00')
            (: hour and minutes are given, but no seconds :)
            else if(string-length($rficdtc) = 16) then concat($rficdtc,':00')
            (: all ok anyway :)
            else ($rficdtc)
        )
        return <rficdtcpair usubjid="{data($dmusubjid)}" rficdtc="{data($rficdtcdt)}" />
)
(: get the DS and DV dataset :)
for $datasetdef in $definedoc//odm:ItemGroupDef[@Name='DS' or @Domain='DS' or @Name='DV' or @Domain='DV']
    let $name := $datasetdef/@Name
    (: get the location and the dataset document itself :)
    let $datasetloc := (
		if($defineversion = '2.1') then $datasetdef/def21:leaf/@xlink:href
		else $datasetdef/def:leaf/@xlink:href
	)
    let $datasetdoc := (
		if($datasetloc) then doc(concat($base,$datasetloc))
		else ()
	)
    (: get the OID of USUBJID and --STDTC :)
    let $usubjidoid := (
        for $a in $definedoc//odm:ItemDef[@Name='USUBJID']/@OID 
        where $a = $datasetdef/odm:ItemRef/@ItemOID
        return $a
    )
    let $stdtcoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'STDTC')]/@OID 
        where $a = $datasetdef/odm:ItemRef/@ItemOID
        return $a
    )
    (: and the full name of --STDTC :)
    let $stdtcname := $definedoc//odm:ItemDef[@OID=$stdtcoid]/@Name
    (: iterate over all the records that do have a value for --STDTC :)
    for $record in $datasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$stdtcoid]/@Value]
        let $recnum := $record/@data:ItemGroupDataSeq
        (: get the values of USUBJID and -STDTC :)
        let $usubjid := $record/odm:ItemData[@ItemOID=$usubjidoid]/@Value
        let $stdtc := $record/odm:ItemData[@ItemOID=$stdtcoid]/@Value
        (: and look them up in the DM-USUBJID-RFICDTC structure, 
        retrieving the value for RFICDTC for the given subject :)
        let $rficdtc := $rficdtcpairs[@usubjid=$usubjid]/@rficdtc
        (: -DTC must be after RFICDTC - but first need to check for the correct datatype :)
        where $rficdtc castable as xs:dateTime and $stdtc castable as xs:dateTime and xs:dateTime($stdtc) < xs:dateTime($rficdtc) 
        return <error rule="SD1319" dataset="{data($name)}" variable="{data($stdtcname)}" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">{data($stdtcname)}={data($stdtc)} is before DM.RFICDTC={data($rficdtc)} in dataset {data($name)}</error>		
	]]>
	</rulexquery>
</sdsrule>

<!-- REMARK: this rule was extensively tested on files from the second CDISC-FDA pilot (LZZT study) -->
<sdsrule originator="FDA" id="SD1317" standard="SDTM" last-update="2020-08-08">
	<ruledescription>DSSTDTC for DEATH record must be the same as DM.DTHDTC</ruledescription>
	<ruledetaileddescription>DEATH records (DSDECOD='DEATH') in Disposition (DS) domain should have Start Date of Event (DSDTDTC) equals to subject's Date/Time of Death (DTHDTC) populated in Demographics (DM) domain. Subject's Date/Time of Death info should be consistent across all domain</ruledetaileddescription>
	<igversion>3.1.3</igversion>
	<igversion>3.2</igversion>
	<domain>DS</domain>
	<rulexquery><![CDATA[
(: Rule SD1317: DSSTDTC for DEATH record is not the same as DM.DTHDTC - DEATH records (DSDECOD='DEATH') in Disposition (DS) domain should have Start Date of Event (DSDTDTC) equals to subject's Date/Time of Death (DTHDTC) populated in Demographics (DM) domain. Subject's Date/Time of Death info should be consistent across all domain. :)
(: ONLY applies DS :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external; 
declare variable $define external;
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' 
let $define := 'define2-0-0-example-sdtm.xml'  :)
let $definedoc := doc(concat($base,$define))
(: get the DM dataset :)
let $dmdatasetdef := $definedoc//odm:ItemGroupDef[@Name='DM']
(: and the location and the document itself :)
let $dmdatasetloc := (
	if($defineversion = '2.1') then $dmdatasetdef/def21:leaf/@xlink:href
	else $dmdatasetdef/def:leaf/@xlink:href
)
let $dmdatasetdoc := doc(concat($base,$dmdatasetloc))
(:  get the OIDs of USUBJID and DTHDTC in DM :)
let $dmusubjidoid := (
    for $a in $definedoc//odm:ItemDef[@Name='USUBJID']/@OID 
        where $a = $dmdatasetdef/odm:ItemRef/@ItemOID
        return $a
)
let $dmdthdtcoid := (
    for $a in $definedoc//odm:ItemDef[@Name='DTHDTC']/@OID 
        where $a = $dmdatasetdef/odm:ItemRef/@ItemOID
        return $a
)
(: now create a temporary structure containing the DM USUBJID-DTHDTC pairs :)
let $dthdtcpairs := (
    for $record in $dmdatasetdoc//odm:ItemGroupData
        let $dmusubjid := $record/odm:ItemData[@ItemOID=$dmusubjidoid]/@Value
        let $dthdtc := $record/odm:ItemData[@ItemOID=$dmdthdtcoid]/@Value
        return <dthdtcpair usubjid="{data($dmusubjid)}" dthdtc="{data($dthdtc)}" />
)
(: get the DS dataset(s) :)
for $datasetdef in $definedoc//odm:ItemGroupDef[@Name='DS' or @Domain='DS']
    let $name := $datasetdef/@Name
    (: get the location and the dataset document itself :)
    let $datasetloc := (
		if($defineversion = '2.1') then $datasetdef/def21:leaf/@xlink:href
		else $datasetdef/def:leaf/@xlink:href
	)
    let $datasetdoc := (
		if($datasetloc) then doc(concat($base,$datasetloc))
		else ()
	)
    (: get the OID of USUBJID, DSDECOD, DSDTDTC :)
    let $usubjidoid := (
        for $a in $definedoc//odm:ItemDef[@Name='USUBJID']/@OID 
        where $a = $datasetdef/odm:ItemRef/@ItemOID
        return $a
    )
    let $dsdecodoid := (
        for $a in $definedoc//odm:ItemDef[@Name='DSDECOD']/@OID 
        where $a = $datasetdef/odm:ItemRef/@ItemOID
        return $a
    )
    let $dsstdtcoid := (
        for $a in $definedoc//odm:ItemDef[@Name='DSSTDTC']/@OID 
        where $a = $datasetdef/odm:ItemRef/@ItemOID
        return $a
    )
    (: iterate over all the records that for which DSDECOD='DEATH' :)
    for $record in $datasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$dsdecodoid and @Value='DEATH']]
        let $recnum := $record/@data:ItemGroupDataSeq
        (: get the values of USUBJID and DSDTDTC :)
        let $usubjid := $record/odm:ItemData[@ItemOID=$usubjidoid]/@Value
        let $dsstdtc := $record/odm:ItemData[@ItemOID=$dsstdtcoid]/@Value
        (: and look them up in the DM-USUBJID- structure, 
        retrieving the value for DTHDTC for the given subject :)
        let $dthdtc := $dthdtcpairs[@usubjid=$usubjid]/@dthdtc
        (: DTHDTC from DM and DSSTDTC from DS must be equal :)
        where not($dsstdtc = $dthdtc) 
        return <error rule="SD1317" dataset="{data($name)}" variable="DSSTDTC" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">DSSTDTC value '{data($dsstdtc)}' does not correspond to DM.DTHDTC='{data($dthdtc)}' for USUBJID={data($usubjid)}  </error>		
	]]>
	</rulexquery>
</sdsrule>

<sdsrule originator="FDA" id="SD1318" standard="SDTM" last-update="2020-08-08">
	<ruledescription>Subjects with DSDECOD='PROTOCOL VIOLATION' in DS must have records in DV</ruledescription>
	<ruledetaileddescription>Subjects who has DSDECOD='PROTOCOL VIOLATION' records in Disposition (DS) domain should have supportive records with details in Protocol Deviations (DV) domain</ruledetaileddescription>
	<igversion>3.1.2</igversion>
	<igversion>3.1.3</igversion>
	<igversion>3.2</igversion>
	<domain>DS</domain>
	<rulexquery><![CDATA[
(: Rule SD1318: No records for subject found in DV domain for DS PROTOCOL VIOLATION - 
Subjects who has DSDECOD='PROTOCOL VIOLATION' records in Disposition (DS) domain should have supportive records with details in Protocol Deviations (DV) domain.
:)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external; 
declare variable $define external;
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' 
let $define := 'define2-0-0-example-sdtm.xml'  :)
let $definedoc := doc(concat($base,$define))
(: get the DS dataset :)
let $dsdatasetdef := $definedoc//odm:ItemGroupDef[@Name='DS' or @Domain='DS']
let $name := $dsdatasetdef/@Name (: for the ultimate case of DS splitted datasets :)
(: get the location and the dataset document itself :)
let $dsdatasetlocation := (
	if($defineversion = '2.1') then $dsdatasetdef/def21:leaf/@xlink:href
	else $dsdatasetdef/def:leaf/@xlink:href
)
let $dsdatasetdoc := (
	if($dsdatasetlocation) then doc(concat($base,$dsdatasetlocation))
	else ()
)
(: get the OID of USUBJID and DSDECOD :)
let $dsusubjidoid := (
    for $a in $definedoc//odm:ItemDef[@Name='USUBJID']/@OID 
    where $a = $dsdatasetdef/odm:ItemRef/@ItemOID
    return $a
)
let $dsdecodoid := (
    for $a in $definedoc//odm:ItemDef[@Name='DSDECOD']/@OID 
    where $a = $dsdatasetdef/odm:ItemRef/@ItemOID
    return $a
)
(: get the DV (Protocol Deviations) dataset(s) :)
for $dvdatasetdef in $definedoc//odm:ItemGroupDef[@Domain='DV']
    let $name := $dvdatasetdef/@Name
    (: get the dataset location and the document itself :)
    let $dvdatasetloc := (
		if($defineversion = '2.1') then $dvdatasetdef/def21:leaf/@xlink:href
		else $dvdatasetdef/def:leaf/@xlink:href
	)
    let $dvdatasetdoc := (
		if($dvdatasetloc) then doc(concat($base,$dvdatasetloc))
		else ()
	)
    (: we need the OID of USUBJID in DV :)
    let $dvusubjidoid := (
        for $a in $definedoc//odm:ItemDef[@Name='USUBJID']/@OID 
        where $a = $dvdatasetdef/odm:ItemRef/@ItemOID
        return $a
    )
    (: iterate over all records in the DS dataset for which DSDECOD = 'PROTOCOL VIOLATION' :)
    for $record in $dsdatasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$dsdecodoid and @Value='PROTOCOL VIOLATION']]
        let $recnum := $record/@data:ItemGroupDataSeq
        (: get the USUBJID value :)
        let $dsusubjid := $record/odm:ItemData[@ItemOID=$dsusubjidoid]/@Value
        (: count the number of records in DV for this subject :)
        let $count := count($dvdatasetdoc//odm:ItemGroupData/odm:ItemData[@ItemOID=$dvusubjidoid]/@Value=$dsusubjid)
        (: there must be such at least one record in DV :)
        where $count = 0
        return <error rule="SD1318" variable="DSDECOD" dataset="{$name}" recordnumber="{$recnum}" rulelastupdate="2020-08-08">No record was found in DV for subject with USUBJID='{data($dsusubjid)}' although there is a record in DS with DSDECOD='PROTOCOL DEVIATION'</error>				
	]]>
	</rulexquery>
</sdsrule>

<sdsrule originator="FDA" id="SD1039" standard="SDTM" last-update="2020-08-08">
	<ruledescription>--CAT value may not be redundant with other variable values</ruledescription>
	<ruledetaileddescription>Value of Category (--CAT) should not be identical to that in Domain Abbreviation (DOMAIN); Reported Term (--TERM); Name of Measurement, Test, or Examination (--TESTCD); Name of Treatment (--TRT); Body System or Organ Class (--BODSYS)</ruledetaileddescription>
	<igversion>3.1.2</igversion>
	<igversion>3.1.3</igversion>
	<igversion>3.2</igversion>
	<domain>INTERVENTIONS</domain>
	<domain>EVENTS</domain>
	<domain>FINDINGS</domain>
	<domain>TI</domain>
	<rulexquery><![CDATA[
(: Rule SD1039: --CAT value is redundant with other variable values - Value of Category (--CAT) should not be identical to that in Domain Abbreviation (DOMAIN); Reported Term (--TERM); Name of Measurement, Test, or Examination (--TESTCD); Name of Treatment (--TRT); Body System or Organ Class (--BODSYS) :)
(: Applicable to INTERVENTIONS, EVENTS, FINDINGS, TI :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external; 
declare variable $define external;
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' 
let $define := 'define2-0-0-example-sdtm.xml'  :)
let $definedoc := doc(concat($base,$define))
(: iterate over all datasets in INTERVENTIONS, EVENTS, FINDINGS, TI :)
for $datasetdef in $definedoc//odm:ItemGroupDef[@Name='TI' or upper-case(@def:Class)='INTERVENTIONS' or upper-case(@def:Class)='EVENTS' or upper-case(@def:Class)='FINDINGS' 
		or upper-case(./def21:Class/@Name)='INTERVENTIONS' or upper-case(./def21:Class/@Name)='EVENTS' or upper-case(./def21:Class/@Name)='FINDINGS']
    (: get the name of the dataset :)
    let $name := $datasetdef/@Name
    (: get the OID of --CAT - ATTENTION! Take care of --SCAT :)
    let $catoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'CAT') and string-length(@Name)=5]/@OID 
        where $a = $datasetdef/odm:ItemRef/@ItemOID 
        return $a
    )
	let $catname := $definedoc//odm:ItemDef[@OID=$catoid]/@Name
    (: get the OIDs of Domain Abbreviation (DOMAIN); Reported Term (--TERM); Name of Measurement, Test, or Examination (--TESTCD); Name of Treatment (--TRT); Body System or Organ Class (--BODSYS) :)
    let $domainoid := (
        for $a in $definedoc//odm:ItemDef[@Name='DOMAIN']/@OID 
        where $a = $datasetdef/odm:ItemRef/@ItemOID 
        return $a
    )
    let $termoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'TERM')]/@OID 
        where $a = $datasetdef/odm:ItemRef/@ItemOID 
        return $a
    )
    let $testcdoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'TESTCD')]/@OID 
        where $a = $datasetdef/odm:ItemRef/@ItemOID 
        return $a
    )
    let $testcdoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'TESTCD')]/@OID 
        where $a = $datasetdef/odm:ItemRef/@ItemOID 
        return $a
    )
    let $trtoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'TRT')]/@OID 
        where $a = $datasetdef/odm:ItemRef/@ItemOID 
        return $a
    )
    let $bodsysoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'BODSYS')]/@OID 
        where $a = $datasetdef/odm:ItemRef/@ItemOID 
        return $a
    )
    (: get the dataset location :)
    let $datasetloc := (
		if($defineversion = '2.1') then $datasetdef/def21:leaf/@xlink:href
		else $datasetdef/def:leaf/@xlink:href
	)
    (: and the document itself :)
    let $datasetdoc := (
		if($datasetloc) then doc(concat($base,$datasetloc))
		else ()
	)
    (: iterate over all records that have --CAT populated :)
    for $record in $datasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$catoid]/@Value]
        let $recnum := $record/@data:ItemGroupDataSeq
        (: get the value of the --CAT variable :)
        let $cat := $record/odm:ItemData[@ItemOID=$catoid]/@Value
        (: get the values of DOMAIN, --TERM, --TESTCD, --TRT, --BODSYS
            (if any) :)
        let $domain := $record/odm:ItemData[@ItemOID=$domainoid]/@Value
        let $term := $record/odm:ItemData[@ItemOID=$termoid]/@Value
        let $testcd := $record/odm:ItemData[@ItemOID=$testcdoid]/@Value
        let $trt := $record/odm:ItemData[@ItemOID=$trtoid]/@Value
        let $bodsys := $record/odm:ItemData[@ItemOID=$bodsysoid]/@Value
        (: The value of --CAT may not be identical to any of DOMAIN, --TERM, --TESTCD, --TRT, --BODSYS :)
        where ($cat=$domain or $cat=$term or $cat=$testcd or $cat=$trt or $cat=$bodsys)	
		return <warning rule="SD1039" variable="{$catname}" dataset="{$name}" recordnumber="{$recnum}" rulelastupdate="2020-08-08">The value of {data($catname)} is redundant. It is equal to one of DOMAIN, --TERM, --TESTCD, --TRT, --BODSYS</warning>
	]]>
	</rulexquery>
</sdsrule>

<sdsrule originator="FDA" id="SD1039-SD" standard="SDTM" last-update="2020-08-08" requiresDomainOrDataset="Yes">
	<ruledescription>--CAT value may not be redundant with other variable values</ruledescription>
	<ruledetaileddescription>Value of Category (--CAT) should not be identical to that in Domain Abbreviation (DOMAIN); Reported Term (--TERM); Name of Measurement, Test, or Examination (--TESTCD); Name of Treatment (--TRT); Body System or Organ Class (--BODSYS)</ruledetaileddescription>
	<igversion>3.1.2</igversion>
	<igversion>3.1.3</igversion>
	<igversion>3.2</igversion>
	<domain>INTERVENTIONS</domain>
	<domain>EVENTS</domain>
	<domain>FINDINGS</domain>
	<domain>TI</domain>
	<rulexquery><![CDATA[
(: Rule SD1039: --CAT value is redundant with other variable values - Value of Category (--CAT) should not be identical to that in Domain Abbreviation (DOMAIN); Reported Term (--TERM); Name of Measurement, Test, or Examination (--TESTCD); Name of Treatment (--TRT); Body System or Organ Class (--BODSYS) :)
(: Applicable to INTERVENTIONS, EVENTS, FINDINGS, TI :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external; 
declare variable $define external;
declare variable $defineversion external;
declare variable $datasetname external;
(: let $base := '/db/fda_submissions/cdisc01/' 
let $define := 'define2-0-0-example-sdtm.xml'  :)
let $definedoc := doc(concat($base,$define))
(: iterate over all datasets in INTERVENTIONS, EVENTS, FINDINGS, TI :)
for $datasetdef in $definedoc//odm:ItemGroupDef[@Name=$datasetname][@Name='TI' or upper-case(@def:Class)='INTERVENTIONS' or upper-case(@def:Class)='EVENTS' or upper-case(@def:Class)='FINDINGS' 
		or upper-case(./def21:Class/@Name)='INTERVENTIONS' or upper-case(./def21:Class/@Name)='EVENTS' or upper-case(./def21:Class/@Name)='FINDINGS']
    (: get the name of the dataset :)
    let $name := $datasetdef/@Name
    (: get the OID of --CAT - ATTENTION! Take care of --SCAT :)
    let $catoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'CAT') and string-length(@Name)=5]/@OID 
        where $a = $datasetdef/odm:ItemRef/@ItemOID 
        return $a
    )
	let $catname := $definedoc//odm:ItemDef[@OID=$catoid]/@Name
    (: get the OIDs of Domain Abbreviation (DOMAIN); Reported Term (--TERM); Name of Measurement, Test, or Examination (--TESTCD); Name of Treatment (--TRT); Body System or Organ Class (--BODSYS) :)
    let $domainoid := (
        for $a in $definedoc//odm:ItemDef[@Name='DOMAIN']/@OID 
        where $a = $datasetdef/odm:ItemRef/@ItemOID 
        return $a
    )
    let $termoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'TERM')]/@OID 
        where $a = $datasetdef/odm:ItemRef/@ItemOID 
        return $a
    )
    let $testcdoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'TESTCD')]/@OID 
        where $a = $datasetdef/odm:ItemRef/@ItemOID 
        return $a
    )
    let $testcdoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'TESTCD')]/@OID 
        where $a = $datasetdef/odm:ItemRef/@ItemOID 
        return $a
    )
    let $trtoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'TRT')]/@OID 
        where $a = $datasetdef/odm:ItemRef/@ItemOID 
        return $a
    )
    let $bodsysoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'BODSYS')]/@OID 
        where $a = $datasetdef/odm:ItemRef/@ItemOID 
        return $a
    )
    (: get the dataset location :)
    let $datasetloc := (
		if($defineversion = '2.1') then $datasetdef/def21:leaf/@xlink:href
		else $datasetdef/def:leaf/@xlink:href
	)
    (: and the document itself :)
    let $datasetdoc := (
		if($datasetloc) then doc(concat($base,$datasetloc))
		else ()
	)
    (: iterate over all records that have --CAT populated :)
    for $record in $datasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$catoid]/@Value]
        let $recnum := $record/@data:ItemGroupDataSeq
        (: get the value of the --CAT variable :)
        let $cat := $record/odm:ItemData[@ItemOID=$catoid]/@Value
        (: get the values of DOMAIN, --TERM, --TESTCD, --TRT, --BODSYS
            (if any) :)
        let $domain := $record/odm:ItemData[@ItemOID=$domainoid]/@Value
        let $term := $record/odm:ItemData[@ItemOID=$termoid]/@Value
        let $testcd := $record/odm:ItemData[@ItemOID=$testcdoid]/@Value
        let $trt := $record/odm:ItemData[@ItemOID=$trtoid]/@Value
        let $bodsys := $record/odm:ItemData[@ItemOID=$bodsysoid]/@Value
        (: The value of --CAT may not be identical to any of DOMAIN, --TERM, --TESTCD, --TRT, --BODSYS :)
        where ($cat=$domain or $cat=$term or $cat=$testcd or $cat=$trt or $cat=$bodsys)	
		return <warning rule="SD1039" variable="{$catname}" dataset="{$name}" recordnumber="{$recnum}" rulelastupdate="2020-08-08">The value of {data($catname)} is redundant. It is equal to one of DOMAIN, --TERM, --TESTCD, --TRT, --BODSYS</warning>
	]]>
	</rulexquery>
</sdsrule>

<!-- TODO: (unusual) case that MHENDTCs are of type dateTime: the current algorithm assumes they are dates or partialDates   -->
<sdsrule originator="FDA" id="SD1145" standard="SDTM" last-update="2020-08-08">
	<ruledescription>MHENDTC date may not be after RFSTDTC - </ruledescription>
	<ruledetaileddescription>The medical history dataset includes the subject's prior history at the start of the trial. End Date/Time of Medical History Event (MHENDTC) should be before Subject Reference Start Date/Time (RFSTDTC)</ruledetaileddescription>
	<igversion>3.2</igversion>
	<domain>MH</domain>
	<synonym context="CDISC">CG0078</synonym>
	<rulexquery><![CDATA[
(: Rule SD1145: When MHENDTC != null then MHENDTC < DM.RFSTDTC :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external; 
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: Get the DM dataset and its location :)
let $dmitemgroupdef := $definedoc//odm:ItemGroupDef[@Name='DM']
let $dmdatasetlocation := (
	if($defineversion = '2.1') then $dmitemgroupdef/def21:leaf/@xlink:href
	else $dmitemgroupdef/def:leaf/@xlink:href
)
let $dmdatasetdoc := doc(concat($base,$dmdatasetlocation))
(: and the OID of USUBJID and of RFSTDTC :)
let $dmusubjidoid := (
    for $a in $definedoc//odm:ItemDef[@Name='USUBJID']/@OID 
    where $a = $dmitemgroupdef/odm:ItemRef/@ItemOID
    return $a
)
let $rfstdtcoid := (
    for $a in $definedoc//odm:ItemDef[@Name='RFSTDTC']/@OID 
    where $a = $dmitemgroupdef/odm:ItemRef/@ItemOID
    return $a
)
(: get the MH dataset(s) - take splitted domains into account :)
for $mhitemgroupdef in $definedoc//odm:ItemGroupDef[starts-with(@Name,'MH') or @Domain='MH']
	let $name := $mhitemgroupdef/@Name
	let $mhdatasetlocation := (
		if($defineversion = '2.1') then $mhitemgroupdef/def21:leaf/@xlink:href
		else $mhitemgroupdef/def:leaf/@xlink:href
	)
	let $mhdatasetdoc := (
		if($mhdatasetlocation) then doc(concat($base,$mhdatasetlocation))
		else ()
	)
	(: get the OID of MHENDTC and of USUBJID in MH :)
	let $mhendtcoid := (
		for $a in $definedoc//odm:ItemDef[@Name='MHENDTC']/@OID 
		where $a = $mhitemgroupdef/odm:ItemRef/@ItemOID
		return $a
	)
	let $mhusubjidoid := (
		for $a in $definedoc//odm:ItemDef[@Name='USUBJID']/@OID 
		where $a = $mhitemgroupdef/odm:ItemRef/@ItemOID
		return $a
	)
	(: iterate over all records in the MH dataset that have MHENDTC not null :)
	for $record in $mhdatasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$mhendtcoid and @Value != '']]
		let $recnum := $record/@data:ItemGroupDataSeq
		(: get the value of USUBJID and of MHENDTC for this record :)
		let $usubjid := $record/odm:ItemData[@ItemOID=$mhusubjidoid]/@Value
		let $mhendtc := $record/odm:ItemData[@ItemOID=$mhendtcoid]/@Value
		(: look up the corresponding DM record - there should be only one :)
		let $dmrecord := $dmdatasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$dmusubjidoid and @Value=$usubjid]][1]
		(: and get the value of RFSTDTC :)
		let $rfstdtc := $dmrecord/odm:ItemData[@ItemOID=$rfstdtcoid]/@Value
		(: in case MHENDTC is a partial date, we must do something :)
		(: when only the year is present, -01-01 is added, when year and month are present, -01 is added.
		This will take care that 'in case of doubt', no error is thrown :)
		let $mhendtccomplete := (
			if(string-length($mhendtc)=4) then concat($mhendtc,'-01-01')
			else if(string-length($mhendtc)=7) then concat($mhendtc,'-01')
			else $mhendtc
		)
		(: compare the dates, take care that both really are complete dates. 
		give an error when MHENDTC > RFSTDTC :)
		where $rfstdtc castable as xs:date and $mhendtccomplete castable as xs:date and not(xs:date($mhendtccomplete) <= xs:date($rfstdtc))
		return <error rule="CG0078" variable="SD1145" dataset="{data($name)}" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">MHENDTC={data($mhendtc)} is after or on RFSTDTC={data($rfstdtc)}</error>
		]]>
	</rulexquery>
</sdsrule>
	
<!-- TODO: (unusual) case that MHSTDTCs are of type dateTime: the current algorithm assumes they are dates or partialDates   -->
<sdsrule originator="FDA" id="SD1144" standard="SDTM" last-update="2020-08-08">
	<ruledescription>MHSTDTC date may not be after RFSTDTC</ruledescription>
	<ruledetaileddescription>The medical history dataset includes the subject's prior history at the start of the trial. Start Date/Time of Medical History Event (MHSTDTC) should be before Subject Reference Start Date/Time (RFSTDTC)</ruledetaileddescription>
	<igversion>3.1.2</igversion>
	<igversion>3.1.3</igversion>
	<igversion>3.2</igversion>
	<domain>MH</domain>
	<synonym context="CDISC">CG0079</synonym>
	<rulexquery><![CDATA[
(: Rule SD1144: When MHSTDTC != null then MHSTDTC < DM.RFSTDTC :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external; 
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/'  :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: Get the DM dataset and its location :)
let $dmitemgroupdef := $definedoc//odm:ItemGroupDef[@Name='DM']
let $dmdatasetlocation := (
	if($defineversion = '2.1') then $dmitemgroupdef/def21:leaf/@xlink:href
	else $dmitemgroupdef/def:leaf/@xlink:href
)
let $dmdatasetdoc := doc(concat($base,$dmdatasetlocation))
(: and the OID of USUBJID and of RFSTDTC :)
let $dmusubjidoid := (
    for $a in $definedoc//odm:ItemDef[@Name='USUBJID']/@OID 
    where $a = $dmitemgroupdef/odm:ItemRef/@ItemOID
    return $a
)
let $rfstdtcoid := (
    for $a in $definedoc//odm:ItemDef[@Name='RFSTDTC']/@OID 
    where $a = $dmitemgroupdef/odm:ItemRef/@ItemOID
    return $a
)
(: get the MH dataset(s) - take splitted domains into account :)
for $mhitemgroupdef in $definedoc//odm:ItemGroupDef[starts-with(@Name,'MH') or @Domain='MH']
	let $name := $mhitemgroupdef/@Name
	let $mhdatasetlocation := (
		if($defineversion = '2.1') then $mhitemgroupdef/def21:leaf/@xlink:href
		else $mhitemgroupdef/def:leaf/@xlink:href
	)
	let $mhdatasetdoc := (
		if($mhdatasetlocation) then doc(concat($base,$mhdatasetlocation))
		else ()
	)
	(: get the OID of MHSTDTC and of USUBJID in MH :)
	let $mhstdtcoid := (
		for $a in $definedoc//odm:ItemDef[@Name='MHSTDTC']/@OID 
		where $a = $mhitemgroupdef/odm:ItemRef/@ItemOID
		return $a
	)
	let $mhusubjidoid := (
		for $a in $definedoc//odm:ItemDef[@Name='USUBJID']/@OID 
		where $a = $mhitemgroupdef/odm:ItemRef/@ItemOID
		return $a
	)

	(: iterate over all records in the MH dataset that have MHENDTC not null :)
	for $record in $mhdatasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$mhstdtcoid and @Value != '']]
		let $recnum := $record/@data:ItemGroupDataSeq
		(: get the value of USUBJID and of MHSTDTC for this record :)
		let $usubjid := $record/odm:ItemData[@ItemOID=$mhusubjidoid]/@Value
		let $mhstdtc := $record/odm:ItemData[@ItemOID=$mhstdtcoid]/@Value
		(: look up the corresponding DM record - there should be only one :)
		let $dmrecord := $dmdatasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$dmusubjidoid and @Value=$usubjid]][1]
		(: and get the value of RFSTDTC :)
		let $rfstdtc := $dmrecord/odm:ItemData[@ItemOID=$rfstdtcoid]/@Value
		(: in case MHSTDTC is a partial date, we must do something :)
		(: when only the year is present, -01-01 is added, when year and month are present, -01 is added.
		This will take care that 'in case of doubt', no error is thrown :)
		let $mhstdtccomplete := (
			if(string-length($mhstdtc)=4) then concat($mhstdtc,'-01-01')
			else if(string-length($mhstdtc)=7) then concat($mhstdtc,'-01')
			else $mhstdtc
		)
		(: compare the dates, take care that both really are complete dates. 
		give an error when MHSTDTC > RFSTDTC :)
		where $rfstdtc castable as xs:date and $mhstdtccomplete castable as xs:date and not(xs:date($mhstdtccomplete) <= xs:date($rfstdtc))
		return <error rule="SD1144" variable="MHSTDTC" dataset="{data($name)}" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">MHSTDTC={data($mhstdtc)} is after or on RFSTDTC={data($rfstdtc)}</error>
		]]>
	</rulexquery>
</sdsrule>
	

<sdsrule originator="FDA" id="SD0042" standard="SDTM" last-update="2020-08-08">
	<ruledescription>--STAT must be equal to 'NOT DONE', when --PRESP='Y' and --OCCUR is NULL</ruledescription>
	<ruledetaileddescription>When no response is provided for Occurrence (--OCCUR) for a pre-specified Intervention or Event (--PRESP = 'Y'), then Status (--STAT) should be set to 'NOT DONE'</ruledetaileddescription>
	<igversion>3.1.3</igversion>
	<igversion>3.2</igversion>
	<domain>EVENTS</domain>
	<domain>INTERVENTIONS</domain>
	<synonym context="CDISC">SD0042</synonym>
	<rulexquery><![CDATA[
(: Rule SD0042 When --PRESP = 'Y' and --OCCUR = null then --STAT = 'NOT DONE'
 Applies to: EVENTS,INTERVENTIONS, NOT(DS, DV, EX) :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: Applies to: EVENTS,INTERVENTIONS, NOT(DS, DV, EX) :)
(: ORIG: for $itemgroupdef in $definedoc//odm:ItemGroupDef[upper-case(@def:Class)='INTERVENTIONS' or upper-case(@def:Class)='EVENTS'][not(@Name='DS') and not(@Name='DV') and not(@Name='EX')] :)
for $itemgroupdef in $definedoc//odm:ItemGroupDef[upper-case(@def:Class)='INTERVENTIONS' or upper-case(@def:Class)='EVENTS' 
		or upper-case(./def21:Class/@Name)='INTERVENTIONS' or upper-case(./def21:Class/@Name)='EVENTS']
    let $name := $itemgroupdef/@Name
    (: get the dataset :)
    let $name := $itemgroupdef/@Name
    let $datasetlocation := (
		if($defineversion = '2.1') then $itemgroupdef/def21:leaf/@xlink:href
		else $itemgroupdef/def:leaf/@xlink:href
	)
    let $datasetdoc := (
		if($datasetlocation) then doc(concat($base,$datasetlocation))
		else ()
	)
    (: find the OID and name of the --PRESP variable :)
    let $prespoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'PRESP')]/@OID 
        where $a = $itemgroupdef/odm:ItemRef/@ItemOID
        return $a
    )
    let $prespname := $definedoc//odm:ItemDef[OID=$prespoid]/@Name
    (: get the OID and name of the --OCCUR variable :)
    let $occuroid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'OCCUR')]/@OID 
        where $a = $itemgroupdef/odm:ItemRef/@ItemOID
        return $a
    )
    let $occurname := $definedoc//odm:ItemDef[OID=$occuroid]/@Name
    (: get the OID and name of the --STAT variable :)
    let $statoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'STAT')]/@OID 
        where $a = $itemgroupdef/odm:ItemRef/@ItemOID
        return $a
    )
    let $statname := $definedoc//odm:ItemDef[@OID=$statoid]/@Name
    (: iterate over all the records with --PRESP = 'Y' and --OCCUR = null  :)
    for $record in $datasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$prespoid and @Value='Y'] and not(odm:ItemData[@ItemOID=$occuroid])]
    let $recnum := $record/@data:ItemGroupDataSeq
    (: get the value of --STAT :)
    let $stat := $record/odm:ItemData[@ItemOID=$statoid]/@Value
    (: the value of --STAT must be 'NOT DONE' :)
    where not($stat='NOT DONE')
    return <error rule="SD0042" variable="{data($statname)}" dataset="{$name}" recordnumber="{data($recnum)}" rulelastupdate="2020-08-08">Invalid value for {data($statname)}='{data($stat)}' for {data($prespname)}='Y' and {data($occurname)}=null. Value for {data($statname)} expected is 'NOT DONE'</error>				
	]]></rulexquery>
</sdsrule>
	
<sdsrule originator="FDA" id="SD0042-SD" standard="SDTM" last-update="2020-08-08" requiresDomainOrDataset="Yes">
	<ruledescription>--STAT must be equal to 'NOT DONE', when --PRESP='Y' and --OCCUR is NULL</ruledescription>
	<ruledetaileddescription>When no response is provided for Occurrence (--OCCUR) for a pre-specified Intervention or Event (--PRESP = 'Y'), then Status (--STAT) should be set to 'NOT DONE'</ruledetaileddescription>
	<igversion>3.1.3</igversion>
	<igversion>3.2</igversion>
	<domain>EVENTS</domain>
	<domain>INTERVENTIONS</domain>
	<synonym context="CDISC">SD0042</synonym>
	<rulexquery><![CDATA[
(: Rule SD0042 When --PRESP = 'Y' and --OCCUR = null then --STAT = 'NOT DONE'
 Applies to: EVENTS,INTERVENTIONS, NOT(DS, DV, EX) :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
declare variable $datasetname external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: Applies to: EVENTS,INTERVENTIONS, NOT(DS, DV, EX) :)
(: ORIG: for $itemgroupdef in $definedoc//odm:ItemGroupDef[upper-case(@def:Class)='INTERVENTIONS' or upper-case(@def:Class)='EVENTS'][not(@Name='DS') and not(@Name='DV') and not(@Name='EX')] :)
for $itemgroupdef in $definedoc//odm:ItemGroupDef[@Name=$datasetname][upper-case(@def:Class)='INTERVENTIONS' or upper-case(@def:Class)='EVENTS' 
		or upper-case(./def21:Class/@Name)='INTERVENTIONS' or upper-case(./def21:Class/@Name)='EVENTS']
    let $name := $itemgroupdef/@Name
    (: get the dataset :)
    let $name := $itemgroupdef/@Name
    let $datasetlocation := (
		if($defineversion = '2.1') then $itemgroupdef/def21:leaf/@xlink:href
		else $itemgroupdef/def:leaf/@xlink:href
	)
    let $datasetdoc := (
		if($datasetlocation) then doc(concat($base,$datasetlocation))
		else ()
	)
    (: find the OID and name of the --PRESP variable :)
    let $prespoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'PRESP')]/@OID 
        where $a = $itemgroupdef/odm:ItemRef/@ItemOID
        return $a
    )
    let $prespname := $definedoc//odm:ItemDef[OID=$prespoid]/@Name
    (: get the OID and name of the --OCCUR variable :)
    let $occuroid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'OCCUR')]/@OID 
        where $a = $itemgroupdef/odm:ItemRef/@ItemOID
        return $a
    )
    let $occurname := $definedoc//odm:ItemDef[OID=$occuroid]/@Name
    (: get the OID and name of the --STAT variable :)
    let $statoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'STAT')]/@OID 
        where $a = $itemgroupdef/odm:ItemRef/@ItemOID
        return $a
    )
    let $statname := $definedoc//odm:ItemDef[@OID=$statoid]/@Name
    (: iterate over all the records with --PRESP = 'Y' and --OCCUR = null  :)
    for $record in $datasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$prespoid and @Value='Y'] and not(odm:ItemData[@ItemOID=$occuroid])]
    let $recnum := $record/@data:ItemGroupDataSeq
    (: get the value of --STAT :)
    let $stat := $record/odm:ItemData[@ItemOID=$statoid]/@Value
    (: the value of --STAT must be 'NOT DONE' :)
    where not($stat='NOT DONE')
    return <error rule="SD0042" variable="{data($statname)}" dataset="{$name}" recordnumber="{data($recnum)}" rulelastupdate="2020-08-08">Invalid value for {data($statname)}='{data($stat)}' for {data($prespname)}='Y' and {data($occurname)}=null. Value for {data($statname)} expected is 'NOT DONE'</error>				
	]]></rulexquery>
</sdsrule>

<sdsrule originator="FDA" id="SD1241" standard="SDTM" last-update="2020-08-08">
	<ruledescription>--BODSYS must be populated when --BDSYCD is populated</ruledescription>
	<ruledetaileddescription>Body System or Organ Class (--BODSYS) must be populated when Body System or Organ Class Code (--BDSYCD) is populated</ruledetaileddescription>
	<igversion>3.1.2</igversion>
	<igversion>3.1.3</igversion>
	<igversion>3.2</igversion>
	<domain>AE</domain>
	<domain>MH</domain>
	<domain>CE</domain>
	<synonym context="CDISC">CG0082</synonym>
	<rulexquery><![CDATA[
(: Rule SD1241 When --BDSYCD != null then --BODSYS != null 
Applies to: AE, MH, CE :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: Applies to: EVENTS,INTERVENTIONS :)
for $itemgroupdef in $definedoc//odm:ItemGroupDef[@Name='AE' or @Domain='AE' or @Name='MH' or @Domain='MH' or @Name='CE' or @Domain='CE']
    let $name := $itemgroupdef/@Name
    (: get the dataset :)
    let $name := $itemgroupdef/@Name
	let $datasetlocation := (
		if($defineversion = '2.1') then $itemgroupdef/def21:leaf/@xlink:href
		else $itemgroupdef/def:leaf/@xlink:href
	)
    let $datasetdoc := (
		if($datasetlocation) then doc(concat($base,$datasetlocation))
		else ()
	)
    (: get the OID of --BDSYCD and of --BODSYS :)
    let $bdsycdoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'BDSYCD')]/@OID 
        where $a = $itemgroupdef/odm:ItemRef/@ItemOID
        return $a
    )
    let $bdsycdname := $definedoc//odm:ItemDef[@OID=$bdsycdoid]/@Name
    let $bodsysoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'BODSYS')]/@OID 
        where $a = $itemgroupdef/odm:ItemRef/@ItemOID
        return $a
    )
    let $bodsysname := $definedoc//odm:ItemDef[@OID=$bodsysoid]/@Name
    (: iterate over all the records that have --BDSYCD != null :)
    for $record in $datasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$bdsycdoid and @Value!='']]
        let $recnum := $record/@data:ItemGroupDataSeq
        (: get the value of --BODSYS and of --BDSYCD :)
        let $bodsys := $record/odm:ItemData[@ItemOID=$bodsysoid]/@Value
        let $bdsycd := $record/odm:ItemData[@ItemOID=$bdsycdoid]/@Value
        (: --BODSYS must not be null :)
        where not($bodsys)
        return <error rule="SD1241" variable="{data($bdsycdname)}" dataset="{$name}" recordnumber="{data($recnum)}" rulelastupdate="2020-08-08">Null value found for {data($bodsysname)} where {data($bdsycdname)}='{data($bdsycd)}'</error>
       ]]>
	</rulexquery>
</sdsrule>
	
<sdsrule originator="FDA" id="SD1241-SD" standard="SDTM" last-update="2020-08-08" requiresDomainOrDataset="Yes">
	<ruledescription>--BODSYS must be populated when --BDSYCD is populated</ruledescription>
	<ruledetaileddescription>Body System or Organ Class (--BODSYS) must be populated when Body System or Organ Class Code (--BDSYCD) is populated</ruledetaileddescription>
	<igversion>3.1.2</igversion>
	<igversion>3.1.3</igversion>
	<igversion>3.2</igversion>
	<domain>AE</domain>
	<domain>MH</domain>
	<domain>CE</domain>
	<synonym context="CDISC">CG0082</synonym>
	<rulexquery><![CDATA[
(: Rule SD1241 When --BDSYCD != null then --BODSYS != null 
Applies to: AE, MH, CE :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
declare variable $datasetname external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: Applies to: EVENTS,INTERVENTIONS :)
for $itemgroupdef in $definedoc//odm:ItemGroupDef[@Name=$datasetname][@Name='AE' or @Domain='AE' or @Name='MH' or @Domain='MH' or @Name='CE' or @Domain='CE']
    let $name := $itemgroupdef/@Name
    (: get the dataset :)
    let $name := $itemgroupdef/@Name
	let $datasetlocation := (
		if($defineversion = '2.1') then $itemgroupdef/def21:leaf/@xlink:href
		else $itemgroupdef/def:leaf/@xlink:href
	)
    let $datasetdoc := (
		if($datasetlocation) then doc(concat($base,$datasetlocation))
		else ()
	)
    (: get the OID of --BDSYCD and of --BODSYS :)
    let $bdsycdoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'BDSYCD')]/@OID 
        where $a = $itemgroupdef/odm:ItemRef/@ItemOID
        return $a
    )
    let $bdsycdname := $definedoc//odm:ItemDef[@OID=$bdsycdoid]/@Name
    let $bodsysoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'BODSYS')]/@OID 
        where $a = $itemgroupdef/odm:ItemRef/@ItemOID
        return $a
    )
    let $bodsysname := $definedoc//odm:ItemDef[@OID=$bodsysoid]/@Name
    (: iterate over all the records that have --BDSYCD != null :)
    for $record in $datasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$bdsycdoid and @Value!='']]
        let $recnum := $record/@data:ItemGroupDataSeq
        (: get the value of --BODSYS and of --BDSYCD :)
        let $bodsys := $record/odm:ItemData[@ItemOID=$bodsysoid]/@Value
        let $bdsycd := $record/odm:ItemData[@ItemOID=$bdsycdoid]/@Value
        (: --BODSYS must not be null :)
        where not($bodsys)
        return <error rule="SD1241" variable="{data($bdsycdname)}" dataset="{$name}" recordnumber="{data($recnum)}" rulelastupdate="2020-08-08">Null value found for {data($bodsysname)} where {data($bdsycdname)}='{data($bdsycd)}'</error>
       ]]>
	</rulexquery>
</sdsrule>


<!-- FDA: limited to AE, MH, CE -->
<sdsrule originator="FDA" id="SD1242" standard="SDTM" last-update="2020-08-08">
	<ruledescription>Value for --BODSYS must be consistent within --BDSYCD</ruledescription>
	<ruledetaileddescription>All values of Body System or Organ Class (--BODSYS) should be the same for a given value of Body System or Organ Class Code (--BDSYCD)</ruledetaileddescription>
	<igversion>3.1.2</igversion>
	<igversion>3.1.3</igversion>
	<igversion>3.2</igversion>
	<domain>AE</domain>
	<domain>MH</domain>
	<domain>CE</domain>
	<synonym context="CDISC">CG0083</synonym>
	<rulexquery><![CDATA[
(: Rule SD1242 - Inconsistent value for --BODSYS within --BDSYCD :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' 
let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: Iterate over the EVENTS and INTERVENTIONS datasets :)
for $itemgroupdef in $definedoc//odm:ItemGroupDef[@Name='AE' or @Domain='AE' or @Name='MH' or @Domain='MH' or @Name='CE' or @Domain='CE']
    let $name := $itemgroupdef/@Name
	let $datasetname := (
		if($defineversion = '2.1') then $itemgroupdef/def21:leaf/@xlink:href
		else $itemgroupdef/def:leaf/@xlink:href
	)
    let $datasetdoc := (
		if($datasetname) then doc(concat($base,$datasetname))
		else ()
	)
    (: and get the OID of --BODSYS and of --BDSYCD :)
    let $bdsycdoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'BDSYCD')]/@OID 
        where $a = $itemgroupdef/odm:ItemRef/@ItemOID
        return $a
    )
    let $bdsycdname := $definedoc//odm:ItemDef[@OID=$bdsycdoid]/@Name
    let $bodsysoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'BODSYS')]/@OID 
        where $a = $itemgroupdef/odm:ItemRef/@ItemOID
        return $a
    )
    let $bodsysname := $definedoc//odm:ItemDef[@OID=$bodsysoid]/@Name
    (: group all the records by --BDSYCD value: all the records in the group will have the same value for --BODSYS :)
    let $orderedrecords := (
        for $record in $datasetdoc//odm:ItemGroupData
        group by 
        $b := $record/odm:ItemData[@ItemOID=$bdsycdoid]/@Value
        return element group {  
            $record
        }
    )
    (: iterate over all groups - each having the same value of --BDSYCD for all records within the group :)
    for $group in $orderedrecords[$bdsycdoid and $bodsysoid]
        (: get the value of --BDSYCD from the first record only :)
        let $bdsycd := $group/odm:ItemGroupData[1]/odm:ItemData[@ItemOID=$bdsycdoid]/@Value
        (: get the first value of --BODSYS :)
        let $bodsys1 := $group/odm:ItemGroupData[1]/odm:ItemData[@ItemOID=$bodsysoid]/@Value
        let $recnum1 := $group/odm:ItemGroupData[1]/@data:ItemGroupDataSeq
        (: return <test>{data($bodsys)} - {data($bodsys)}</test> :)
        (: iterate over all other records in the same group, they have the same value for --BDSYCD,
        and must also have the same value for --BODSYS :)
        for $record in $group/odm:ItemGroupData[position()>1]
            let $recnum2 := $record/@data:ItemGroupDataSeq
            (: get the value of --BODSYS :)
            let $bodsys2 := $record/odm:ItemData[@ItemOID=$bodsysoid]/@Value
            (: the two --BODSYS values must be equal, except when they are null :)
            where $bdsycd!='' and $bodsys1!='' and $bodsys2!='' and not($bodsys1 = $bodsys2)
            return <error rule="SD1242" rulelastupdate="2020-08-08" dataset="{data($name)}" variable="{data($bodsysname)}" recordnumber="{data($recnum2)}">Combination of {data($bdsycdname)} and {data($bodsysname)} in dataset {data($datasetname)} is inconsistent: 
                record {data($recnum1)}: {data($bdsycdname)}='{data($bdsycd)}' - {data($bodsysname)}='{data($bodsys1)}', 
                record {data($recnum2)}: {data($bdsycdname)}='{data($bdsycd)}' - {data($bodsysname)}='{data($bodsys2)}', , 
            </error>				
	]]>
	</rulexquery>
</sdsrule>

<!-- FDA: limited to AE, MH, CE -->
<sdsrule originator="FDA" id="SD1243" standard="SDTM" last-update="2020-08-08">
	<ruledescription>Value for --BDSYCD must be consistent within --BODSYS - </ruledescription>
	<ruledetaileddescription>All values of Body System or Organ Class Code (--BDSYCD) should be the same for a given value of Body System or Organ Class (--BODSYS)</ruledetaileddescription>
	<igversion>3.1.2</igversion>
	<igversion>3.1.3</igversion>
	<igversion>3.2</igversion>
	<domain>AE</domain>
	<domain>MH</domain>
	<domain>CE</domain>
	<synonym context="CDISC">CG0083</synonym>
	<rulexquery><![CDATA[
(: Rule SD1243 - Inconsistent value for --BDSYCD within --BODSYS :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' 
let $define := 'define2-0-0-example-sdtm.xml'  :)
let $definedoc := doc(concat($base,$define))
(: Iterate over the EVENTS and INTERVENTIONS datasets :)
for $itemgroupdef in $definedoc//odm:ItemGroupDef[@Name='AE' or @Domain='AE' or @Name='MH' or @Domain='MH' or @Name='CE' or @Domain='CE']
    let $name := $itemgroupdef/@Name
	let $datasetname := (
		if($defineversion = '2.1') then $itemgroupdef/def21:leaf/@xlink:href
		else $itemgroupdef/def:leaf/@xlink:href
	)
    let $datasetdoc := (
		if($datasetname) then doc(concat($base,$datasetname))
		else ()
	)
    (: and get the OID of --BODSYS and of --BDSYCD :)
    let $bdsycdoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'BDSYCD')]/@OID 
        where $a = $itemgroupdef/odm:ItemRef/@ItemOID
        return $a
    )
    let $bdsycdname := $definedoc//odm:ItemDef[@OID=$bdsycdoid]/@Name
    let $bodsysoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'BODSYS')]/@OID 
        where $a = $itemgroupdef/odm:ItemRef/@ItemOID
        return $a
    )
    let $bodsysname := $definedoc//odm:ItemDef[@OID=$bodsysoid]/@Name
    (: group all the records by --BODSYS value: all the records in the group will have the same value for --BODSYS :)
    let $orderedrecords := (
        for $record in $datasetdoc//odm:ItemGroupData
        group by 
        $b := $record/odm:ItemData[@ItemOID=$bodsysoid]/@Value
        return element group {  
            $record
        }
    )
    (: iterate over all groups - each having the same value of --BODSYS for all records within the group :)
    for $group in $orderedrecords[$bdsycdoid and $bodsysoid]
        (: get the value of --BODSYS from the first record only :)
        let $bodsys := $group/odm:ItemGroupData[1]/odm:ItemData[@ItemOID=$bodsysoid]/@Value
        (: get the first value of --BDSYCD :)
        let $bdsycd1 := $group/odm:ItemGroupData[1]/odm:ItemData[@ItemOID=$bdsycdoid]/@Value
        let $recnum1 := $group/odm:ItemGroupData[1]/@data:ItemGroupDataSeq
        (: return <test>{data($bodsys)} - {data($bdsycd)}</test> :)
        (: iterate over all other records in the same group, they have the same value for --BODSYS,
        and must also have the same value for --BDSYCD :)
        for $record in $group/odm:ItemGroupData[position()>1]
            let $recnum2 := $record/@data:ItemGroupDataSeq
            (: get the value of --BDSYCD :)
            let $bdsycd2 := $record/odm:ItemData[@ItemOID=$bdsycdoid]/@Value
            (: the two --BDSYCD values must be equal, except when they are null :)
            where $bodsys!='' and $bdsycd1 != '' and $bdsycd2 != '' and not($bdsycd1 = $bdsycd2)
            return <error rule="SD1243" rulelastupdate="2020-08-08" dataset="{data($name)}" variable="{data($bdsycdname)}" recordnumber="{data($recnum2)}">Combination of {data($bdsycdname)} and {data($bodsysname)} in dataset {data($datasetname)} is inconsistent: 
                record {data($recnum1)}: {data($bdsycdname)}='{data($bdsycd1)}' - {data($bodsysname)}='{data($bodsys)}', 
                record {data($recnum2)}: {data($bdsycdname)}='{data($bdsycd2)}' - {data($bodsysname)}='{data($bodsys)}', , 
            </error>				
		]]>
	</rulexquery>
</sdsrule>

<sdsrule originator="FDA" id="SD1243-SD" standard="SDTM" last-update="2020-08-08" requiresDomainOrDataset="Yes">
	<ruledescription>Value for --BDSYCD must be consistent within --BODSYS - </ruledescription>
	<ruledetaileddescription>All values of Body System or Organ Class Code (--BDSYCD) should be the same for a given value of Body System or Organ Class (--BODSYS)</ruledetaileddescription>
	<igversion>3.1.2</igversion>
	<igversion>3.1.3</igversion>
	<igversion>3.2</igversion>
	<domain>AE</domain>
	<domain>MH</domain>
	<domain>CE</domain>
	<synonym context="CDISC">CG0083</synonym>
	<rulexquery><![CDATA[
(: Rule SD1243 - Inconsistent value for --BDSYCD within --BODSYS :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
declare variable $datasetname external;
(: let $base := '/db/fda_submissions/cdisc01/' 
let $define := 'define2-0-0-example-sdtm.xml'  :)
let $definedoc := doc(concat($base,$define))
(: Iterate over the EVENTS and INTERVENTIONS datasets :)
for $itemgroupdef in $definedoc//odm:ItemGroupDef[@Name=$datasetname][@Domain='AE' or @Domain='MH' or @Domain='CE']
    let $name := $itemgroupdef/@Name
	let $dsname := (
		if($defineversion = '2.1') then $itemgroupdef/def21:leaf/@xlink:href
		else $itemgroupdef/def:leaf/@xlink:href
	)
    let $datasetdoc := (
		if($dsname) then doc(concat($base,$dsname))
		else ()
	)
    (: and get the OID of --BODSYS and of --BDSYCD :)
    let $bdsycdoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'BDSYCD')]/@OID 
        where $a = $itemgroupdef/odm:ItemRef/@ItemOID
        return $a
    )
    let $bdsycdname := $definedoc//odm:ItemDef[@OID=$bdsycdoid]/@Name
    let $bodsysoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'BODSYS')]/@OID 
        where $a = $itemgroupdef/odm:ItemRef/@ItemOID
        return $a
    )
    let $bodsysname := $definedoc//odm:ItemDef[@OID=$bodsysoid]/@Name
    (: group all the records by --BODSYS value: all the records in the group will have the same value for --BODSYS :)
    let $orderedrecords := (
        for $record in $datasetdoc//odm:ItemGroupData
        group by 
        $b := $record/odm:ItemData[@ItemOID=$bodsysoid]/@Value
        return element group {  
            $record
        }
    )
    (: iterate over all groups - each having the same value of --BODSYS for all records within the group :)
    for $group in $orderedrecords[$bdsycdoid and $bodsysoid]
        (: get the value of --BODSYS from the first record only :)
        let $bodsys := $group/odm:ItemGroupData[1]/odm:ItemData[@ItemOID=$bodsysoid]/@Value
        (: get the first value of --BDSYCD :)
        let $bdsycd1 := $group/odm:ItemGroupData[1]/odm:ItemData[@ItemOID=$bdsycdoid]/@Value
        let $recnum1 := $group/odm:ItemGroupData[1]/@data:ItemGroupDataSeq
        (: return <test>{data($bodsys)} - {data($bdsycd)}</test> :)
        (: iterate over all other records in the same group, they have the same value for --BODSYS,
        and must also have the same value for --BDSYCD :)
        for $record in $group/odm:ItemGroupData[position()>1]
            let $recnum2 := $record/@data:ItemGroupDataSeq
            (: get the value of --BDSYCD :)
            let $bdsycd2 := $record/odm:ItemData[@ItemOID=$bdsycdoid]/@Value
            (: the two --BDSYCD values must be equal, except when they are null :)
            where $bodsys!='' and $bdsycd1 != '' and $bdsycd2 != '' and not($bdsycd1 = $bdsycd2)
            return <error rule="SD1243" rulelastupdate="2020-08-08" dataset="{data($name)}" variable="{data($bdsycdname)}" recordnumber="{data($recnum2)}">Combination of {data($bdsycdname)} and {data($bodsysname)} in dataset {data($datasetname)} is inconsistent: 
                record {data($recnum1)}: {data($bdsycdname)}='{data($bdsycd1)}' - {data($bodsysname)}='{data($bodsys)}', 
                record {data($recnum2)}: {data($bdsycdname)}='{data($bdsycd2)}' - {data($bodsysname)}='{data($bodsys)}', , 
            </error>				
		]]>
	</rulexquery>
</sdsrule>


<sdsrule originator="FDA" id="SD1280" standard="SDTM" last-update="2020-08-08">
	<ruledescription>--TOX variable may not be present when --TOXGR variable is missing</ruledescription>
	<ruledetaileddescription>Toxicity (--TOX) variable should not be present in the domain, when the Toxicity Grade (--TOXGR) variable is missing</ruledetaileddescription>
	<igversion>3.1.2</igversion>
	<igversion>3.1.3</igversion>
	<igversion>3.2</igversion>
	<domain>LB</domain>
	<domain>AE</domain>
	<synonym context="CDISC">SD1280</synonym>
	<rulexquery><![CDATA[
(: Rule SD1280: For LB and AE: --TOX variable may not be present when --TOXGR variable is missing :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: Iterate over the LB and AE datasets - keep 'splitted' datasets into account :)
for $itemgroupdef in $definedoc//odm:ItemGroupDef[starts-with(@Name,'LB') or starts-with(@Name,'AE')]
    let $name := $itemgroupdef/@Name
	let $datasetname := (
		if($defineversion = '2.1') then $itemgroupdef/def21:leaf/@xlink:href
		else $itemgroupdef/def:leaf/@xlink:href
	)
    let $datasetdoc := (
		if($datasetname) then doc(concat($base,$datasetname))
		else ()
	)
    (: and get the OID and name of --TOXGR and of ----TOX :)
    let $toxgroid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'TOXGR')]/@OID 
        where $a = $itemgroupdef/odm:ItemRef/@ItemOID
        return $a
    )
    let $toxgrname := $definedoc//odm:ItemDef[@OID=$toxgroid]/@Name
    let $toxoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'TOX')]/@OID 
        where $a = $itemgroupdef/odm:ItemRef/@ItemOID
        return $a
    )
    let $toxname := $definedoc//odm:ItemDef[@OID=$toxoid]/@Name
    (: When --TOXGR is absent than --TOX must be absent :)
    where $datasetdoc and not($toxgroid) and $toxoid (: error when --TOXGR is absent and --TOX is present :)
    return 
    <error rule="SD1280" rulelastupdate="2020-08-08" dataset="{data($name)}" variable="{data($toxname)}">{data($toxname)} is present, although {data($toxgrname)} is absent in dataset {data($name)}</error>	
		]]>
	</rulexquery>
</sdsrule>

<sdsrule originator="FDA" id="SD0041" standard="SDTM" last-update="2020-08-08">
	<ruledescription>--OCCUR may only be populated, when --PRESP = 'Y'</ruledescription>
	<ruledetaileddescription>Occurrence (--OCCUR) may only be populated, when a given Intervention or Event has been pre-specified (--PRESP = 'Y')</ruledetaileddescription>
	<igversion>3.1.3</igversion>
	<igversion>3.2</igversion>
	<domain>INTERVENTIONS</domain>
	<domain>EVENTS</domain>
	<rulexquery><![CDATA[
(: Rule SD0041: Occurrence (--OCCUR) may only be populated, when a given Intervention or Event has been pre-specified (--PRESP = 'Y')
 Applies to: INTERVENTIONS, EVENTS :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external;  
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' 
let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: Iterate over the EVENTS and INTERVENTIONS datasets :)
for $itemgroupdef in $definedoc//odm:ItemGroupDef[upper-case(@def:Class)='INTERVENTIONS' or upper-case(@def:Class)='EVENTS' 
		or upper-case(./def21:Class/@Name)='INTERVENTIONS' or upper-case(./def21:Class/@Name)='EVENTS']
    let $name := $itemgroupdef/@Name
	let $datasetname := (
		if($defineversion = '2.1') then $itemgroupdef/def21:leaf/@xlink:href
		else $itemgroupdef/def:leaf/@xlink:href
	)
    let $datasetdoc := (
		if($datasetname) then doc(concat($base,$datasetname))
		else ()
	)
    (: and get the OID of --OCCUR and of --PRESP :)
    let $occuroid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'OCCUR')]/@OID 
        where $a = $itemgroupdef/odm:ItemRef/@ItemOID
        return $a
    )
    let $occurname := $definedoc//odm:ItemDef[@OID=$occuroid]/@Name
    let $prespoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'PRESP')]/@OID 
        where $a = $itemgroupdef/odm:ItemRef/@ItemOID
        return $a
    )
    let $prespname := $definedoc//odm:ItemDef[@OID=$prespoid]/@Name
    (: iterate over all records for which --PRESP is NOT 'Y' :)
    for $record in $datasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$prespoid and not(@Value='Y')] or not(odm:ItemData[@ItemOID=$prespoid])]
        let $recnum := $record/@data:ItemGroupDataSeq
        (: get the value of --OCCUR - if any:)
        let $occur := $record/odm:ItemData[@ItemOID=$occuroid]/@Value
        (: occur must be empty or null :)
        where string-length($occur) > 0
        return <error rule="SD0041" rulelastupdate="2020-08-08" dataset="{data($name)}" variable="{data($occurname)}" recordnumber="{data($recnum)}">{data($occurname)} is populated, value={data($occur)} although {data($prespname)} is not populated or absent</error>		
	]]>
	</rulexquery>
</sdsrule>

<!-- Rule SD1281 applies to INTERVENTIONS, EVENTS, NOT(AE, DS, DV, EX) -->
<sdsrule originator="FDA" id="SD1281" standard="SDTM" last-update="2020-08-08">
	<ruledescription>--OCCUR variable may not be present when --PRESP variable is missing</ruledescription>
	<ruledetaileddescription>Occurrence (--OCCUR) variable should not be present in the domain, when the Pre-specified (--PRESP) variable is missing</ruledetaileddescription>
	<igversion>3.1.2</igversion>
	<igversion>3.1.3</igversion>
	<igversion>3.2</igversion>
	<domain>EVENTS</domain>
	<domain>INTERVENTIONS</domain>
	<synonym context="CDISC">CG0088</synonym>
	<rulexquery><![CDATA[
(: Rule SD1281 - When --PRESP not present in dataset then --OCCUR not present in dataset
Applies to: EVENTS,INTERVENTIONS, NOT(DS, DV, AE) :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external; 
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: Applies to: EVENTS,INTERVENTIONS but not to AE, DS, DV :)
for $itemgroupdef in $definedoc//odm:ItemGroupDef[upper-case(@def:Class)='INTERVENTIONS' or upper-case(@def:Class)='EVENTS' 
		or upper-case(./def21:Class/@Name)='INTERVENTIONS' or upper-case(./def21:Class/@Name)='EVENTS'
		and not(@Domain='AE') and not(@Domain='DS') and not(@Domain='DV') and not(@Domain='EX')]
    let $name := $itemgroupdef/@Name
    (: get the dataset :)
    let $name := $itemgroupdef/@Name
    let $datasetlocation := (
		if($defineversion = '2.1') then $itemgroupdef/def21:leaf/@xlink:href
		else $itemgroupdef/def:leaf/@xlink:href
	)
    let $datasetdoc := (
		if($datasetlocation) then doc(concat($base,$datasetlocation))
		else ()
	)
    (: get the OID and name of --PRESP :)
    let $prespoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'PRESP')]/@OID 
        where $a = $itemgroupdef/odm:ItemRef/@ItemOID
        return $a
    )
    let $prespname := $definedoc//odm:ItemDef[@OID=$prespoid]/@Name
    (: get the OID of --OCCUR :)
     let $occuroid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'OCCUR')]/@OID 
        where $a = $itemgroupdef/odm:ItemRef/@ItemOID
        return $a
    )
    let $occurname := $definedoc//odm:ItemDef[@OID=$occuroid]/@Name
    (: When --PRESP not present in dataset then --OCCUR not present in dataset :)
    where not($prespoid) and $occuroid
    return 
         <error rule="SD1281" rulelastupdate="2020-08-08" dataset="{data($name)}" variable="{data($occurname)}" >Variable {data($occurname)} is present in dataset {data($name)} although {data($prespname)} is not present in the dataset</error>			
		]]>
	</rulexquery>
</sdsrule>
	
<sdsrule originator="FDA" id="SD1245" standard="SDTM" last-update="2020-08-08">
	<ruledescription>--TPTREF must be present in the dataset when --RFTDTC present</ruledescription>
	<ruledetaileddescription>Time Point Reference (--TPTREF) variable must be included into the domain, when Date/Time of Reference Point (--RFTDTC) variable is present</ruledetaileddescription>
	<igversion>3.1.2</igversion>
	<igversion>3.1.3</igversion>
	<igversion>3.2</igversion>
	<domain>ALL</domain>
	<synonym context="CDISC">CG0090</synonym>
	<rulexquery><![CDATA[
(: Rule SD1245 - When --RFTDTC present in dataset then --TPTREF present in dataset :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdiscpilot01/' :)
(: let $define := 'define_2_0.xml' :)
let $definedoc := doc(concat($base,$define))
(: Applies to: all datasets :)
for $itemgroupdef in $definedoc//odm:ItemGroupDef
    let $name := $itemgroupdef/@Name
    let $domain := (
        if($itemgroupdef/@Domain) then $itemgroupdef/@Domain
        else substring($name,1,2) (: first 2 characters :)
    )
    (: get the dataset :)
    let $name := $itemgroupdef/@Name
    (: get the OID and name of --RFTDTC :)
    let $rftdtcoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'RFTDTC')]/@OID 
        where $a = $itemgroupdef/odm:ItemRef/@ItemOID
        return $a
    )
    let $rftdtcname := $definedoc//odm:ItemDef[@OID=$rftdtcoid]/@Name
    (: get the OID of --TPTREF (if any) :)
    let $tptrefoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'TPTREF')]/@OID 
        where $a = $itemgroupdef/odm:ItemRef/@ItemOID
        return $a
    )
    (: if there is no --TPTREF, we cannot know its full name, so we must then construct it :)
    let $tptrefname := (
        if($tptrefoid) then $definedoc//odm:ItemDef[@OID=$tptrefoid]/@Name
        else concat($domain,'TPTREF')
    )
    (: --RFTDTC present in dataset then --TPTREF must present in dataset :)
    where $rftdtcoid and not($tptrefoid)
    return
        <error rule="SD1245" rulelastupdate="2020-08-08" dataset="{data($name)}" variable="{data($tptrefname)}" >Variable {data($rftdtcname)} is present in dataset {data($name)} but {data($tptrefname)} is not present in the dataset</error>				
		]]>
	</rulexquery>
</sdsrule>

<!-- SD0032 applies to INTERVENTIONS, EVENTS, FINDINGS, CO -->
<sdsrule originator="FDA" id="SD0032" standard="SDTM" last-update="2020-08-08">
	<ruledescription>Value for --TPT must be provided, when --TPTNUM is provided</ruledescription>
	<ruledetaileddescription>Planned Time Point Name (--TPT) should not be NULL, when Planned Time Point Number (--TPTNUM) is populated</ruledetaileddescription>
	<igversion>3.1.2</igversion>
	<igversion>3.1.3</igversion>
	<igversion>3.2</igversion>
	<domain>INTERVENTIONS</domain>
	<domain>EVENTS</domain>
	<domain>FINDINGS</domain>
	<domain>CO</domain>
	<rulexquery><![CDATA[
(: Rule SD0032: Missing value for --TPT, when --TPTNUM is provided :)
(: Applies to: INTERVENTIONS, EVENTS, FINDINGS, CO :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external;  
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' 
let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: Iterate over the INTERVENTIONS, EVENTS, FINDINGS, and CO datasets :)
for $itemgroupdef in $definedoc//odm:ItemGroupDef[upper-case(@def:Class)='INTERVENTIONS' or upper-case(@def:Class)='EVENTS' or upper-case(@def:Class)='FINDINGS' 
		or upper-case(./def21:Class/@Name)='INTERVENTIONS' or upper-case(./def21:Class/@Name)='EVENTS' or upper-case(./def21:Class/@Name)='FINDINGS'
		or @Domain='CO']
    let $name := $itemgroupdef/@Name
    (: get the dataset location and the dataset document itself :)
    let $datasetname := (
		if($defineversion = '2.1') then $itemgroupdef/def21:leaf/@xlink:href
		else $itemgroupdef/def:leaf/@xlink:href
	)
    let $datasetdoc := (
		if($datasetname) then doc(concat($base,$datasetname))
		else ()
	)
    (: and get the OID of --TPT and of --TPTNUM and their name :)
    let $tptoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'TPT')]/@OID 
        where $a = $itemgroupdef/odm:ItemRef/@ItemOID
        return $a
    )
    let $tptname := $definedoc//odm:ItemDef[@OID=$tptoid]/@Name
    let $tptnumoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'TPTNUM')]/@OID 
        where $a = $itemgroupdef/odm:ItemRef/@ItemOID
        return $a
    )
    let $tptnumname := $definedoc//odm:ItemDef[@OID=$tptnumoid]/@Name
    (: iterate over all the records in the dataset that have --TPTNUM populated :)
    for $record in $datasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$tptnumoid]/@Value]
        let $recnum := $record/@data:ItemGroupDataSeq
        (: get the value of --TPTNUM :)
        let $tptnum := $record/odm:ItemData[@ItemOID=$tptnumoid]/@Value
        (: get the value of --TPT (if any) :)
        let $tpt := $record/odm:ItemData[@ItemOID=$tptoid]/@Value
        (: --TPT must be populated :)
        where not($tpt)
        return <error rule="SD0032" rulelastupdate="2020-08-08" dataset="{data($name)}" variable="{data($tptname)}" recordnumber="{data($recnum)}">Variable {data($tptname)} is not populated although variable {data($tptnumname)} is populated, value={data($tptnum)} </error>				
	]]>
	</rulexquery>
</sdsrule>

<!-- SD0032 applies to INTERVENTIONS, EVENTS, FINDINGS, CO -->
<sdsrule originator="FDA" id="SD0032-SD" standard="SDTM" last-update="2020-08-08" requiresDomainOrDataset="Yes">
	<ruledescription>Value for --TPT must be provided, when --TPTNUM is provided</ruledescription>
	<ruledetaileddescription>Planned Time Point Name (--TPT) should not be NULL, when Planned Time Point Number (--TPTNUM) is populated</ruledetaileddescription>
	<igversion>3.1.2</igversion>
	<igversion>3.1.3</igversion>
	<igversion>3.2</igversion>
	<domain>INTERVENTIONS</domain>
	<domain>EVENTS</domain>
	<domain>FINDINGS</domain>
	<domain>CO</domain>
	<rulexquery><![CDATA[
(: Rule SD0032: Missing value for --TPT, when --TPTNUM is provided :)
(: Applies to: INTERVENTIONS, EVENTS, FINDINGS, CO :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external;  
declare variable $defineversion external;
declare variable $datasetname external;
(: let $base := '/db/fda_submissions/cdisc01/' 
let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: Iterate over the INTERVENTIONS, EVENTS, FINDINGS, and CO datasets :)
for $itemgroupdef in $definedoc//odm:ItemGroupDef[@Name=$datasetname][upper-case(@def:Class)='INTERVENTIONS' or upper-case(@def:Class)='EVENTS' or upper-case(@def:Class)='FINDINGS' 
		or upper-case(./def21:Class/@Name)='INTERVENTIONS' or upper-case(./def21:Class/@Name)='EVENTS' or upper-case(./def21:Class/@Name)='FINDINGS' 
		or @Domain='CO']
    let $name := $itemgroupdef/@Name
    (: get the dataset location and the dataset document itself :)
	let $datasetname := (
		if($defineversion = '2.1') then $itemgroupdef/def21:leaf/@xlink:href
		else $itemgroupdef/def:leaf/@xlink:href
	)
    let $datasetdoc := (
		if($datasetname) then doc(concat($base,$datasetname))
		else ()
	)
    (: and get the OID of --TPT and of --TPTNUM and their name :)
    let $tptoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'TPT')]/@OID 
        where $a = $itemgroupdef/odm:ItemRef/@ItemOID
        return $a
    )
    let $tptname := $definedoc//odm:ItemDef[@OID=$tptoid]/@Name
    let $tptnumoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'TPTNUM')]/@OID 
        where $a = $itemgroupdef/odm:ItemRef/@ItemOID
        return $a
    )
    let $tptnumname := $definedoc//odm:ItemDef[@OID=$tptnumoid]/@Name
    (: iterate over all the records in the dataset that have --TPTNUM populated :)
    for $record in $datasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$tptnumoid]/@Value]
        let $recnum := $record/@data:ItemGroupDataSeq
        (: get the value of --TPTNUM :)
        let $tptnum := $record/odm:ItemData[@ItemOID=$tptnumoid]/@Value
        (: get the value of --TPT (if any) :)
        let $tpt := $record/odm:ItemData[@ItemOID=$tptoid]/@Value
        (: --TPT must be populated :)
        where not($tpt)
        return <error rule="SD0032" rulelastupdate="2020-08-08" dataset="{data($name)}" variable="{data($tptname)}" recordnumber="{data($recnum)}">Variable {data($tptname)} is not populated although variable {data($tptnumname)} is populated, value={data($tptnum)} </error>				
	]]>
	</rulexquery>
</sdsrule>

<sdsrule originator="FDA" id="SD1244" standard="SDTM" last-update="2020-08-08">
	<ruledescription>--TPT variable must be present, when --TPTNUM variable is present</ruledescription>
	<ruledetaileddescription>Planned Time Point Name (--TPT) variable must be included into the domain, when Planned Time Point Number (--TPTNUM) variable is present</ruledetaileddescription>
	<igversion>3.1.2</igversion>
	<igversion>3.1.3</igversion>
	<igversion>3.2</igversion>
	<domain>ALL</domain>
	<synonym context="CDISC">CG0091</synonym>
	<rulexquery><![CDATA[
(: Rule SD1244 - When --TPTNUM present in dataset then --TPT must be present in dataset :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdiscpilot01/' :)
(: let $define := 'define_2_0.xml' :)
let $definedoc := doc(concat($base,$define))
(: Applies to: all datasets :)
for $itemgroupdef in $definedoc//odm:ItemGroupDef
    let $name := $itemgroupdef/@Name
    let $domain := (
        if($itemgroupdef/@Domain) then $itemgroupdef/@Domain
        else substring($name,1,2) (: first 2 characters :)
    )
    (: get the dataset :)
    let $name := $itemgroupdef/@Name
    (: get the OID and name of --TPTNUM :)
    let $tptnumoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'TPTNUM')]/@OID 
        where $a = $itemgroupdef/odm:ItemRef/@ItemOID
        return $a
    )
    let $tptnumname := $definedoc//odm:ItemDef[@OID=$tptnumoid]/@Name
    (: get the OID of --TPT (if any) :)
    let $tptoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'TPT') and string-length(@Name)=5]/@OID 
        where $a = $itemgroupdef/odm:ItemRef/@ItemOID
        return $a
    )
    (: if there is no --TPT, we cannot know its full name, so we must then construct it :)
    let $tptname := (
        if($tptoid) then $definedoc//odm:ItemDef[@OID=$tptoid]/@Name
        else concat($domain,'TPT')
    )
    (: When --TPTNUM present in dataset then --TPT present in dataset :)
    where $tptnumoid and not($tptoid)
    return
        <error rule="SD1244" rulelastupdate="2020-08-08" dataset="{data($name)}" variable="{data($tptname)}" >Variable {data($tptnumname)} is present in dataset {data($name)} but {data($tptname)} is not present in the dataset</error>			
		]]>
	</rulexquery>
</sdsrule>

<sdsrule originator="FDA" id="SD1246" standard="SDTM" last-update="2020-08-08">
	<ruledescription>--TPTREF variable must be present in dataset, when --ELTM variable is present</ruledescription>
	<ruledetaileddescription>Time Point Reference (--TPTREF) variable must be included into the domain, when Planned Elapsed Time from Time Point Ref (--ELTM) variable is present</ruledetaileddescription>
	<igversion>3.1.2</igversion>
	<igversion>3.1.3</igversion>
	<igversion>3.2</igversion>
	<domain>ALL</domain>
	<synonym context="CDISC">CG0092</synonym>
	<rulexquery><![CDATA[
(: Rule SD1246 - When --ELTM present in dataset then --TPTREF present in dataset :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdiscpilot01/' :)
(: let $define := 'define_2_0.xml' :)
let $definedoc := doc(concat($base,$define))
(: Applies to: all datasets :)
for $itemgroupdef in $definedoc//odm:ItemGroupDef
    let $name := $itemgroupdef/@Name
    let $domain := (
        if($itemgroupdef/@Domain) then $itemgroupdef/@Domain
        else substring($name,1,2) (: first 2 characters :)
    )
    (: get the dataset :)
    let $name := $itemgroupdef/@Name
    (: get the OID and name of --ELTM :)
    let $eltmoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'ELTM')]/@OID 
        where $a = $itemgroupdef/odm:ItemRef/@ItemOID
        return $a
    )
    let $eltmname := $definedoc//odm:ItemDef[@OID=$eltmoid]/@Name
    (: get the OID of --TPTREF (if any) :)
    let $tptrefoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'TPTREF')]/@OID 
        where $a = $itemgroupdef/odm:ItemRef/@ItemOID
        return $a
    )
    (: if there is no --TPTREF, we cannot know its full name, so we must then construct it :)
    let $tptrefname := (
        if($tptrefoid) then $definedoc//odm:ItemDef[@OID=$tptrefoid]/@Name
        else concat($domain,'TPTREF')
    )
    (: When --ELTM present in dataset then --TPTREF present in dataset :)
    where $eltmoid and not($tptrefoid)
    return
        <error rule="SD1246" rulelastupdate="2020-08-08" dataset="{data($name)}" variable="{data($tptrefname)}" >Variable {data($eltmname)} is present in dataset {data($name)} but {data($tptrefname)} is not present in the dataset</error>				
		]]>
	</rulexquery>
</sdsrule>

<sdsrule originator="FDA" id="SD1282" standard="SDTM" last-update="2020-08-08">
	<ruledescription>When --ELTM,  --TPTNUM,  and --TPT is not present in the dataset then --TPTREF may not be present in the dataset</ruledescription>
	<ruledetaileddescription>Time Point Reference (--TPTREF) variable should not be present in the domain, when the Planned Elapsed Time from Time Point Ref (--ELTM) variable, Planned Time Point Number (--TPTNUM) variable, and Planned Time Point (--TPT) variable are missing</ruledetaileddescription>
	<igversion>3.1.2</igversion>
	<igversion>3.1.3</igversion>
	<igversion>3.2</igversion>
	<domain>ALL</domain>
	<synonym context="CDISC">CG0093</synonym>
	<rulexquery><![CDATA[
(: Rule SD1282 - When --ELTM,  --TPTNUM,  and --TPT not present in dataset then --TPTREF not present in dataset :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdiscpilot01/' :)
(: let $define := 'define_2_0.xml' :)
let $definedoc := doc(concat($base,$define))
(: Applies to: all datasets :)
for $itemgroupdef in $definedoc//odm:ItemGroupDef
    let $name := $itemgroupdef/@Name
    let $domain := (
        if($itemgroupdef/@Domain) then $itemgroupdef/@Domain
        else substring($name,1,2) (: first 2 characters :)
    )
    (: get the dataset :)
    let $name := $itemgroupdef/@Name
    (: get the OID and name of --ELTM :)
    let $eltmoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'ELTM')]/@OID 
        where $a = $itemgroupdef/odm:ItemRef/@ItemOID
        return $a
    )
    (: if there is no --ELTM, we do also not know its full name, so we must construct it :)
    let $eltmname := (
        if($eltmoid) then $definedoc//odm:ItemDef[@OID=$eltmoid]/@Name
        else concat($domain,'ELTM')
    )
    (: get the OID of --TPTNUM (if any) :)
    let $tptnumoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'TPTNUM')]/@OID 
        where $a = $itemgroupdef/odm:ItemRef/@ItemOID
        return $a
    )
    (: if there is no --TPTNUM, we cannot know its full name, so we must then construct it :)
    let $tptnumname := (
        if($tptnumoid) then $definedoc//odm:ItemDef[@OID=$tptnumoid]/@Name
        else concat($domain,'TPTNUM')
    )
    (: get the OID of --TPT :)
    let $tptoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'TPT') and string-length(@Name)=5]/@OID 
        where $a = $itemgroupdef/odm:ItemRef/@ItemOID
        return $a
    )
    (: if there is no --TPT, we cannot know its full name, so we must then construct it :)
    let $tptname := (
        if($tptoid) then $definedoc//odm:ItemDef[@OID=$tptoid]/@Name
        else concat($domain,'TPT')
    )
    (: get the OID and Name of --TPTREF :)
    let $tptrefoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'TPTREF')]/@OID 
        where $a = $itemgroupdef/odm:ItemRef/@ItemOID
        return $a
    )
    let $tptrefname := $definedoc//odm:ItemDef[@OID=$tptrefoid]/@Name (: if there is an OID, then there is a name :)
    (: When --ELTM,  --TPTNUM,  and --TPT not present in dataset then --TPTREF not present in dataset :)
    where not($eltmoid) and not($tptnumoid) and not($tptoid) and $tptrefoid
    return
        <error rule="SD1282" rulelastupdate="2020-08-08" dataset="{data($name)}" variable="{data($tptrefname)}" >Variable {data($tptrefname)} is present in dataset {data($name)} although {data($eltmname)}, {data($tptnumname)} and {data($tptname)} are not present in the dataset</error>		
		]]>
	</rulexquery>
</sdsrule>

<!-- was rule FDAC175 -->
<sdsrule id="SD0023" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>--STAT must be populated, when --REASND is populated</ruledescription>
<ruledetaileddescription>Completion Status (--STAT) should be set to 'NOT DONE', when Reason Not Done (--REASND) is populated</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>ALL</domain>
<rulexquery><![CDATA[	
(: Rule SD0023 - Missing value for --STAT, when --REASND is provided:
Completion Status (--STAT) should be set to 'NOT DONE', when Reason Not Done (--REASND) is populated
:)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: Find all "Findings", "Interventions" and "Events" datasets  :)
for $dataset in $definedoc//odm:ItemGroupDef[upper-case(@def:Class)='FINDINGS' or upper-case(@def:Class)='INTERVENTIONS' or upper-case(@def:Class)='EVENTS' 
		or upper-case(./def21:Class/@Name)='FINDINGS' or upper-case(./def21:Class/@Name)='INTERVENTIONS' or upper-case(./def21:Class/@Name)='EVENTS']
    let $name := $dataset/@Name
	let $datasetname := (
		if($defineversion = '2.1') then $dataset/def21:leaf/@xlink:href
		else $dataset/def:leaf/@xlink:href
	)
    let $datasetdoc := (
		if($datasetname) then doc(concat($base,$datasetname))
		else ()
	)
    (: Get the OIDs of the STAT and REASND variables :)
    let $statoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'STAT')]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
    )
    let $statname := $definedoc//odm:ItemDef[@OID=$statoid]/@Name
    let $reasndoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'REASND')]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
    )
    let $reasndname := $definedoc//odm:ItemDef[@OID=$reasndoid]/@Name
    (: iterate over each record in the dataset for which there is a REASND data point :)
    for $record in $datasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$reasndoid]]
        (: get the record number and the value for REASND :)
        let $recnum := $record/@data:ItemGroupDataSeq
        let $reasndvalue := $record/odm:ItemData[@ItemOID=$reasndoid]/@Value
        (: and check whether there is a corresponding xxSTAT :)
        let $statvalue := $record/odm:ItemData[@ItemOID=$statoid]/@Value
        (: check whether xxSTAT is absent or deviates from 'NOT DONE' :)
        where not($statvalue) or  not($statvalue='NOT DONE') 
        return <warning rule="SD0023" dataset="{data($name)}" variable="{data($statname)}" rulelastupdate="2020-08-08" recordnumber="{$recnum}">Missing or invalid value for {data($statname)} when {data($reasndname)} is provided - {data($reasndname)}={data($reasndvalue)} - {data($statname)}={data($statvalue)} in dataset {data($datasetname)}</warning>
]]></rulexquery>
</sdsrule>

<sdsrule id="SD0023-SD" last-update="2020-08-08" originator="FDA" standard="SDTM" requiresDomainOrDataset="Yes">
<ruledescription>--STAT must be populated, when --REASND is populated</ruledescription>
<ruledetaileddescription>Completion Status (--STAT) should be set to 'NOT DONE', when Reason Not Done (--REASND) is populated</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>ALL</domain>
<rulexquery><![CDATA[	
(: Rule SD0023 - Missing value for --STAT, when --REASND is provided:
Completion Status (--STAT) should be set to 'NOT DONE', when Reason Not Done (--REASND) is populated
:)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
declare variable $datasetname external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: Find all "Findings", "Interventions" and "Events" datasets  :)
for $dataset in $definedoc//odm:ItemGroupDef[@Name=$datasetname][upper-case(@def:Class)='FINDINGS' or upper-case(@def:Class)='INTERVENTIONS' or upper-case(@def:Class)='EVENTS' 
		or upper-case(./def21:Class/@Name)='FINDINGS' or upper-case(./def21:Class/@Name)='INTERVENTIONS' or upper-case(./def21:Class/@Name)='EVENTS']
    let $name := $dataset/@Name
	let $datasetname := (
		if($defineversion = '2.1') then $dataset/def21:leaf/@xlink:href
		else $dataset/def:leaf/@xlink:href
	)
    let $datasetdoc := (
		if($datasetname) then doc(concat($base,$datasetname))
		else ()
	)
    (: Get the OIDs of the STAT and REASND variables :)
    let $statoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'STAT')]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
    )
    let $statname := $definedoc//odm:ItemDef[@OID=$statoid]/@Name
    let $reasndoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'REASND')]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
    )
    let $reasndname := $definedoc//odm:ItemDef[@OID=$reasndoid]/@Name
    (: iterate over each record in the dataset for which there is a REASND data point :)
    for $record in $datasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$reasndoid]]
        (: get the record number and the value for REASND :)
        let $recnum := $record/@data:ItemGroupDataSeq
        let $reasndvalue := $record/odm:ItemData[@ItemOID=$reasndoid]/@Value
        (: and check whether there is a corresponding xxSTAT :)
        let $statvalue := $record/odm:ItemData[@ItemOID=$statoid]/@Value
        (: check whether xxSTAT is absent or deviates from 'NOT DONE' :)
        where not($statvalue) or  not($statvalue='NOT DONE') 
        return <warning rule="SD0023" dataset="{data($name)}" variable="{data($statname)}" rulelastupdate="2020-08-08" recordnumber="{$recnum}">Missing or invalid value for {data($statname)} when {data($reasndname)} is provided - {data($reasndname)}={data($reasndvalue)} - {data($statname)}={data($statvalue)} in dataset {data($datasetname)}</warning>	
]]></rulexquery>
</sdsrule>

<!-- Rule SD1283  applies to MI, MO, TU, VS, FA, SR, EX, EC, PR -->
<sdsrule originator="FDA" id="SD1283" standard="SDTM" last-update="2020-08-08">
		<ruledescription>--LAT variable may not be present when --LOC variable is missing</ruledescription>
		<ruledetaileddescription>Laterality (--LAT) variable should not be present in the domain, when the Location (--LOC) variable is missing</ruledetaileddescription>
		<igversion>3.1.3</igversion>
		<igversion>3.2</igversion>
		<domain>MI</domain>
		<domain>MO</domain>
		<domain>TU</domain>
		<domain>VS</domain>
		<domain>FA</domain>
		<domain>SR</domain>
		<domain>EX</domain>
		<domain>EC</domain>
		<domain>PR</domain>
		<synonym context="CDISC">CG0095</synonym>
		<rulexquery><![CDATA[
(: Rule SD1283 - When --LOC not present in dataset then --LAT not present in dataset
Applies to: MI, MO, TU, VS, FA, SR, EX, EC, PR :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdiscpilot01/' :)
(: let $define := 'define_2_0.xml' :)
let $definedoc := doc(concat($base,$define))
(: iterate over the MI, MO, TU, VS, FA, SR, EX, EC, PR :)
for $itemgroupdef in $definedoc//odm:ItemGroupDef[@Domain='MI' or starts-with(@Name,'MI') 
    or @Domain='MO' or starts-with(@Name,'MO') or @Domain='TU' or starts-with(@Name,'TU') 
    or @Domain='VS' or starts-with(@Name,'VS') or @Domain='FA' or starts-with(@Name,'FA')
    or @Domain='SR' or starts-with(@Name,'SR') or @Domain='EX' or starts-with(@Name,'EX')
    or @Domain='EC' or starts-with(@Name,'EC') or @Domain='EC' or starts-with(@Name,'EC')
    ]
    let $name := $itemgroupdef/@Name
    let $domain := (
        if($itemgroupdef/@Domain) then $itemgroupdef/@Domain
        else substring($name,1,2) (: first 2 characters of name :)
    )
    (: get the OID of --LOC (if any) :)
    let $locoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'LOC') and string-length(@Name)=5]/@OID 
        where $a = $itemgroupdef/odm:ItemRef/@ItemOID
        return $a
    )
    (: get the full name - in case there is no OID, we need to construct it :)
    let $locname := (
        if($locoid) then $definedoc//odm:ItemDef[@OID=$locoid]/@Name
        else concat($domain,'LOC')
    )
    (: get the OID and Name of --LAT :)
    (: get the OID of --LOC (if any) :)
    let $latoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'LAT') and string-length(@Name)=5]/@OID 
        where $a = $itemgroupdef/odm:ItemRef/@ItemOID
        return $a
    )
    (: get the full name - in case there is no OID, we need to construct it :)
    let $latname := (
        if($latoid) then $definedoc//odm:ItemDef[@OID=$latoid]/@Name
        else concat($domain,'LAT')
    )
    (: When --LOC not present in dataset then --LAT not present in dataset :)
    where not($locoid) and $latoid 
    return 
        <error rule="SD1283" rulelastupdate="2020-08-08" dataset="{data($name)}" variable="{data($latname)}">Variable {data($locname)} is absent in dataset {data($name)} but {data($latname)} is present in the dataset</error>
    	]]>
	</rulexquery>
</sdsrule>

<sdsrule originator="FDA" id="SD1247" standard="SDTM" last-update="2020-08-08">
	<ruledescription>ECDOSE must be greater than 0 when ECOCCUR does not equal 'N' and ECDOSTXT is null</ruledescription>
	<ruledetaileddescription>ECDOSE must not be set to zero (0) as an alternative method for indicating doses not taken, not given, or missed</ruledetaileddescription>
	<igversion>3.2</igversion>
	<domain>EC</domain>
	<synonym context="CDISC">CG0100</synonym>
	<rulexquery><![CDATA[
(: Rule SD1247: When ECOCCUR != 'N' and ECDOSTXT is null then ECDOSE > 0 :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: get the EC dataset(s) definition :)
for $itemgroupdef in $definedoc//odm:ItemGroupDef[@Domain='EC' or starts-with(@Name,'EC')]
    let $name := $itemgroupdef/@Name
    (: get the dataset itself :)
    let $datasetlocation := (
		if($defineversion = '2.1') then $itemgroupdef/def21:leaf/@xlink:href
		else $itemgroupdef/def:leaf/@xlink:href
	)
    let $datasetdoc := (
		if($datasetlocation) then doc(concat($base,$datasetlocation))
		else ()
	)
    (: get the OID of ECOCCUR, ECDOSTXT and ECDOSE :)
    let $ecoccuroid := (
        for $a in $definedoc//odm:ItemDef[@Name='ECOCCUR']/@OID 
        where $a = $itemgroupdef/odm:ItemRef/@ItemOID
        return $a
    )
    let $ecdostxtoid := (
        for $a in $definedoc//odm:ItemDef[@Name='ECDOSTXT']/@OID 
        where $a = $itemgroupdef/odm:ItemRef/@ItemOID
        return $a
    )
    (:  :)
    let $ecdoseoid := (
        for $a in $definedoc//odm:ItemDef[@Name='ECDOSE']/@OID 
        where $a = $itemgroupdef/odm:ItemRef/@ItemOID
        return $a
    )
    (: get all the records in the dataset for which ECOCCUR != 'N' and ECDOSTXT is null :)
    for $record in $datasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$ecoccuroid and @Value!='N'] and not(odm:ItemData[@ItemOID=$ecdostxtoid])]
        let $recnum := $record/@data:ItemGroupDataSeq
        (: get the value of ECDOSE (as text) :)
        let $dose := $record/odm:ItemData[@ItemOID=$ecdoseoid]/@Value
        (: ECDOSE > 0 :)
        where $dose castable as xs:double and xs:double($dose)<0
        return 
            <error rule="SD1247" dataset="{data($name)}" variable="ECDOSE" rulelastupdate="2020-08-08" recordnumber="{$recnum}">ECDOSE is not a number or is a non-positive number where ECOCCUR != 'N' and ECDOSTXT is null. The value found for ECDOSE is '{data($dose)}'</error>			
		]]>
	</rulexquery>
</sdsrule>
	
<!-- Remark: this rule has to do with that sponsors should not set ECDOSE=0 for a dose that was not given  -->
<sdsrule originator="FDA" id="SD1248" standard="SDTM" last-update="2020-08-08">
	<ruledescription>When ECOCCUR = 'N' then ECDOSE = null</ruledescription>
	<ruledetaileddescription>ECDOSE must either be null when ECOCCUR equals 'N', or ECDOSE must be greater than 0. ECDOSE would be greater than 0 (when ECOCCUR equals N) for records that represent specific dose amounts that are not taken, not given, or missed</ruledetaileddescription>
	<igversion>3.2</igversion>
	<synonym context="CDISC">CG0101</synonym>
	<domain>EC</domain>
	<rulexquery><![CDATA[
(: When ECOCCUR = 'N' then ECDOSE = null :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: get the EC dataset(s) definition :)
for $itemgroupdef in $definedoc//odm:ItemGroupDef[@Domain='EC' or starts-with(@Name,'EC')]
    let $name := $itemgroupdef/@Name
    (: get the dataset itself :)
	let $datasetlocation := (
		if($defineversion = '2.1') then $itemgroupdef/def21:leaf/@xlink:href
		else $itemgroupdef/def:leaf/@xlink:href
	)
    let $datasetdoc := (
		if($datasetlocation) then doc(concat($base,$datasetlocation))
		else ()
	)
    (: get the OID of ECOCCUR, ECDOSTXT and ECDOSE :)
    let $ecoccuroid := (
        for $a in $definedoc//odm:ItemDef[@Name='ECOCCUR']/@OID 
        where $a = $itemgroupdef/odm:ItemRef/@ItemOID
        return $a
    )
    (:  :)
    let $ecdoseoid := (
        for $a in $definedoc//odm:ItemDef[@Name='ECDOSE']/@OID 
        where $a = $itemgroupdef/odm:ItemRef/@ItemOID
        return $a
    )
    (: get all the records in the dataset for which ECOCCUR = 'N' :)
    for $record in $datasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$ecoccuroid and @Value='N']]
        let $recnum := $record/@data:ItemGroupDataSeq
        (: get the value of ECDOSE (as text) :)
        let $dose := $record/odm:ItemData[@ItemOID=$ecdoseoid]/@Value
        (: When ECOCCUR = 'N' then ECDOSE = null :)
        where $dose (: meaning that a dose is present and not null in this record :)
        return 
            <error rule="SD1248" dataset="{data($name)}" variable="ECDOSE" rulelastupdate="2020-08-08" recordnumber="{$recnum}">ECDOSE={data($dose)} is expected to be null as ECOCCUR='N'</error>			
		]]>
	</rulexquery>
</sdsrule>

<sdsrule originator="FDA" id="SD1249" standard="SDTM" last-update="2020-08-08">
	<ruledescription>When EXTRT = 'PLACEBO' then EXDOSE = 0</ruledescription>
	<ruledetaileddescription>Doses of placebo should be represented by EXTRT = 'PLACEBO' and EXDOSE = 0 (indicating 0 mg of active ingredient was taken or administered)</ruledetaileddescription>
	<igversion>3.1.2</igversion>
	<igversion>3.1.3</igversion>
	<igversion>3.2</igversion>
	<domain>EX</domain>
	<synonym context="CDISC">CG0102</synonym>
	<rulexquery><![CDATA[
(: When EXTRT = 'PLACEBO' then EXDOSE = 0 :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: get the EX dataset(s) definition :)
for $itemgroupdef in $definedoc//odm:ItemGroupDef[@Domain='EX' or starts-with(@Name,'EX')]
    let $name := $itemgroupdef/@Name
    (: get the dataset itself :)
	let $datasetlocation := (
		if($defineversion = '2.1') then $itemgroupdef/def21:leaf/@xlink:href
		else $itemgroupdef/def:leaf/@xlink:href
	)
    let $datasetdoc := (
		if($datasetlocation) then doc(concat($base,$datasetlocation))
		else ()
	)
    (: get the OID of EXTRT and EXDOSE :)
    let $extrtoid := (
        for $a in $definedoc//odm:ItemDef[@Name='EXTRT']/@OID 
        where $a = $itemgroupdef/odm:ItemRef/@ItemOID
        return $a
    )
    (: get the OID of EXDOSE :)
    let $exdoseoid := (
        for $a in $definedoc//odm:ItemDef[@Name='EXDOSE']/@OID 
        where $a = $itemgroupdef/odm:ItemRef/@ItemOID
        return $a
    )
    (: return <test>{data($extrtoid)}</test>		 :)
    (: get all the records in the dataset for which EXTRT='PLACEBO' :)
    for $record in $datasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$extrtoid and @Value='PLACEBO']]
        let $recnum := $record/@data:ItemGroupDataSeq
        (: get the value of EXDOSE (as text) :)
        let $exdose := $record/odm:ItemData[@ItemOID=$exdoseoid]/@Value
        (: When EXTRT = 'PLACEBO' then EXDOSE = 0 :)
        where $exdose castable as xs:double and xs:double($exdose)!= 0 
        return 
			<error rule="SD1249" dataset="{data($name)}" variable="EXDOSE" rulelastupdate="2020-08-08" recordnumber="{$recnum}">EXDOSE={data($exdose)} is expected to be 0 as EXTRT='PLACEBO'</error>	
		]]>
	</rulexquery>
</sdsrule>
	
<sdsrule originator="FDA" id="SD1251" standard="SDTM" last-update="2020-08-08">
	<ruledescription>When EXTRTV = null then EXVAMTU = null</ruledescription>
	<ruledetaileddescription>When EXTRTV = null then EXVAMTU must be null</ruledetaileddescription>
	<igversion>3.1.2</igversion>
	<igversion>3.1.3</igversion>
	<igversion>3.2</igversion>
	<domain>INTERVENTIONS</domain>
	<synonym context="CDISC">CG0108</synonym>
	<rulexquery><![CDATA[
(: Rule SD1251 - When EXTRTV = null then EXVAMTU = null :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: Iterate over the EX datasets :)
for $itemgroupdef in $definedoc//odm:ItemGroupDef[upper-case(@def:Class)='INTERVENTIONS' or upper-case(./def21:Class/@Name)='INTERVENTIONS']
    let $name := $itemgroupdef/@Name
    (: get the OID of EXTRTV :)
    let $extrtvoid := (
        for $a in $definedoc//odm:ItemDef[@Name='EXTRTV']/@OID 
        where $a = $itemgroupdef/odm:ItemRef/@ItemOID
        return $a
    )
    (: get the OID of EXVAMTU :)
    let $exvamutoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'EXVAMTU')]/@OID 
        where $a = $itemgroupdef/odm:ItemRef/@ItemOID
        return $a
    )
    (: get the dataset location and make it a document :)
	let $datasetlocation := (
		if($defineversion = '2.1') then $itemgroupdef/def21:leaf/@xlink:href
		else $itemgroupdef/def:leaf/@xlink:href
	)
    let $datasetdoc := (
		if($datasetlocation) then doc(concat($base,$datasetlocation))
		else ()
	)
    (: iterate over all records in the dataset for which EXTRTV is null (i.e. absent in Dataset-XML) :)
    for $record in $datasetdoc//odm:ItemGroupData[not(odm:ItemData[@ItemOID=$extrtvoid])]
        let $recnum := $record/@data:ItemGroupDataSeq
        (: and check whether there is a datapoint for EXVAMTU :)
        let $exvamtu := $record/odm:ItemData[@ItemOID=$exvamutoid]/@Value
        (: give an error when there is a EXVAMTU value :)
        where $exvamtu and $exvamtu!=''
        return
        <error rule="SD1251" dataset="{data($name)}" variable="EXVAMTU" recordnumber="{data($recnum)}" rulelastupdate="2020-08-08">EXVAMTU={data($exvamtu)} but was expected to be null as EXTRTV is null</error>			
		]]>
	</rulexquery>
</sdsrule>

<sdsrule originator="FDA" id="SD1251-SD" standard="SDTM" last-update="2020-08-08" requiresDomainOrDataset="Yes">
	<ruledescription>When EXTRTV = null then EXVAMTU = null</ruledescription>
	<ruledetaileddescription>When EXTRTV = null then EXVAMTU must be null</ruledetaileddescription>
	<igversion>3.1.2</igversion>
	<igversion>3.1.3</igversion>
	<igversion>3.2</igversion>
	<domain>INTERVENTIONS</domain>
	<synonym context="CDISC">CG0108</synonym>
	<rulexquery><![CDATA[
(: Rule SD1251 - When EXTRTV = null then EXVAMTU = null :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
declare variable $datasetname external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: Iterate over the EX datasets :)
for $itemgroupdef in $definedoc//odm:ItemGroupDef[@Name=$datasetname][upper-case(@def:Class)='INTERVENTIONS' or upper-case(./def21:Class/@Name)='INTERVENTIONS']
    let $name := $itemgroupdef/@Name
    (: get the OID of EXTRTV :)
    let $extrtvoid := (
        for $a in $definedoc//odm:ItemDef[@Name='EXTRTV']/@OID 
        where $a = $itemgroupdef/odm:ItemRef/@ItemOID
        return $a
    )
    (: get the OID of EXVAMTU :)
    let $exvamutoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'EXVAMTU')]/@OID 
        where $a = $itemgroupdef/odm:ItemRef/@ItemOID
        return $a
    )
    (: get the dataset location and make it a document :)
	let $datasetlocation := (
		if($defineversion = '2.1') then $itemgroupdef/def21:leaf/@xlink:href
		else $itemgroupdef/def:leaf/@xlink:href
	)
     let $datasetdoc := (
		if($datasetlocation) then doc(concat($base,$datasetlocation))
		else ()
	)
    (: iterate over all records in the dataset for which EXTRTV is null (i.e. absent in Dataset-XML) :)
    for $record in $datasetdoc//odm:ItemGroupData[not(odm:ItemData[@ItemOID=$extrtvoid])]
        let $recnum := $record/@data:ItemGroupDataSeq
        (: and check whether there is a datapoint for EXVAMTU :)
        let $exvamtu := $record/odm:ItemData[@ItemOID=$exvamutoid]/@Value
        (: give an error when there is a EXVAMTU value :)
        where $exvamtu and $exvamtu!=''
        return
        <error rule="SD1251" dataset="{data($name)}" variable="EXVAMTU" recordnumber="{data($recnum)}" rulelastupdate="2020-08-08">EXVAMTU={data($exvamtu)} but was expected to be null as EXTRTV is null</error>			
		]]>
	</rulexquery>
</sdsrule>	
	
<sdsrule originator="FDA" id="SD1047" standard="SDTM" last-update="2020-08-08">
	<ruledescription>Only one of Dose (--DOSE) or Dose Description (--DOSTXT) variables must be populated on the same record</ruledescription>
	<ruledetaileddescription>Only one of Dose (--DOSE) or Dose Description (--DOSTXT) variables must be populated on the same record</ruledetaileddescription>
	<igversion>3.1.2</igversion>
	<igversion>3.1.3</igversion>
	<igversion>3.2</igversion>
	<domain>INTERVENTIONS</domain>
	<synonym context="CDISC">CG0110</synonym>
	<rulexquery><![CDATA[
(: Rule SD1047 - Only one of Dose (--DOSE) or Dose Description (--DOSTXT) variables must be populated on the same record :)
(: corresponds to rule FDAC0155 :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink"; 
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: iterate over all the datasets , but only the INTERVENTIONS ones :)
for $dataset in $definedoc//odm:ItemGroupDef[upper-case(@def:Class)='INTERVENTIONS' or upper-case(./def21:Class/@Name)='INTERVENTIONS']
    let $name := $dataset/@Name
	let $datasetname := (
		if($defineversion = '2.1') then $dataset/def21:leaf/@xlink:href
		else $dataset/def:leaf/@xlink:href
	)
    let $datasetdoc := (
		if($datasetname) then doc(concat($base,$datasetname))
		else ()
	)
    (: get the OID of the --DOSE and --DOSTXT variables :)
    let $doseoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'DOSE')]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
    )
    let $dosename := $definedoc//odm:ItemDef[@OID=$doseoid]/@Name
    let $dostxtoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'DOSTXT')]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
    )
    let $dostxtname := $definedoc//odm:ItemDef[@OID=$dostxtoid]/@Name
    (: iterate over the records in the dataset, but ONLY when --DOSE and --DOSTXT were defined :)
    for $record in $datasetdoc[$doseoid and $dostxtoid]//odm:ItemGroupData
        let $recnum := $record/@data:ItemGroupDataSeq
        (: get the values of --DOSE and --DOSTXT (when present) :)
        let $dosevalue := $record/odm:ItemData[@ItemOID=$doseoid]/@Value
        let $dostxtvalue := $record/odm:ItemData[@ItemOID=$dostxtoid]/@Value
        (: When --DOSTXT != null then --DOSE = null :)
        where $dostxtvalue and $dosevalue (: --DOSTXT is populated and --DOSE is populated :)
        return <error rule="SD1047" dataset="{data($name)}" variable="{data($dosename)}" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">Both {data($dosename)} and {data($dostxtname)} are populated</error>			
		]]>
	</rulexquery>
</sdsrule>

<sdsrule originator="FDA" id="SD1047-SD" standard="SDTM" last-update="2020-08-08" requiresDomainOrDataset="Yes">
	<ruledescription>Only one of Dose (--DOSE) or Dose Description (--DOSTXT) variables must be populated on the same record</ruledescription>
	<ruledetaileddescription>Only one of Dose (--DOSE) or Dose Description (--DOSTXT) variables must be populated on the same record</ruledetaileddescription>
	<igversion>3.1.2</igversion>
	<igversion>3.1.3</igversion>
	<igversion>3.2</igversion>
	<domain>INTERVENTIONS</domain>
	<synonym context="CDISC">CG0110</synonym>
	<rulexquery><![CDATA[
(: Rule SD1047 - Only one of Dose (--DOSE) or Dose Description (--DOSTXT) variables must be populated on the same record :)
(: corresponds to rule FDAC0155 :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink"; 
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: iterate over all the datasets , but only the INTERVENTIONS ones :)
for $dataset in $definedoc//odm:ItemGroupDef[upper-case(@def:Class)='INTERVENTIONS' or upper-case(./def21:Class/@Name)='INTERVENTIONS']
    let $name := $dataset/@Name
	let $datasetname := (
		if($defineversion = '2.1') then $dataset/def21:leaf/@xlink:href
		else $dataset/def:leaf/@xlink:href
	)
    let $datasetdoc := (
		if($datasetname) then doc(concat($base,$datasetname))
		else ()
	)
    (: get the OID of the --DOSE and --DOSTXT variables :)
    let $doseoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'DOSE')]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
    )
    let $dosename := $definedoc//odm:ItemDef[@OID=$doseoid]/@Name
    let $dostxtoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'DOSTXT')]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
    )
    let $dostxtname := $definedoc//odm:ItemDef[@OID=$dostxtoid]/@Name
    (: iterate over the records in the dataset, but ONLY when --DOSE and --DOSTXT were defined :)
    for $record in $datasetdoc[$doseoid and $dostxtoid]//odm:ItemGroupData
        let $recnum := $record/@data:ItemGroupDataSeq
        (: get the values of --DOSE and --DOSTXT (when present) :)
        let $dosevalue := $record/odm:ItemData[@ItemOID=$doseoid]/@Value
        let $dostxtvalue := $record/odm:ItemData[@ItemOID=$dostxtoid]/@Value
        (: When --DOSTXT != null then --DOSE = null :)
        where $dostxtvalue and $dosevalue (: --DOSTXT is populated and --DOSE is populated :)
        return <error rule="SD1047" dataset="{data($name)}" variable="{data($dosename)}" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">Both {data($dosename)} and {data($dostxtname)} are populated</error>			
		]]>
	</rulexquery>
</sdsrule>

<sdsrule originator="FDA" id="SD0035" standard="SDTM" last-update="2020-08-08">
	<ruledescription>When --DOSE != null or --DOSTOT != null or --DOSTXT != null then --DOSU may not be null</ruledescription>
	<ruledetaileddescription>Dose Units (--DOSU) must be populated, when Dose per Administration (--DOSE), Dose Description (--DOSTXT) or Total Daily Dose (--DOSTOT) is provided</ruledetaileddescription>
	<igversion>3.1.2</igversion>
	<igversion>3.1.3</igversion>
	<igversion>3.2</igversion>
	<domain>INTERVENTIONS</domain>
	<synonym context="CDISC">CG0114</synonym>
	<rulexquery><![CDATA[
(: Rule SD0035 - When --DOSE != null or --DOSTOT != null or --DOSTXT != null then --DOSU != null :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink"; 
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: iterate over all the datasets , but only the INTERVENTIONS ones :)
for $dataset in $definedoc//odm:ItemGroupDef[upper-case(@def:Class)='INTERVENTIONS' or upper-case(./def21:Class/@Name)='INTERVENTIONS']
    (: get the dataset :)
    let $domain := $dataset/@Domain
    let $name := $dataset/@Name
    (: the prefix (first 2 characters) is either the domain name or the first characters of the dataset name (case splitted domains) :)
    let $prefix := (
        if($dataset/@Domain) then $dataset/@Domain
        else substring($name,1,2)
    )
	let $datasetname := (
		if($defineversion = '2.1') then $dataset/def21:leaf/@xlink:href
		else $dataset/def:leaf/@xlink:href
	)
    let $datasetdoc := (
		if($datasetname) then doc(concat($base,$datasetname))
		else ()
	)
    (: get the OID and name of the --DOSTOT variable :)
    let $dostotoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'DOSTOT')]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
    )
    let $dostotname := concat($prefix,'DOSTOT')
    (: get the OID and name of the --DOSE variable :)
    let $doseoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'DOSE')]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
    )
    let $dosename := concat($prefix,'DOSE')
    (: get the OID and name of the --DOSTXT variable :)
    let $dostxtoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'DOSTXT')]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
    )
    let $dostxtname := concat($prefix,'DOSTXT')
    (: get the OID and name of the DOSU variable :)
    let $dosuoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'DOSU')]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
    )
    let $dosuname := $definedoc//odm:ItemDef[@OID=$dosuoid]/@Name
    (: iterate over all record in the dataset for which there is a DOSE, DOSTXT or DOSTOT data point :) 
    for $record in $datasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$doseoid] or odm:ItemData[@ItemOID=$dostxtoid] or odm:ItemData[@ItemOID=$dostotoid]]
        let $recnum := $record/@data:ItemGroupDataSeq
        (: DOSEU must be populated :)
        where not($record/odm:ItemData[@ItemOID=$dosuoid])
        return <error rule="SD0035" dataset="{data($name)}" variable="{data($dosuname)}" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">{data($dosuname)} is not populated although one of {data($dosename)}, {data($dostxtname)} or {data($dostotname)} is populated</error>				
		]]>
	</rulexquery>
</sdsrule>

<sdsrule originator="FDA" id="SD0035-SD" standard="SDTM" last-update="2020-08-08" requiresDomainOrDataset="Yes">
	<ruledescription>When --DOSE != null or --DOSTOT != null or --DOSTXT != null then --DOSU must be != null</ruledescription>
	<ruledetaileddescription>Dose Units (--DOSU) must be populated, when Dose per Administration (--DOSE), Dose Description (--DOSTXT) or Total Daily Dose (--DOSTOT) is provided</ruledetaileddescription>
	<igversion>3.1.2</igversion>
	<igversion>3.1.3</igversion>
	<igversion>3.2</igversion>
	<domain>INTERVENTIONS</domain>
	<synonym context="CDISC">CG0114</synonym>
	<rulexquery><![CDATA[
(: Rule SD0035 - When --DOSE != null or --DOSTOT != null or --DOSTXT != null then --DOSU != null :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink"; 
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
declare variable $datasetname external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: iterate over all the datasets , but only the INTERVENTIONS ones :)
for $dataset in $definedoc//odm:ItemGroupDef[@Name=$datasetname][upper-case(@def:Class)='INTERVENTIONS' or upper-case(./def21:Class/@Name)='INTERVENTIONS']
    (: get the dataset :)
    let $domain := $dataset/@Domain
    let $name := $dataset/@Name
    (: the prefix (first 2 characters) is either the domain name or the first characters of the dataset name (case splitted domains) :)
    let $prefix := (
        if($dataset/@Domain) then $dataset/@Domain
        else substring($name,1,2)
    )
	let $datasetname := (
		if($defineversion = '2.1') then $dataset/def21:leaf/@xlink:href
		else $dataset/def:leaf/@xlink:href
	)
    let $datasetdoc := (
		if($datasetname) then doc(concat($base,$datasetname))
		else ()
	)
    (: get the OID and name of the --DOSTOT variable :)
    let $dostotoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'DOSTOT')]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
    )
    let $dostotname := concat($prefix,'DOSTOT')
    (: get the OID and name of the --DOSE variable :)
    let $doseoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'DOSE')]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
    )
    let $dosename := concat($prefix,'DOSE')
    (: get the OID and name of the --DOSTXT variable :)
    let $dostxtoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'DOSTXT')]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
    )
    let $dostxtname := concat($prefix,'DOSTXT')
    (: get the OID and name of the DOSU variable :)
    let $dosuoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'DOSU')]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
    )
    let $dosuname := $definedoc//odm:ItemDef[@OID=$dosuoid]/@Name
    (: iterate over all record in the dataset for which there is a DOSE, DOSTXT or DOSTOT data point :) 
    for $record in $datasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$doseoid] or odm:ItemData[@ItemOID=$dostxtoid] or odm:ItemData[@ItemOID=$dostotoid]]
        let $recnum := $record/@data:ItemGroupDataSeq
        (: DOSEU must be populated :)
        where not($record/odm:ItemData[@ItemOID=$dosuoid])
        return <error rule="SD0035" dataset="{data($name)}" variable="{data($dosuname)}" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">{data($dosuname)} is not populated although one of {data($dosename)}, {data($dostxtname)} or {data($dostotname)} is populated</error>				
		]]>
	</rulexquery>
</sdsrule>

<sdsrule originator="FDA" id="SD1284" standard="SDTM" last-update="2020-08-08">
	<ruledescription>Portion or Totality (--PORTOT) variable may not be present, when the Location (--LOC) variable is missing</ruledescription>
	<ruledetaileddescription>Portion or Totality (--PORTOT) variable should not be present in the domain, when the Location (--LOC) variable is missing</ruledetaileddescription>
	<igversion>3.1.3</igversion>
	<igversion>3.2</igversion>
	<domain>INTERVENTIONS</domain>
	<domain>FINDINGS</domain>
	<synonym context="CDISC">CG0115</synonym>
	<rulexquery><![CDATA[
(: Rule SD1284 - When --LOC not present in dataset then --PORTOT not present in dataset
Applicable to INTERVENTIONS and FINDINGS :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink"; 
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: iterate over all the datasets , but only the INTERVENTIONS and FINDINGS ones :)
for $dataset in $definedoc//odm:ItemGroupDef[upper-case(@def:Class)='INTERVENTIONS' or upper-case(@def:Class)='FINDINGS' or upper-case(./def21:Class/@Name)='INTERVENTIONS' or upper-case(./def21:Class/@Name)='FINDINGS']
    (: get the dataset :)
    let $name := $dataset/@Name
    (: the prefix (first 2 characters) is either the domain name or the first characters of the dataset name (case splitted domains) :)
    let $prefix := (
        if($dataset/@Domain) then $dataset/@Domain
        else substring($name,1,2)
    )
    (: get the OID and name of --LOC and --PORTOT :)
    let $locoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'LOC')]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
    )
    let $locname := concat($prefix,'LOC')
    (: get the OID and name of --PORTOT :)
    let $portotoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'PORTOT')]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
    )
    let $portotname := concat($prefix,'PORTOT')
    (: When --LOC not present in dataset then --PORTOT not present in dataset :)
    where not($locoid) and $portotoid  (: --LOC is not present, but --PORTOT is present :)
        return 
            <error rule="SD1284" dataset="{data($name)}" variable="{data($portotname)}" rulelastupdate="2020-08-08">{data($portotname)} is present although {data($locname)} is present in dataset {data($name)}</error>				
		]]>
	</rulexquery>
</sdsrule>

<sdsrule originator="FDA" id="SD1285" standard="SDTM" last-update="2020-08-08">
	<ruledescription>Directionality (--DIR) variable may not be present, when the Location (--LOC) variable is missing</ruledescription>
	<ruledetaileddescription>Directionality (--DIR) variable should not be present in the domain, when the Location (--LOC) variable is missing</ruledetaileddescription>
	<igversion>3.1.3</igversion>
	<igversion>3.2</igversion>
	<domain>INTERVENTIONS</domain>
	<domain>FINDINGS</domain>
	<synonym context="CDISC">CG0116</synonym>
	<rulexquery><![CDATA[
(: Rule SD1285 - When --LOC not present in dataset then --DIR not present in dataset
Applicable to INTERVENTIONS and FINDINGS :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink"; 
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: iterate over all the datasets , but only the INTERVENTIONS and FINDINGS ones :)
for $dataset in $definedoc//odm:ItemGroupDef[upper-case(@def:Class)='INTERVENTIONS' or upper-case(@def:Class)='FINDINGS' or upper-case(./def21:Class/@Name)='INTERVENTIONS' or upper-case(./def21:Class/@Name)='FINDINGS']
    (: get the dataset :)
    let $name := $dataset/@Name
    (: the prefix (first 2 characters) is either the domain name or the first characters of the dataset name (case splitted domains) :)
    let $prefix := (
        if($dataset/@Domain) then $dataset/@Domain
        else substring($name,1,2)
    )
    (: get the OID and name of --LOC and --PORTOT :)
    let $locoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'LOC')]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
    )
    let $locname := concat($prefix,'LOC')
    (: get the OID and name of --DIR :)
    let $diroid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'DIR')]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
    )
    let $dirname := concat($prefix,'DIR')
    (: When --LOC not present in dataset then --DIR not present in dataset :)
    where not($locoid) and $diroid  (: --LOC is not present, but --DIR is present :)
        return 
            <error rule="SD1285" dataset="{data($name)}" variable="{data($dirname)}" rulelastupdate="2020-08-08">{data($dirname)} is present although {data($locname)} is present in dataset {data($name)}</error>				
		]]>
	</rulexquery>
</sdsrule>

<sdsrule originator="FDA" id="SD2003" standard="SDTM" last-update="2020-08-08">
	<ruledescription>When ACTARM is not in TA.ARM, then ACTARM must be one of 'Screen Failure', 'Not Assigned', 'Not Treated', or 'Unplanned Treatment' - </ruledescription>
	<ruledetaileddescription>'Description of Actual Arm (ACTARM) values should match entries in the Trial Arms (TA) dataset, except for subjects who failed screening ('Screen Failure'), or were not fully assigned to an Arm ('Not Assigned'), or randomized, but not treated ('Not Treated') or received non-planned treatment ('Unplanned Treatment')</ruledetaileddescription>
	<igversion>3.1.3</igversion>
	<igversion>3.2</igversion>
	<domain>DM</domain>
	<synonym context="CDISC">CG0117</synonym>
	<rulexquery><![CDATA[
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
declare namespace functx = "http://www.functx.com";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: function to find out whether a value is in a sequence (array) :)
declare function functx:is-value-in-sequence
  ( $value as xs:anyAtomicType? ,
    $seq as xs:anyAtomicType* )  as xs:boolean {
   $value = $seq
} ;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: get the TA dataset and the ARM (Arm Name( OID in TA :)
let $tadataset := $definedoc//odm:ItemGroupDef[@Name='TA']
let $tadatasetname := (
	if($defineversion = '2.1') then $tadataset/def21:leaf/@xlink:href
	else $tadataset/def:leaf/@xlink:href
)
let $tadatasetlocation := concat($base,$tadatasetname)
let $armfromtaoid := (
    for $a in $definedoc//odm:ItemDef[@Name='ARM']/@OID 
    where $a = $definedoc//odm:ItemGroupDef[@Name='TA']/odm:ItemRef/@ItemOID
    return $a
)
(: make an array/sequence with all TA-ARM values, take it from the dataset itself, not from the define.xml codelist :)
let $armsequence := distinct-values(
    for $itemdata in doc($tadatasetlocation)//odm:ItemGroupData/odm:ItemData[@ItemOID=$armfromtaoid]
    return $itemdata/@Value
)
(: Get the DM dataset :)
for $dataset in $definedoc//odm:ItemGroupDef[@Name='DM']
    let $datasetname := (
		if($defineversion = '2.1') then $dataset/def21:leaf/@xlink:href
		else $dataset/def:leaf/@xlink:href
	)
    let $dmdatasetdoc := doc(concat($base,$datasetname))
    (: get the OID of the ACTARM :)
    let $actarmoid := (
        for $a in $definedoc//odm:ItemDef[@Name='ACTARM']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name='DM']/odm:ItemRef/@ItemOID
        return $a
    )
    (: iterate over all the records in the DM dataset that have ACTARM populated :)
    for $record in $dmdatasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$actarmoid]]
        let $recnum := $record/@data:ItemGroupDataSeq
        (: get the value of ACTARM :)
        let $actarmvalue := $record/odm:ItemData[@ItemOID=$actarmoid]/@Value
        (: and check whether it is one of those from TA/ARM OR 
        'Screen Failure' or 'Not Assigned' or 'Not Treated' or 'Unplanned Treatment'
        :)
        where not(functx:is-value-in-sequence($actarmvalue,$armsequence)) and not($actarmvalue='Screen Failure') and not($actarmvalue='Not Assigned') and not($actarmvalue='Not Treated') and not($actarmvalue='Unplanned Treatment')
        return <error rule="SD2003" dataset="DM" variable="ACTARM" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">Invalid value for ACTARM, value={data($actarmvalue)} in dataset {data($datasetname)}</error>			
		]]>
	</rulexquery>
</sdsrule>

<sdsrule originator="FDA" id="SD0071-A" standard="SDTM" last-update="2020-08-08">
	<ruledescription>Screen failure subjects, if submitted, must be in DM, with ARMCD = 'SCRNFAIL' and ARM = 'Screen Failure'</ruledescription>
	<ruledetaileddescription>The combination of Description of Planned Arm (ARM) and Planned Arm Code (ARMCD) values must match entries in the Trial Arms (TA) dataset, except for subjects who failed screening (ARMCD = 'SCRNFAIL') or were not fully assigned to an Arm (ARMCD = 'NOTASSGN')</ruledetaileddescription>
	<igversion>3.1.2</igversion>
	<igversion>3.1.3</igversion>
	<igversion>3.2</igversion>
	<domain>DM</domain>
	<synonym context="CDISC">CG0128</synonym>
	<rulexquery><![CDATA[
(: Rule SD0071 - When ARMCD not in TA.ARMCD then ARMCD in ('SCRNFAIL', 'NOTASSGN') :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
declare namespace functx = "http://www.functx.com";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external; 
declare variable $define external;
declare variable $defineversion external;
(: function to find out whether a value is in a sequence (array) :)
declare function functx:is-value-in-sequence
  ( $value as xs:anyAtomicType? ,
    $seq as xs:anyAtomicType* )  as xs:boolean {
   $value = $seq
} ;
(: let $base := '/db/fda_submissions/cdiscpilot01/' :)
(: let $define := 'define_2_0.xml' :)
let $definedoc := doc(concat($base,$define))
(: get the TA dataset and the ARMCD OID in TA :)
let $tadataset := $definedoc//odm:ItemGroupDef[@Name='TA']
let $tadatasetname := (
	if($defineversion = '2.1') then $tadataset/def21:leaf/@xlink:href
	else $tadataset/def:leaf/@xlink:href
)
let $tadatasetdoc := (
	if($tadatasetname) then doc(concat($base,$tadatasetname))
	else ()
)
let $armcdfromtaoid := (
    for $a in $definedoc//odm:ItemDef[@Name='ARMCD']/@OID 
    where $a = $definedoc//odm:ItemGroupDef[@Name='TA']/odm:ItemRef/@ItemOID
    return $a
)
(: make an array/sequence with all TA-ARMCD values, take it from the dataset itself, not from the define.xml codelist :)
let $armcdsequence := distinct-values(
    for $itemdata in $tadatasetdoc//odm:ItemGroupData/odm:ItemData[@ItemOID=$armcdfromtaoid]
    return $itemdata/@Value
)

(: Get the DM dataset :)
for $dataset in $definedoc//odm:ItemGroupDef[@Name='DM']
    let $datasetname := (
		if($defineversion = '2.1') then $dataset/def21:leaf/@xlink:href
		else $dataset/def:leaf/@xlink:href
	)
    let $dmdatasetdoc := doc(concat($base,$datasetname))
    (: get the OID of the ARMCD :)
    let $armcdoid := (
        for $a in $definedoc//odm:ItemDef[@Name='ARMCD']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name='DM']/odm:ItemRef/@ItemOID
        return $a
    )
    (: iterate over all the records in the DM dataset that have ARMCD populated :)
    for $record in $dmdatasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$armcdoid]]
        let $recnum := $record/@data:ItemGroupDataSeq
        (: get the value of ARMCD :)
        let $armcdvalue := $record/odm:ItemData[@ItemOID=$armcdoid]/@Value
        (: When ARMCD not in TA.ARMCD then ARMCD in ('SCRNFAIL', 'NOTASSGN') :)
        where not(functx:is-value-in-sequence($armcdvalue,$armcdsequence)) and not($armcdvalue='SCRNFAIL') and not($armcdvalue='NOTASSGN')
        return <error rule="SD0071" dataset="DM" variable="ARMCD" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">Invalid value for ARMCD, value='{data($armcdvalue)}' is not found in TA and is not one of('SCRNFAIL', 'NOTASSGN')</error>												
		]]>
	</rulexquery>
</sdsrule>

<sdsrule originator="FDA" id="SD0071-B" standard="SDTM" last-update="2020-08-08">
	<ruledescription>Subjects withdrawn before arm assignment, if not screen failures, must have ARMCD ='NOTASSGN' and ARM = 'Not Assigned' in DM</ruledescription>
	<ruledetaileddescription>Subjects withdrawn from a trial before assignment to an Arm, if they are not screen failures, should have ARMCD ='NOTASSGN' and ARM = 'Not Assigned'</ruledetaileddescription>
	<igversion>3.1.2</igversion>
	<igversion>3.1.3</igversion>
	<igversion>3.2</igversion>
	<domain>DM</domain>
	<synonym context="CDISC">CG0128</synonym>
	<rulexquery><![CDATA[
(: Rule SD0071 - When ARM is not in TA.ARM then ARM must be one of 'Screen Failure', 'Not Assigned' :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
declare namespace functx = "http://www.functx.com";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external; 
declare variable $define external;
declare variable $defineversion external;
(: function to find out whether a value is in a sequence (array) :)
declare function functx:is-value-in-sequence
  ( $value as xs:anyAtomicType? ,
    $seq as xs:anyAtomicType* )  as xs:boolean {
   $value = $seq
} ;
(: let $base := '/db/fda_submissions/cdiscpilot01/' 
let $define := 'define_2_0.xml' :)
let $definedoc := doc(concat($base,$define))
(: get the TA dataset and the ARM OID in TA :)
let $tadataset := $definedoc//odm:ItemGroupDef[@Name='TA']
let $tadatasetname := (
	if($defineversion = '2.1') then $tadataset/def21:leaf/@xlink:href
	else $tadataset/def:leaf/@xlink:href
)
let $tadatasetdoc := (
	if($tadatasetname) then doc(concat($base,$tadatasetname))
	else ()
)
(: get the OID of ARM in TA :)
let $armfromtaoid := (
    for $a in $definedoc//odm:ItemDef[@Name='ARM']/@OID 
    where $a = $definedoc//odm:ItemGroupDef[@Name='TA']/odm:ItemRef/@ItemOID
    return $a
)
(: make an array/sequence with all TA-ARM values, take it from the dataset itself, not from the define.xml codelist :)
let $armsequence := distinct-values(
    for $itemdata in $tadatasetdoc//odm:ItemGroupData/odm:ItemData[@ItemOID=$armfromtaoid]
    return $itemdata/@Value
)

(: Get the DM dataset :)
for $dataset in $definedoc//odm:ItemGroupDef[@Name='DM']
    let $datasetname := (
		if($defineversion = '2.1') then $dataset/def21:leaf/@xlink:href
		else $dataset/def:leaf/@xlink:href
	)
    let $dmdatasetdoc := doc(concat($base,$datasetname))
    (: get the OID of the ARM :)
    let $armoid := (
        for $a in $definedoc//odm:ItemDef[@Name='ARM']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name='DM']/odm:ItemRef/@ItemOID
        return $a
    )
    (: iterate over all the records in the DM dataset that have ARM populated :)
    for $record in $dmdatasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$armoid]]
        let $recnum := $record/@data:ItemGroupDataSeq
        (: get the value of ARM :)
        let $armvalue := $record/odm:ItemData[@ItemOID=$armoid]/@Value
        (: When ARM not in TA.ARM then ARM in ('Screen Failure', 'Not Assigned') :)
        where not(functx:is-value-in-sequence($armvalue,$armsequence)) and not($armvalue='Screen Failure') and not($armvalue='Not Assigned')
        return <error rule="SD0071" dataset="DM" variable="ARMC" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">Invalid value for ARM, value='{data($armvalue)}' is not found in TA and is not one of('Screen Failure', 'Not Assigned')</error>												
		]]>
	</rulexquery>
</sdsrule>

<sdsrule originator="FDA" id="SD2001" standard="SDTM" last-update="2020-08-08">
	<ruledescription>ACTARMCD may be no more than 20 characters</ruledescription>
	<ruledetaileddescription>The value of Actual Arm Code (ACTARMCD) may be no more than 20 characters in length</ruledetaileddescription>
	<igversion>3.1.3</igversion>
	<igversion>3.2</igversion>
	<domain>DM</domain>
	<synonym context="CDISC">CG0123</synonym>
	<rulexquery><![CDATA[
(: Rule SD2001 - ACTARMCD value length <= 20:
The value of Actual Arm Code (ACTARMCD) should be no more than 20 characters in length
:) 
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdiscpilot01/'  :)
(: let $define := 'define_2_0.xml' :)
let $definedoc := doc(concat($base,$define))
(: Get the DM dataset :)
for $dataset in $definedoc//odm:ItemGroupDef[@Name='DM']
	let $datasetname := (
		if($defineversion = '2.1') then $dataset/def21:leaf/@xlink:href
		else $dataset/def:leaf/@xlink:href
	)
    let $dmdatasetdoc := doc(concat($base,$datasetname))
    (: get the OID of the ACTARMCD :)
    let $actarmcdoid := (
        for $a in $definedoc//odm:ItemDef[@Name='ACTARMCD']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name='DM']/odm:ItemRef/@ItemOID
        return $a
    )
    (: iterate over all the records in the DM dataset that have ACTARMCD populated :)
    for $record in $dmdatasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$actarmcdoid]]
        let $recnum := $record/@data:ItemGroupDataSeq
        (: get the value of ACTARMCD :)
        let $actarmcdvalue := $record/odm:ItemData[@ItemOID=$actarmcdoid]/@Value
        (: and check whether it is not longer than 20 characters :)
        where string-length($actarmcdvalue) > 20
        return <warning rule="SD2001" dataset="DM" variable="ACTARMCD" ulelastupdate="2020-08-08" recordnumber="{data($recnum)}">Invalid value for ACTARMCD in dataset {data($datasetname)}, it's length is more than 20 characters, value='{data($actarmcdvalue)}'</warning>				
		]]>
	</rulexquery>
</sdsrule>

<sdsrule originator="FDA" id="SD2002" standard="SDTM" last-update="2020-08-08">
	<ruledescription>When ACTARMCD not in ('SCRNFAIL', 'NOTASSGN', 'NOTTRT', 'UNPLAN') then ACTARMCD must be in TA.ARMCD</ruledescription>
	<ruledetaileddescription>Actual Arm Code (ACTARMCD) values should match entries in the Trial Arms (TA) dataset, except for subjects who failed screening ('SCRNFAIL'),  or were not fully assigned to an Arm ('NOTASSGN'),  or randomized but not treated ('NOTTRT'), or received non-planned treatment ('UNPLAN')</ruledetaileddescription>
	<igversion>3.1.3</igversion>
	<igversion>3.2</igversion>
	<domain>DM</domain>
	<synonym context="CDISC">CG0125</synonym>
	<rulexquery><![CDATA[
(: Rule SD2002 - When ACTARMCD not in ('SCRNFAIL', 'NOTASSGN', 'NOTTRT', 'UNPLAN') then ACTARMCD in TA.ARMCD :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
declare namespace functx = "http://www.functx.com";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: function to find out whether a value is in a sequence (array) :)
declare function functx:is-value-in-sequence
  ( $value as xs:anyAtomicType? ,
    $seq as xs:anyAtomicType* )  as xs:boolean {
   $value = $seq
} ;
(: let $base := '/db/fda_submissions/cdiscpilot01/'  :)
(: let $define := 'define_2_0.xml' :)
let $definedoc := doc(concat($base,$define))
(: get the TA dataset and the ARMCD OID in TA :)
let $tadataset := $definedoc//odm:ItemGroupDef[@Name='TA']
let $tadatasetname := (
	if($defineversion = '2.1') then $tadataset/def21:leaf/@xlink:href
	else $tadataset/def:leaf/@xlink:href
)
let $tadatasetdoc := (
	if($tadatasetname) then doc(concat($base,$tadatasetname))
	else ()
)
let $armcdfromtaoid := (
    for $a in $definedoc//odm:ItemDef[@Name='ARMCD']/@OID 
    where $a = $definedoc//odm:ItemGroupDef[@Name='TA']/odm:ItemRef/@ItemOID
    return $a
)
(: make an array/sequence with all TA-ARMCD values, take it from the dataset itself, not from the define.xml codelist :)
let $armcdsequence := (
    for $itemdata in $tadatasetdoc//odm:ItemGroupData/odm:ItemData[@ItemOID=$armcdfromtaoid]
    return $itemdata/@Value
)
(: Get the DM dataset :)
for $dataset in $definedoc//odm:ItemGroupDef[@Name='DM']
    let $datasetname := (
		if($defineversion = '2.1') then $dataset/def21:leaf/@xlink:href
		else $dataset/def:leaf/@xlink:href
	)
    let $dmdatasetdoc := doc(concat($base,$datasetname))
    (: get the OID of the ACTARMCD :)
    let $actarmcdoid := (
        for $a in $definedoc//odm:ItemDef[@Name='ACTARMCD']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name='DM']/odm:ItemRef/@ItemOID
        return $a
    )
    (: iterate over all the records in the DM dataset that have ACTARMCD populated :)
    for $record in $dmdatasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$actarmcdoid]]
        let $recnum := $record/@data:ItemGroupDataSeq
        (: get the value of ACTARMCD :)
        let $actarmcdvalue := $record/odm:ItemData[@ItemOID=$actarmcdoid]/@Value
        (: When ACTARMCD not in ('SCRNFAIL', 'NOTASSGN', 'NOTTRT', 'UNPLAN') then ACTARMCD in TA.ARMCD :)
        where not($actarmcdvalue='SCRNFAIL') and not($actarmcdvalue='NOTASSGN') and not($actarmcdvalue='NOTTRT') and not($actarmcdvalue='UNPLAN') and not(functx:is-value-in-sequence($actarmcdvalue,$armcdsequence))
        return <error rule="SD2002" dataset="DM" variable="ACTARMCD" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">Invalid value for ACTARMCD, value={data($actarmcdvalue)} in dataset DM is not one of ('SCRNFAIL', 'NOTASSGN', 'NOTTRT', 'UNPLAN') and is not found in TA.ARMCD - {data($armcdsequence)}</error>			
		]]>
	</rulexquery>
</sdsrule>

	
<sdsrule originator="FDA" id="SD0066" standard="SDTM" last-update="2020-08-08">
	<ruledescription>When ARMCD not in ('SCRNFAIL', 'NOTASSGN') then ARMCD must be in TA.ARMCD</ruledescription>
	<ruledetaileddescription>Planned Arm Code (ARMCD) values should match entries in the Trial Arms (TA) dataset, except for subjects who failed screening (ARMCD = 'SCRNFAIL') or were not fully assigned to an Arm (ARMCD = 'NOTASSGN')</ruledetaileddescription>
	<igversion>3.1.2</igversion>
	<igversion>3.1.3</igversion>
	<igversion>3.2</igversion>
	<domain>DM</domain>
	<synonym context="CDISC">CG0126</synonym>
	<rulexquery><![CDATA[
(: Rule SD0066 - When ARMCD not in ('SCRNFAIL', 'NOTASSGN') then ARMCD in TA.ARMCD :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
declare namespace functx = "http://www.functx.com";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external; 
declare variable $define external; 
declare variable $defineversion external;
(: function to find out whether a value is in a sequence (array) :)
declare function functx:is-value-in-sequence
  ( $value as xs:anyAtomicType? ,
    $seq as xs:anyAtomicType* )  as xs:boolean {
   $value = $seq
} ;
(: let $base := '/db/fda_submissions/cdiscpilot01/'  :)
(: let $define := 'define_2_0.xml' :)
let $definedoc := doc(concat($base,$define))
(: get the TA dataset and the ARMCD OID in TA :)
let $tadataset := $definedoc//odm:ItemGroupDef[@Name='TA']
let $tadatasetname := (
	if($defineversion = '2.1') then $tadataset/def21:leaf/@xlink:href
	else $tadataset/def:leaf/@xlink:href
)
let $tadatasetdoc := (
	if($tadatasetname) then doc(concat($base,$tadatasetname))
	else ()
)
let $armcdfromtaoid := (
    for $a in $definedoc//odm:ItemDef[@Name='ARMCD']/@OID 
    where $a = $definedoc//odm:ItemGroupDef[@Name='TA']/odm:ItemRef/@ItemOID
    return $a
)
(: make an array/sequence with all TA-ARMCD values, take it from the dataset itself, not from the define.xml codelist :)
let $armcdsequence := distinct-values(
    for $itemdata in $tadatasetdoc//odm:ItemGroupData/odm:ItemData[@ItemOID=$armcdfromtaoid]
    return $itemdata/@Value
)
(: Get the DM dataset :)
for $dataset in $definedoc//odm:ItemGroupDef[@Name='DM']
    let $datasetname := (
		if($defineversion = '2.1') then $dataset/def21:leaf/@xlink:href
		else $dataset/def:leaf/@xlink:href
	)
    let $dmdatasetdoc := doc(concat($base,$datasetname))
    (: get the OID of the ARMCD :)
    let $armcdoid := (
        for $a in $definedoc//odm:ItemDef[@Name='ARMCD']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name='DM']/odm:ItemRef/@ItemOID
        return $a
    )
    (: iterate over all the records in the DM dataset that have ACTARMCD populated :)
    for $record in $dmdatasetdoc //odm:ItemGroupData[odm:ItemData[@ItemOID=$armcdoid]]
        let $recnum := $record/@data:ItemGroupDataSeq
        (: get the value of ARMCD :)
        let $armcdvalue := $record/odm:ItemData[@ItemOID=$armcdoid]/@Value
        (: When ARMCD not in ('SCRNFAIL', 'NOTASSGN') then ARMCD in TA.ARMCD :)
        where not($armcdvalue='SCRNFAIL') and not($armcdvalue='NOTASSGN') and not(functx:is-value-in-sequence($armcdvalue,$armcdsequence))
        return <error rule="SD0066" dataset="DM" variable="ARMCD" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">Invalid value for ARMCD, value={data($armcdvalue)} in dataset DM is not one of ('SCRNFAIL', 'NOTASSGN') and is not found in TA.ARMCD</error>			
		]]>
	</rulexquery>
</sdsrule>

<sdsrule originator="FDA" id="SD0011" standard="SDTM" last-update="2020-08-08">
	<ruledescription>ARM must equal 'Screen Failure', when ARMCD is 'SCRNFAIL', and vice versa</ruledescription>
	<ruledetaileddescription>Description of Arm (ARM) must equal 'Screen Failure',  when Arm Code (ARMCD) is 'SCRNFAIL', and vice versa</ruledetaileddescription>
	<igversion>3.1.2</igversion>
	<igversion>3.1.3</igversion>
	<igversion>3.2</igversion>
	<domain>DM</domain>
	<domain>TA</domain>
	<synonym context="CDISC">CG0127</synonym>
	<rulexquery><![CDATA[
(: Rule SD0011 - When ACTARMCD in ('SCRNFAIL', 'NOTASSGN')  then ACTARMCD = ARMCD :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
declare namespace functx = "http://www.functx.com";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdiscpilot01/' :)
(: let $define := 'define_2_0.xml' :)
let $definedoc := doc(concat($base,$define))
(: Get the DM dataset :)
for $dataset in $definedoc//odm:ItemGroupDef[@Name='DM']
	let $dmdatasetname := (
		if($defineversion = '2.1') then $dataset/def21:leaf/@xlink:href
		else $dataset/def:leaf/@xlink:href
	)
    let $dmdatasetdoc := doc(concat($base,$dmdatasetname))
    (: get the OID of the ARMCD and ACTARMCD :)
    let $armcdoid := (
        for $a in $definedoc//odm:ItemDef[@Name='ARMCD']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name='DM']/odm:ItemRef/@ItemOID
        return $a
    )
    let $actarmcdoid := (
       for $a in $definedoc//odm:ItemDef[@Name='ACTARMCD']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name='DM']/odm:ItemRef/@ItemOID
        return $a 
    )
    (: iterate over all the records in the DM dataset that have ARMCD populated :)
    for $record in $dmdatasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$armcdoid]]
        let $recnum := $record/@data:ItemGroupDataSeq
        (: get the value of ARMCD and ACTARMCD :)
        let $armcdvalue := $record/odm:ItemData[@ItemOID=$armcdoid]/@Value
        let $actarmcdvalue := $record/odm:ItemData[@ItemOID=$actarmcdoid]/@Value
        (: When ACTARMCD in ('SCRNFAIL', 'NOTASSGN')  then ACTARMCD = ARMCD :)
        where ($actarmcdvalue='SCRNFAIL' or $actarmcdvalue='NOTASSGN') and not($armcdvalue=$actarmcdvalue)
        return <error rule="SD0011" dataset="DM" variable="ACTARMCD" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">Invalid value for ACTARMCD, value='{data($actarmcdvalue)}' is expected to be equal to the value of ARMCD='{data($armcdvalue)}'</error>			
		]]>
	</rulexquery>
</sdsrule>

<sdsrule originator="FDA" id="SD2236" standard="SDTM" last-update="2020-08-08">
	<ruledescription>ACTARMCD is expected to be equal to a ARMCD</ruledescription>
	<ruledetaileddescription>A value for an Actual Arm Code (ACTARMCD) variable is expected to be equal to a value of an Arm Code (ARMCD) variable</ruledetaileddescription>
	<igversion>3.1.3</igversion>
	<igversion>3.2</igversion>
	<domain>DM</domain>
	<rulexquery><![CDATA[
(: Rule SD2236 - A value for an Actual Arm Code (ACTARMCD) variable is expected to be equal to a value of an Arm Code (ARMCD) variable :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
declare namespace functx = "http://www.functx.com";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdiscpilot01/' 
let $define := 'define_2_0.xml' :)
let $definedoc := doc(concat($base,$define))
(: Get the DM dataset :)
for $dataset in $definedoc//odm:ItemGroupDef[@Name='DM']
	let $datasetname := (
		if($defineversion = '2.1') then $dataset/def21:leaf/@xlink:href
		else $dataset/def:leaf/@xlink:href
	)
    let $dmdatasetdoc := doc(concat($base,$datasetname))
    (: get the OID of the ACTARMCD and of ARMCD :)
    let $actarmcdoid := (
        for $a in $definedoc//odm:ItemDef[@Name='ACTARMCD']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name='DM']/odm:ItemRef/@ItemOID
        return $a
    )
    let $armcdoid := (
        for $a in $definedoc//odm:ItemDef[@Name='ARMCD']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name='DM']/odm:ItemRef/@ItemOID
        return $a
    )
    (: iterate over all the records in the DM dataset that have ACTARMCD populated :)
    for $record in $dmdatasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$actarmcdoid]]
        let $recnum := $record/@data:ItemGroupDataSeq
        (: get the value of ACTARMCD and of ARMCD :)
        let $actarmcdvalue := $record/odm:ItemData[@ItemOID=$actarmcdoid]/@Value
        let $armcdvalue := $record/odm:ItemData[@ItemOID=$armcdoid]/@Value
        (: ACTARMCD value must be equal to ARMCD value :)
        where not($actarmcdvalue=$armcdvalue)
        return <warning rule="SD2236" dataset="DM" variable="ACTARMCD" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">Invalid value for ARMCD, value='{data($armcdvalue)}', expected value='{data($actarmcdvalue)}' </warning>					
	]]>
	</rulexquery>
</sdsrule>



<sdsrule id="SD0053" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>ARM must be 'Not Assigned', when ARMCD is 'NOTASSGN', and vice versa</ruledescription>
<ruledetaileddescription>Description of Arm (ARM) must equal 'Not Assigned', when Arm Code (ARMCD) is 'NOTASSGN', and vice versa</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>DM</domain>
<domain>TA</domain>
<rulexquery><![CDATA[	
(: Rule SD0053 - ARM is not 'Not Assigned', when ARMCD equals 'NOTASSGN', or vice versa
Description of Arm (ARM) must equal 'Not Assigned', when Arm Code (ARMCD) is 'NOTASSGN', and vice versa
:)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: get the DM and TA dataset :)
for $dataset in $definedoc//odm:ItemGroupDef[@Name='DM' or @Name='TA']
    let $name := $dataset/@Name
	let $datasetname := (
		if($defineversion = '2.1') then $dataset/def21:leaf/@xlink:href
		else $dataset/def:leaf/@xlink:href
	)
    let $dmdatasetdoc := doc(concat($base,$datasetname))
    (: and get the OIDs of ARMCD and ARM :)
    let $armcdoid := (
    for $a in $definedoc//odm:ItemDef[@Name='ARMCD']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
)
let $armoid := (
    for $a in $definedoc//odm:ItemDef[@Name='ARM']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
)
(: iterate over all the records in the dataset :)
for $record in $dmdatasetdoc//odm:ItemGroupData
    let $recnum := $record/@data:ItemGroupDataSeq
    (: and get the values for ARMCD and ARM :)
    let $armcdvalue := $record/odm:ItemData[@ItemOID=$armcdoid]/@Value
    let $armvalue := $record/odm:ItemData[@ItemOID=$armoid]/@Value
    (: in case that ARMCD=NOTASSGN then ARM must be 'Not Assigned' and vice versa - CASE SENSITIVE :)
    where ($armcdvalue='NOTASSGN' and not($armvalue='Not Assigned')) or ($armvalue='Not Assigned' and not($armcdvalue='NOTASSGN'))
    (: and give out an error message :)
    return <error rule="SD0053" dataset="{data($name)}" variable="ARM" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">ARM is not 'Not Assigned', when ARMCD equals 'NOTASSGN', or vice versa. ARMCD={data($armcdvalue)} with ARM={data($armvalue)} was found</error>
]]></rulexquery>
</sdsrule>


<sdsrule originator="FDA" id="SD1252" standard="SDTM" last-update="2020-08-08">
	<ruledescription>DTHFL must be 'Y' when SS.SSSTRESC = 'DEAD'</ruledescription>
	<ruledetaileddescription>DTHFL must equal 'Y' when there is a record in the Subject Status (SS) domain with SSSTRESC = 'DEAD'</ruledetaileddescription>
	<igversion>3.2</igversion>
	<domain>SS</domain>
	<domain>DM</domain>
	<synonym context="FDA">CG0132</synonym>
	<rulexquery><![CDATA[
(: Rule SD1252 - When SS.SSSTRESC = 'DEAD' then DTHFL = 'Y' :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external; 
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: Get the DM dataset :)
let $dmitemgroupdef := $definedoc//odm:ItemGroupDef[@Name='DM']
(: and the location of the DM dataset :)
let $dmdatasetlocation := (
	if($defineversion = '2.1') then $dmitemgroupdef/def21:leaf/@xlink:href
	else $dmitemgroupdef/def:leaf/@xlink:href
)
let $dmdatasetdoc := doc(concat($base,$dmdatasetlocation))
(: Get the SS (Subject Status) dataset and its location :)
let $ssitemgroupdef := $definedoc//odm:ItemGroupDef[@Name='SS']
let $ssdatasetlocation := (
	if($defineversion = '2.1') then $ssitemgroupdef/def21:leaf/@xlink:href
	else $ssitemgroupdef/def:leaf/@xlink:href
)
let $ssdatasetdoc := (
	if($ssdatasetlocation) then doc(concat($base,$ssdatasetlocation))
	else ()
)
(: we need the OID of SSSTRESC and USUBJID in SS :)
let $ssstrescoid := (
    for $a in $definedoc//odm:ItemDef[@Name='SSSTRESC']/@OID 
    where $a = $ssitemgroupdef/odm:ItemRef/@ItemOID
    return $a
)
let $ssusubjidoid := (
    for $a in $definedoc//odm:ItemDef[@Name='USUBJID']/@OID 
    where $a = $ssitemgroupdef/odm:ItemRef/@ItemOID
    return $a
)
(: and we need the OID of USUBJID and of DTHFL in DM :)
let $dmusubjidoid := (
    for $a in $definedoc//odm:ItemDef[@Name='USUBJID']/@OID 
    where $a = $dmitemgroupdef/odm:ItemRef/@ItemOID
    return $a
) 
let $dthfloid := (
    for $a in $definedoc//odm:ItemDef[@Name='DTHFL']/@OID 
    where $a = $dmitemgroupdef/odm:ItemRef/@ItemOID
    return $a
) 
(: iterate over all records in SS for which SSSTRESC='DEAD' :)
for $record in $ssdatasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$ssstrescoid and @Value='DEAD']]
    let $recnumss := $record/@data:ItemGroupDataSeq
    (: get the value of USUBJID for this record :)
    let $ssusubjid := $record/odm:ItemData[@ItemOID=$ssusubjidoid]/@Value
    (: get the value of DTHFL for this subject in DM :)
    let $dthfl := $dmdatasetdoc//odm:ItemGroupData[@ItemOID=$dmusubjidoid and @Value=$ssusubjid]/odm:ItemData[@ItemOID=$dthfloid]/@Value
    (: the value of DTHFL must be 'Y' :)
    where not($dthfl='Y')
    return <error rule="SD1252" variable="DTHFL" dataset="DM" rulelastupdate="2020-08-08">A record (record number={data($recnumss)}) for USUBJID='{data($ssusubjid)}' has been found for which SSSTRESC='DEAD' but the value of DTHFL='{data($dthfl)}' in DM is not 'Y'</error>			
		]]>
	</rulexquery>
</sdsrule>
	
<sdsrule originator="FDA" id="SD1253" standard="SDTM" last-update="2020-08-08">
	<ruledescription>DTHFL must be 'Y' when there is a SS.STRESC='DEAD' for the subject</ruledescription>
	<ruledetaileddescription>DTHFL must equal 'Y' when there is a record in the Subject Status (SS) domain with SSSTRESC = 'DEAD'</ruledetaileddescription>
	<igversion>3.2</igversion>
	<domain>DD</domain>
	<domain>DM</domain>
	<synonym context="CDISC">CG0133</synonym>
	<rulexquery><![CDATA[
(: Rule SD1253 - When DD (Death Details) record present for subject then DTHFL = 'Y' :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external; 
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: Get the DM dataset :)
let $dmitemgroupdef := $definedoc//odm:ItemGroupDef[@Name='DM']
(: and the location of the DM dataset :)
let $dmdatasetlocation := (
	if($defineversion = '2.1') then $dmitemgroupdef/def21:leaf/@xlink:href
	else $dmitemgroupdef/def:leaf/@xlink:href
)
let $dmdatasetdoc := doc(concat($base,$dmdatasetlocation))
(: Get the DD (Death Details) dataset and its location :)
let $dditemgroupdef := $definedoc//odm:ItemGroupDef[@Name='DD']
let $dddatasetlocation := (
	if($defineversion = '2.1') then $dditemgroupdef/def21:leaf/@xlink:href
	else $dditemgroupdef/def:leaf/@xlink:href
)
let $dddatasetdoc := (
	if($dddatasetlocation) then doc(concat($base,$dddatasetlocation))
	else ()
)
(: get the OID of USUBJID in DM :)
let $dmusubjidoid := (
    for $a in $definedoc//odm:ItemDef[@Name='USUBJID']/@OID 
    where $a = $dmitemgroupdef/odm:ItemRef/@ItemOID
    return $a
) 
(: get the OID of DTHFL in DM :)
let $dthfloid := (
    for $a in $definedoc//odm:ItemDef[@Name='DTHFL']/@OID 
    where $a = $dmitemgroupdef/odm:ItemRef/@ItemOID
    return $a
) 
(: get the OID of USUBJID in DD :)
let $ddusubjidoid := (
    for $a in $definedoc//odm:ItemDef[@Name='USUBJID']/@OID 
    where $a = $dditemgroupdef/odm:ItemRef/@ItemOID
    return $a
)
(: get the distinct values of USUBJID in DD :)
let $deadsubjects := distinct-values($dddatasetdoc//odm:ItemGroupData/odm:ItemData[@ItemOID=$ddusubjidoid]/@Value)
for $subject in $deadsubjects
    (: get the value of DTHFL in DM for this subject :)
    let $dthfl := $dmdatasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$dmusubjidoid and @Value=$subject]]/odm:ItemData[@ItemOID=$dthfloid]/@Value
    (: DTHFL must be 'Y' :)
    where not($dthfl='Y')
    return <error rule="SD1253" variable="DTHFL" dataset="DM" rulelastupdate="2020-08-08">A record for USUBJID='{data($subject)}' has been found in the DD dataset but the value of DTHFL='{data($dthfl)}' in DM is not 'Y'</error>						
		]]>
	</rulexquery>
</sdsrule>

<sdsrule originator="FDA" id="SD1254" standard="SDTM" last-update="2020-08-08">
	<ruledescription>DTHFL must be 'Y' when there is a record in AE with AEOUT = 'FATAL'</ruledescription>
	<ruledetaileddescription>DTHFL must equal 'Y' when there is a record in the Adverse Events (AE) domain with AEOUT = 'FATAL'</ruledetaileddescription>
	<igversion>3.1.3</igversion>
	<igversion>3.2</igversion>
	<domain>AE</domain>
	<domain>DM</domain>
	<synonym context="CDISC">CG0134</synonym>
	<rulexquery><![CDATA[	
(: Rule CG0134 - When AE.AEOUT = 'FATAL' then DTHFL = 'Y' :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external; 
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: Get the DM dataset :)
let $dmitemgroupdef := $definedoc//odm:ItemGroupDef[@Name='DM']
(: and the location of the DM dataset :)
let $dmdatasetlocation := (
	if($defineversion = '2.1') then $dmitemgroupdef/def21:leaf/@xlink:href
	else $dmitemgroupdef/def:leaf/@xlink:href
)
let $dmdatasetdoc := doc(concat($base,$dmdatasetlocation))
(: Get the AE (Adverse Events) dataset and its location :)
let $aeitemgroupdef := $definedoc//odm:ItemGroupDef[@Name='AE']
let $aedatasetlocation := (
	if($defineversion = '2.1') then $aeitemgroupdef/def21:leaf/@xlink:href
	else $aeitemgroupdef/def:leaf/@xlink:href
)
let $aedatasetdoc := (
	if($aedatasetlocation) then doc(concat($base,$aedatasetlocation))
	else ()
)
(: get the OID of USUBJID in DM :)
let $dmusubjidoid := (
    for $a in $definedoc//odm:ItemDef[@Name='USUBJID']/@OID 
    where $a = $dmitemgroupdef/odm:ItemRef/@ItemOID
    return $a
) 
(: get the OID of DTHFL in DM :)
let $dthfloid := (
    for $a in $definedoc//odm:ItemDef[@Name='DTHFL']/@OID 
    where $a = $dmitemgroupdef/odm:ItemRef/@ItemOID
    return $a
) 
(: get the OID of USUBJID in AE :)
let $aeusubjidoid := (
    for $a in $definedoc//odm:ItemDef[@Name='USUBJID']/@OID 
    where $a = $aeitemgroupdef/odm:ItemRef/@ItemOID
    return $a
)
(: get the OID of AEOUT in AE :)
let $aeoutoid := (
    for $a in $definedoc//odm:ItemDef[@Name='AEOUT']/@OID 
    where $a = $aeitemgroupdef/odm:ItemRef/@ItemOID
    return $a
)
(: iterate over all records in AE for which AEOUT='FATAL' :)
    for $record in $aedatasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$aeoutoid and @Value='FATAL']]
        let $recnum := $record/@data:ItemGroupDataSeq
        (: get the value of USUBJID for this record in AE :)
        let $usubjid := $record/odm:ItemData[@ItemOID=$aeusubjidoid]/@Value
        (: and get the value of DTHFL in DM :)
        let $dthfl := $dmdatasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$dmusubjidoid and @Value=$usubjid]]/odm:ItemData[@ItemOID=$dthfloid]/@Value
        (: DTHFL must be 'Y' :)
        where not($dthfl='Y')
        return <error rule="SD1254" variable="DTHFL" dataset="DM" rulelastupdate="2020-08-08">A record for USUBJID='{data($usubjid)}' (record number {data($recnum)}) has been found in the AE dataset with AEOUT='FATAL' but the value of DTHFL='{data($dthfl)}' in DM is not 'Y'</error>		
		]]>
	</rulexquery>
</sdsrule>
	
<sdsrule originator="FDA" id="SD1255" standard="SDTM" last-update="2020-08-08">
	<ruledescription>DTHFL must be 'Y' when there is a record AE with AESDTH = 'Y'</ruledescription>
	<ruledetaileddescription>DTHFL must equal 'Y' when there is a record in the Adverse Events (AE) domain with AESDTH = 'Y'</ruledetaileddescription>
	<igversion>3.1.3</igversion>
	<igversion>3.2</igversion>
	<domain>AE</domain>
	<domain>DM</domain>
	<synonym context="CDISC">CG0135</synonym>
	<rulexquery><![CDATA[	
(: Rule SD1255 - When AE.AESDTH = 'Y' then DTHFL = 'Y' :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external; 
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: Get the DM dataset :)
let $dmitemgroupdef := $definedoc//odm:ItemGroupDef[@Name='DM']
(: and the location of the DM dataset :)
let $dmdatasetlocation := (
	if($defineversion = '2.1') then $dmitemgroupdef/def21:leaf/@xlink:href
	else $dmitemgroupdef/def:leaf/@xlink:href
)
let $dmdatasetdoc := doc(concat($base,$dmdatasetlocation))
(: Get the AE (Adverse Events) dataset and its location :)
let $aeitemgroupdef := $definedoc//odm:ItemGroupDef[@Name='AE']
let $aedatasetlocation := (
	if($defineversion = '2.1') then $aeitemgroupdef/def21:leaf/@xlink:href
	else $aeitemgroupdef/def:leaf/@xlink:href
)
let $aedatasetdoc := (
	if($aedatasetlocation) then doc(concat($base,$aedatasetlocation))
	else ()
)
(: get the OID of USUBJID in DM :)
let $dmusubjidoid := (
    for $a in $definedoc//odm:ItemDef[@Name='USUBJID']/@OID 
    where $a = $dmitemgroupdef/odm:ItemRef/@ItemOID
    return $a
) 
(: get the OID of DTHFL in DM :)
let $dthfloid := (
    for $a in $definedoc//odm:ItemDef[@Name='DTHFL']/@OID 
    where $a = $dmitemgroupdef/odm:ItemRef/@ItemOID
    return $a
) 
(: get the OID of USUBJID in AE :)
let $aeusubjidoid := (
    for $a in $definedoc//odm:ItemDef[@Name='USUBJID']/@OID 
    where $a = $aeitemgroupdef/odm:ItemRef/@ItemOID
    return $a
)
(: get the OID of AESDTH in AE :)
let $aesdthoid := (
    for $a in $definedoc//odm:ItemDef[@Name='AESDTH']/@OID 
    where $a = $aeitemgroupdef/odm:ItemRef/@ItemOID
    return $a
)
(: iterate over all records in AE for which AESDTH='Y' :)
    for $record in $aedatasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$aesdthoid and @Value='Y']]
        let $recnum := $record/@data:ItemGroupDataSeq
        (: get the value of USUBJID for this record in AE :)
        let $usubjid := $record/odm:ItemData[@ItemOID=$aeusubjidoid]/@Value
        (: and get the value of DTHFL in DM :)
        let $dthfl := $dmdatasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$dmusubjidoid and @Value=$usubjid]]/odm:ItemData[@ItemOID=$dthfloid]/@Value
        (: DTHFL must be 'Y' :)
        where not($dthfl='Y')
        return <error rule="SD1255" variable="DTHFL" dataset="DM" rulelastupdate="2020-08-08">A record for USUBJID='{data($usubjid)}' (record number {data($recnum)}) has been found in the AE dataset with AESDTH='Y' but the value of DTHFL='{data($dthfl)}' in DM is not 'Y'</error>				
		]]>
	</rulexquery>
</sdsrule>

<sdsrule originator="FDA" id="SD1256" standard="SDTM" last-update="2020-08-08">
	<ruledescription>DTHFL must be 'Y' when there is a record in DS with DSDECOD = 'DEATH'</ruledescription>
	<ruledetaileddescription>DTHFL must equal 'Y' when there is a record in the Disposition (DS) domain with DSDECOD = 'DEATH'</ruledetaileddescription>
	<igversion>3.1.3</igversion>
	<igversion>3.2</igversion>
	<domain>DS</domain>
	<domain>DM</domain>
	<synonym context="CDISC">CG0136</synonym>
	<rulexquery><![CDATA[	
(: Rule SD1256 - When DS.DSDECOD = 'DEATH' then DTHFL = 'Y' :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external; 
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: Get the DM dataset :)
let $dmitemgroupdef := $definedoc//odm:ItemGroupDef[@Name='DM']
(: and the location of the DM dataset :)
let $dmdatasetlocation := (
	if($defineversion = '2.1') then $dmitemgroupdef/def21:leaf/@xlink:href
	else $dmitemgroupdef/def:leaf/@xlink:href
)
let $dmdatasetdoc := doc(concat($base,$dmdatasetlocation))
(: Get the DS (Disposition) dataset and its location :)
let $dsitemgroupdef := $definedoc//odm:ItemGroupDef[@Name='DS']
let $dsdatasetlocation := (
	if($defineversion = '2.1') then $dsitemgroupdef/def21:leaf/@xlink:href
	else $dsitemgroupdef/def:leaf/@xlink:href
)
let $dsdatasetdoc := (
	if($dsdatasetlocation) then doc(concat($base,$dsdatasetlocation))
	else ()
)
(: get the OID of USUBJID in DM :)
let $dmusubjidoid := (
    for $a in $definedoc//odm:ItemDef[@Name='USUBJID']/@OID 
    where $a = $dmitemgroupdef/odm:ItemRef/@ItemOID
    return $a
) 
(: get the OID of DTHFL in DM :)
let $dthfloid := (
    for $a in $definedoc//odm:ItemDef[@Name='DTHFL']/@OID 
    where $a = $dmitemgroupdef/odm:ItemRef/@ItemOID
    return $a
) 
(: get the OID of USUBJID in DS :)
let $dsusubjidoid := (
    for $a in $definedoc//odm:ItemDef[@Name='USUBJID']/@OID 
    where $a = $dsitemgroupdef/odm:ItemRef/@ItemOID
    return $a
)
(: get the OID of DSDECOD in DS :)
let $dsdecodoid := (
    for $a in $definedoc//odm:ItemDef[@Name='DSDECOD']/@OID 
    where $a = $dsitemgroupdef/odm:ItemRef/@ItemOID
    return $a
)
(: iterate over all records in DS for which DSDECOD='DEATH' :)
for $record in $dsdatasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$dsdecodoid and @Value='DEATH']]
    let $recnum := $record/@data:ItemGroupDataSeq
    (: get the value of USUBJID for this record in DS :)
    let $usubjid := $record/odm:ItemData[@ItemOID=$dsusubjidoid]/@Value
    (: and get the value of DTHFL in DM :)
    let $dthfl := $dmdatasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$dmusubjidoid and @Value=$usubjid]]/odm:ItemData[@ItemOID=$dthfloid]/@Value
    (: DTHFL must be 'Y' :)
    where not($dthfl='Y')
    return <error rule="SD1256" variable="DTHFL" dataset="DM" rulelastupdate="2020-08-08">A record for USUBJID='{data($usubjid)}' (record number {data($recnum)}) has been found in the DS dataset with DSDECOD='DEATH' but the value of DTHFL='{data($dthfl)}' in DM is not 'Y'</error>						
		]]>
	</rulexquery>
</sdsrule>
	
<sdsrule originator="FDA" id="SD0088" standard="SDTM" last-update="2020-08-08">
	<ruledescription>RFENDTC must be populated, unless ARMCD = 'SCRNFAIL' or ARMCD = 'NOTASSGN'</ruledescription>
	<ruledetaileddescription>Subject Reference End Date/Time (RFENDTC) must be populated for all randomized subjects, that have Planned Arm Code (ARMCD) not equal to 'SCRNFAIL' or 'NOTASSGN'</ruledetaileddescription>
	<igversion>3.1.2</igversion>
	<igversion>3.1.3</igversion>
	<igversion>3.2</igversion>
	<domain>DM</domain>
	<synonym context="CDISC">CG0142</synonym>
	<rulexquery><![CDATA[	
(: Rule SD0088 - When ACTARM not in ('Screen Failure', 'Not Assigned') then RFENDTC != null :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external; 
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdiscpilot01/' :)
(: let $define := 'define_2_0.xml' :)
let $definedoc := doc(concat($base,$define))
(: Get the DM dataset :)
let $dmitemgroupdef := $definedoc//odm:ItemGroupDef[@Name='DM']
(: and the location of the DM dataset :)
let $dmdatasetlocation := (
	if($defineversion = '2.1') then $dmitemgroupdef/def21:leaf/@xlink:href
	else $dmitemgroupdef/def:leaf/@xlink:href
)
let $dmdatasetdoc := doc(concat($base,$dmdatasetlocation))
(: get the OID of ACTARM and of RFENDTC :)
let $actarmoid := (
    for $a in $definedoc//odm:ItemDef[@Name='ACTARM']/@OID 
    where $a = $dmitemgroupdef/odm:ItemRef/@ItemOID
    return $a 
)
let $rfendtcoid := (
    for $a in $definedoc//odm:ItemDef[@Name='RFENDTC']/@OID 
    where $a = $dmitemgroupdef/odm:ItemRef/@ItemOID
    return $a 
)
(: iterate over the records in DM :)
for $record in $dmdatasetdoc//odm:ItemGroupData
    let $recnum := $record/@data:ItemGroupDataSeq
    (: get the values of ACTARM and of RFENDTC :)
    let $actarmvalue := $record/odm:ItemData[@ItemOID=$actarmoid]/@Value
    let $rfendtcvalue := $record/odm:ItemData[@ItemOID=$rfendtcoid]/@Value
    (: When ACTARM not in ('Screen Failure', 'Not Assigned') then RFENDTC != null :)
    where not($actarmvalue='Screen Failure' or $actarmvalue='Not Assigned' or $actarmvalue='Not  Treated') and not($rfendtcvalue)
    return <error rule="SD0088" variable="RFENDTC" dataset="DM" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">RFENDTC=null for ACTARM='{data($actarmvalue)}'. RFENDTC is required to be populated for all randomized subjects.</error>			
		]]>
	</rulexquery>
</sdsrule>

<sdsrule originator="FDA" id="SD1240" standard="SDTM" last-update="2020-08-08">
	<ruledescription>All subjects must have an Informed Consent record in the Disposition domain</ruledescription>
	<ruledetaileddescription>All subjects must have an Informed Consent record is DS</ruledetaileddescription>
	<igversion>3.1.2</igversion>
	<igversion>3.1.3</igversion>
	<igversion>3.2</igversion>
	<domain>DS</domain>
	<domain>DM</domain>
	<synonym context="CDISC">CG0143</synonym>
	<rulexquery><![CDATA[
(: Rule SD1240 - All subjects must have an Informed Consent record in the Disposition domain :)
(: This rule is a bit problematic as "when DS.TERM indicates informed consent obtained" is not clearly defined. 
The SDTM-IG states "EXAMPLES of protocol milestones: INFORMED CONSENT OBTAINED ..." :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
declare namespace functx = "http://www.functx.com";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external; 
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdiscpilot01/' 
let $define := 'define_2_0.xml'  :)
let $definedoc := doc(concat($base,$define))
(: Get the DM dataset :)
let $dmitemgroupdef := $definedoc//odm:ItemGroupDef[@Name='DM']
(: and the location of the DM dataset :)
let $dmdatasetlocation := (
	if($defineversion = '2.1') then $dmitemgroupdef/def21:leaf/@xlink:href
	else $dmitemgroupdef/def:leaf/@xlink:href
)
let $dmdatasetdoc := doc(concat($base,$dmdatasetlocation))
(: get the OID of RFICDTC in DM :)
let $rficdtcoid := (
    for $a in $definedoc//odm:ItemDef[@Name='RFICDTC']/@OID 
    where $a = $dmitemgroupdef/odm:ItemRef/@ItemOID
    return $a 
)
(: we also need the OID of USUBJID in DM :)
let $dmusubjidoid := (
    for $a in $definedoc//odm:ItemDef[@Name='USUBJID']/@OID 
    where $a = $dmitemgroupdef/odm:ItemRef/@ItemOID
    return $a 
)
(: get the DS dataset and its location :)
let $dsitemgroupdef := $definedoc//odm:ItemGroupDef[@Name='DS']
let $dsdatasetlocation := (
	if($defineversion = '2.1') then $dsitemgroupdef/def21:leaf/@xlink:href
	else $dsitemgroupdef/def:leaf/@xlink:href
)
let $dsdatasetdoc := (
	if($dsdatasetlocation) then doc(concat($base,$dsdatasetlocation))
	else ()
)
(: and the OID of DSSTDTC and of DSTERM :)
let $dsstdtcoid := (
    for $a in $definedoc//odm:ItemDef[@Name='DSSTDTC']/@OID 
    where $a = $dsitemgroupdef/odm:ItemRef/@ItemOID
    return $a 
)
let $dstermoid := (
    for $a in $definedoc//odm:ItemDef[@Name='DSTERM']/@OID 
    where $a = $dsitemgroupdef/odm:ItemRef/@ItemOID
    return $a 
)
(: we also need the OID of USUBJID in DS :)
let $dsusubjidoid := (
    for $a in $definedoc//odm:ItemDef[@Name='USUBJID']/@OID 
    where $a = $dsitemgroupdef/odm:ItemRef/@ItemOID
    return $a 
)
(: iterate over all the records in DM that have a value for RFICDTC :)
(: for $dmrecord in $dmdatasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$rficdtcoid]] :)
(: 2020-08-08; we iterate over ALL records in DM, not only over these for which there is an RFICDTSC record :)
for $dmrecord in $dmdatasetdoc//odm:ItemGroupData
    let $recnum := $dmrecord/@data:ItemGroupDataSeq
    (: get the value of USUBJID :)
    let $dmusubjid := $dmrecord/odm:ItemData[@ItemOID=$dmusubjidoid]/@Value
    (: let $rficdtc := $dmrecord/odm:ItemData[@ItemOID=$rficdtcoid]/@Value :)
    (: get all records in DS for this subject which have DSTERM='INFORMED CONSENT OBTAINED' :)
    let $dsinfconsents := $dsdatasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$dsusubjidoid and @Value=$dmusubjid]]/odm:ItemData[@ItemOID=$dstermoid and @Value='INFORMED CONSENT OBTAINED']
    (: there must be at least one such record :)
    where count($dsinfconsents) = 0
    return <warning rule="SD1240" variable="DM" dataset="DM" rulelastupdate="2020-08-08" recordnumber="{$recnum}">No informed consent record (DSTERM='INFORMED CONSENT OBTAINED') in DS could be found for subject {data($dmusubjid)}</warning>			
	]]>
	</rulexquery>
</sdsrule>

<sdsrule originator="FDA" id="SD1258" standard="SDTM" last-update="2020-08-08">
	<ruledescription>When ACTARM in ('Screen Failure' 'Not Assigned' 'Not  Treated') then RFSTDTC must be null</ruledescription>
	<ruledetaileddescription>RFSTDTC must be null when actual arm indicates that the subject is a screen failure ('Screen Failure'), was not assigned to an arm ('Not Assigned'), or was not treated ('NOTTRT'). This rule applies to studies where RFSTDTC is associated with a treatment date</ruledetaileddescription>
	<igversion>3.1.3</igversion>
	<igversion>3.2</igversion>
	<domain>DM</domain>
	<synonym context="CDISC">CG0145</synonym>
	<rulexquery><![CDATA[
(: Rule SD1258 - RFSTDTC must be null when actual arm indicates that the subject is a screen failure ('Screen Failure'), was not assigned to an arm ('Not Assigned'), or was not treated ('NOTTRT'). 
This rule applies to studies where RFSTDTC is associated with a treatment date :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external; 
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdiscpilot01/' :)
(: let $define := 'define_2_0.xml' :)
let $definedoc := doc(concat($base,$define))
(: Get the DM dataset :)
let $dmitemgroupdef := $definedoc//odm:ItemGroupDef[@Name='DM']
(: and the location of the DM dataset :)
let $dmdatasetlocation := (
	if($defineversion = '2.1') then $dmitemgroupdef/def21:leaf/@xlink:href
	else $dmitemgroupdef/def:leaf/@xlink:href
)
let $dmdatasetdoc := doc(concat($base,$dmdatasetlocation))
(: get the OID of ACTARM and of RFSTDTC :)
let $actarmoid := (
    for $a in $definedoc//odm:ItemDef[@Name='ACTARM']/@OID 
    where $a = $dmitemgroupdef/odm:ItemRef/@ItemOID
    return $a 
)
let $rfstdtcoid := (
    for $a in $definedoc//odm:ItemDef[@Name='RFSTDTC']/@OID 
    where $a = $dmitemgroupdef/odm:ItemRef/@ItemOID
    return $a 
)
(: iterate over the records in DM :)
for $record in $dmdatasetdoc//odm:ItemGroupData
    let $recnum := $record/@data:ItemGroupDataSeq
    (: get the values of ACTARM and of RFSTDTC :)
    let $actarmvalue := $record/odm:ItemData[@ItemOID=$actarmoid]/@Value
    let $rfstdtcvalue := $record/odm:ItemData[@ItemOID=$rfstdtcoid]/@Value
    (: when ACTARM in ('Screen Failure' 'Not Assigned' 'Not  Treated') then RFSTDTC = null :)
    where ($actarmvalue='Screen Failure' or $actarmvalue='Not Assigned' or $actarmvalue='Not  Treated') and $rfstdtcvalue
    return <error rule="SD1258" variable="RFSTDTC" dataset="DM" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">RFSTDTC='{data($rfstdtcvalue)}' expected to be null for ACTARM='{data($actarmvalue)}'</error>						
		]]>
	</rulexquery>
</sdsrule>

<sdsrule originator="FDA" id="SD0087" standard="SDTM" last-update="2020-08-08">
	<ruledescription>RFSTDTC must be populated for all subjects, those where ARMCD is not equal to 'SCRNFAIL' or 'NOTASSGN'</ruledescription>
	<ruledetaileddescription>Subject Reference Start Date/Time (RFSTDTC) should be populated for all randomized subjects, those where Planned Arm Code (ARMCD) is not equal to 'SCRNFAIL' or 'NOTASSGN'</ruledetaileddescription>
	<igversion>3.1.2</igversion>
	<igversion>3.1.3</igversion>
	<igversion>3.2</igversion>
	<domain>DM</domain>
	<synonym context="CDISC">CG0146</synonym>
	<rulexquery><![CDATA[
(: Rule SD0087 - When Milestone associated with RFSTDTC is start of treatment and ACTARM not in ('Screen Failure' 'Not Assigned' 'Not Treated') then RFSTDTC != null  :)
(: We can only check that when ACTARM not in ('Screen Failure' 'Not Assigned' 'Not  Treated') then RFSTDTC != null :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external; 
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdiscpilot01/' :)
(: let $define := 'define_2_0.xml' :)
let $definedoc := doc(concat($base,$define))
(: Get the DM dataset :)
let $dmitemgroupdef := $definedoc//odm:ItemGroupDef[@Name='DM']
(: and the location of the DM dataset :)
let $dmdatasetlocation := (
	if($defineversion = '2.1') then $dmitemgroupdef/def21:leaf/@xlink:href
	else $dmitemgroupdef/def:leaf/@xlink:href
)
let $dmdatasetdoc := doc(concat($base,$dmdatasetlocation))
(: get the OID of ACTARM and of RFSTDTC :)
let $actarmoid := (
    for $a in $definedoc//odm:ItemDef[@Name='ACTARM']/@OID 
    where $a = $dmitemgroupdef/odm:ItemRef/@ItemOID
    return $a 
)
let $rfstdtcoid := (
    for $a in $definedoc//odm:ItemDef[@Name='RFSTDTC']/@OID 
    where $a = $dmitemgroupdef/odm:ItemRef/@ItemOID
    return $a 
)
(: iterate over the records in DM :)
for $record in $dmdatasetdoc//odm:ItemGroupData
    let $recnum := $record/@data:ItemGroupDataSeq
    (: get the values of ACTARM and of RFSTDTC :)
    let $actarmvalue := $record/odm:ItemData[@ItemOID=$actarmoid]/@Value
    let $rfstdtcvalue := $record/odm:ItemData[@ItemOID=$rfstdtcoid]/@Value
    (: when ACTARM not in ('Screen Failure', 'Not Assigned') then RFSTDTC != null :)
    where not($actarmvalue='Screen Failure' or $actarmvalue='Not Assigned') and not($rfstdtcvalue)
    return <error rule="SD0087" variable="RFSTDTC" dataset="DM" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">RFSTDTC expected to be not null for ACTARM='{data($actarmvalue)}'</error>			
		]]>
	</rulexquery>
</sdsrule>

<sdsrule originator="FDA" id="SD1259" standard="SDTM" last-update="2020-08-08">
	<ruledescription>The value of Set Code (SETCD) may not be more than 8 characters in length</ruledescription>
	<ruledetaileddescription>The value of Set Code (SETCD) should be no more than 8 characters in length</ruledetaileddescription>
	<igversion>3.1.2</igversion>
	<igversion>3.1.3</igversion>
	<igversion>3.2</igversion>
	<domain>DM</domain>
	<synonym context="CDISC">CG0149</synonym>
	<rulexquery><![CDATA[
(: Rule SD1259 - SETCD value length <= 8 :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
declare namespace functx = "http://www.functx.com";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external; 
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdiscpilot01/' :)
(: let $define := 'define_2_0.xml' :)
let $definedoc := doc(concat($base,$define))
(: Get the DM dataset :)
let $dmitemgroupdef := $definedoc//odm:ItemGroupDef[@Name='DM']
(: and the location of the DM dataset :)
let $dmdatasetlocation := (
	if($defineversion = '2.1') then $dmitemgroupdef/def21:leaf/@xlink:href
	else $dmitemgroupdef/def:leaf/@xlink:href
)
let $dmdatasetdoc := doc(concat($base,$dmdatasetlocation))
(: get the OID of SETCD in DM :)
let $setcdoid := (
    for $a in $definedoc//odm:ItemDef[@Name='SETCD']/@OID 
    where $a = $dmitemgroupdef/odm:ItemRef/@ItemOID
    return $a 
)
(: we may also need the OID of USUBJID in DM :)
let $dmusubjidoid := (
    for $a in $definedoc//odm:ItemDef[@Name='USUBJID']/@OID 
    where $a = $dmitemgroupdef/odm:ItemRef/@ItemOID
    return $a 
)
(: iterate over all the records in DM that have a value for SETCD :)
for $dmrecord in $dmdatasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$setcdoid]]
    let $recnum := $dmrecord/@data:ItemGroupDataSeq
    (: get the value of SETCD :)
    let $setcdvalue := $dmrecord/odm:ItemData[@ItemOID=$setcdoid]/@Value
    (: get the value of USUBJID - not necessarily needed :)
    let $dmusubjid := $dmrecord/odm:ItemData[@ItemOID=$dmusubjidoid]/@Value
    (: SETCD value length <= 8 :)
    where string-length($setcdvalue) > 8
    return <error rule="SD1259" variable="SETCD" dataset="DM" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">SETCD='{data($setcdvalue)}' has more than 8 characters</error>				
		]]>
	</rulexquery>
</sdsrule>

<sdsrule id="SD1001" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>SUBJID must be unique for each subject in the study</ruledescription>
<ruledetaileddescription>The value of Subject Identifier for the Study (SUBJID) variable must be unique for each subject within the study - DM</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>DM</domain>
<synonym context="CDISC">FDAC048</synonym>
<rulexquery><![CDATA[
(: Rule SD1001 - Duplicate SUBJID - The value of Subject Identifier for the Study (SUBJID) variable must be unique for each subject within the study - DM :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external;
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdiscpilot01/' :)
(: let $define := 'define_2_0.xml' :)
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: get the OID of SUBJID in define.xml for dataset DM :)
let $subjidoid :=  (
    for $a in $definedoc//odm:ItemDef[@Name='SUBJID']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name='DM']/odm:ItemRef/@ItemOID
        return $a
)
(: and the file where to find the DM records :)
let $datasetname := (
	if($defineversion = '2.1') then $definedoc//odm:ItemGroupDef[@Name='DM']/def21:leaf/@xlink:href
	else $definedoc//odm:ItemGroupDef[@Name='DM']/def:leaf/@xlink:href
)
let $dmdatasetdoc := doc(concat($base,$datasetname))
(: iterate over all records in the DM dataset :)
for $record in $dmdatasetdoc//odm:ItemGroupData
    (: get the value for the SUBJID data point :)
    let $subjidvalue := $record/odm:ItemData[@ItemOID=$subjidoid]/@Value
    let $recnum := $record/@data:ItemGroupDataSeq
    (: and get all following records with the same value of SUBJID :)
    let $count := count($dmdatasetdoc//odm:ItemGroupData[@data:ItemGroupDataSeq > $recnum][odm:ItemData[@ItemOID=$subjidoid][@Value=$subjidvalue]])
    where $count > 0
    return <error rule="SD1001" dataset="DM" variable="SUBJID" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">Duplicate value of SUBJID={data($subjidvalue)} - {data($count)} in dataset {data($datasetname)}</error>	
]]></rulexquery>
</sdsrule>

<sdsrule id="SD0083" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>USUBJID must be unique for each subject</ruledescription>
<ruledetaileddescription>The value of Unique Subject Identifier (USUBJID) variable must be unique for each subject across all trials in the submission</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>DM</domain>
<synonym context="CDISC">FDAC048</synonym>
<rulexquery><![CDATA[
(: Rule SD0083 - Duplicate USUBJID - The value of Subject Identifier for the Study (USUBJID) variable must be unique for each subject within the study - DM :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external;
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' 
let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: get the OID of USUBJID in define.xml for dataset DM :)
let $usubjidoid :=  (
    for $a in $definedoc//odm:ItemDef[@Name='USUBJID']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name='DM']/odm:ItemRef/@ItemOID
        return $a
)
(: and the file where to find the DM records :)
let $datasetname := (
	if($defineversion = '2.1') then $definedoc//odm:ItemGroupDef[@Name='DM']/def21:leaf/@xlink:href
	else $definedoc//odm:ItemGroupDef[@Name='DM']/def:leaf/@xlink:href
)
let $dmdatasetdoc := doc(concat($base,$datasetname))
(: iterate over all records in the DM dataset :)
for $record in $dmdatasetdoc//odm:ItemGroupData
    (: get the value for the SUBJID data point :)
    let $usubjidvalue := $record/odm:ItemData[@ItemOID=$usubjidoid]/@Value
    let $recnum := $record/@data:ItemGroupDataSeq
    (: and get all following records with the same value of SUBJID :)
    let $count := count($dmdatasetdoc//odm:ItemGroupData[@data:ItemGroupDataSeq > $recnum][odm:ItemData[@ItemOID=$usubjidoid][@Value=$usubjidvalue]])
    where $count > 0
    return <error rule="SD0083" dataset="DM" variable="SUBJID" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">Duplicate value of SUBJID={data($usubjidvalue)} - {data($count)} in dataset {data($datasetname)}</error>	
]]></rulexquery>
</sdsrule>

<sdsrule originator="FDA" id="SD1010" standard="SDTM" last-update="2020-08-08">
	<ruledescription>When ETCD = 'UNPLAN' then ELEMENT must be null</ruledescription>
	<ruledetaileddescription>Description of Element (ELEMENT) should be NULL, when subject's experience for a particular period of time is represented as an unplanned Element, where Element Code (ETCD) is equal to 'UNPLAN'</ruledetaileddescription>
	<igversion>3.1.2</igversion>
	<igversion>3.1.3</igversion>
	<igversion>3.2</igversion>
	<domain>SE</domain>
	<synonym context="CDISC">CG0152</synonym>
	<rulexquery><![CDATA[
(: Rule SD1010 - When ETCD = 'UNPLAN' then ELEMENT = null :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external;
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdiscpilot01/' :)
(: let $define := 'define_2_0.xml' :)
let $definedoc := doc(concat($base,$define))
(: get the SE dataset and its location :)
let $seitemgroupdef := $definedoc//odm:ItemGroupDef[@Name='SE']
let $sedatasetlocation := (
	if($defineversion = '2.1') then $seitemgroupdef/def21:leaf/@xlink:href
	else $seitemgroupdef/def:leaf/@xlink:href
)
let $sedatasetdoc := (
	if($sedatasetlocation) then doc(concat($base,$sedatasetlocation))
	else ()
)
(: we need the OID of ETCD and ELEMENT :)
let $etcdoid := (
    for $a in $definedoc//odm:ItemDef[@Name='ETCD']/@OID 
    where $a = $seitemgroupdef/odm:ItemRef/@ItemOID
    return $a 
)
let $elementoid := (
    for $a in $definedoc//odm:ItemDef[@Name='ELEMENT']/@OID 
    where $a = $seitemgroupdef/odm:ItemRef/@ItemOID
    return $a 
)
(: iterate over all the records in the SE dataset :)
for $record in $sedatasetdoc//odm:ItemGroupData
    let $recnum := $record/@data:ItemGroupDataSeq
    (: get the values of ETCD and of ELEMENT :)
    let $etcd := $record/odm:ItemData[@ItemOID=$etcdoid]/@Value
    let $element := $record/odm:ItemData[@ItemOID=$elementoid]/@Value
    (: When ETCD = 'UNPLAN' then ELEMENT = null :)
    where $etcd='UNPLAN' and $element  (: the latter meaning that ELEMENT is populated, so not null :)
    return <error rule="SD1010" dataset="SE" variable="ELEMENT" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">Invalid value for ELEMENT={data($element)}, null was expected for ETCD='UNPLAN'</error>			
		]]>
	</rulexquery>
</sdsrule>

<sdsrule originator="FDA" id="SD1004" standard="SDTM" last-update="2020-08-08">
	<ruledescription>ARMCD may be no more than 20 characters</ruledescription>
	<ruledetaileddescription>The value of Planned Arm Code (ARMCD) should be no more than 20 characters in length</ruledetaileddescription>
	<igversion>3.1.2</igversion>
	<igversion>3.1.3</igversion>
	<igversion>3.2</igversion>
	<domain>DM</domain>
	<domain>TA</domain>
	<domain>TV</domain>
	<synonym context="CDISC">CG0153</synonym>
	<rulexquery><![CDATA[
(: Rule SD1004 - ARMCD value length <= 20 
The value of Planned Arm Code (ARMCD) should be no more than 20 characters in length
:)
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external;
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: Get the DM and TA datasets:)
let $datasets := $definedoc//odm:ItemGroupDef[@Name='DM' or @Name='TA' or @Name='TV']
(: and iterate over these :)
for $dataset in $datasets
    (: get the dataset name and location :)
    let $name := $dataset/@Name
    let $datasetname := (
		if($defineversion = '2.1') then $dataset/def21:leaf/@xlink:href
		else $dataset/def:leaf/@xlink:href
	)
    let $datasetdoc := (
		if($datasetname) then doc(concat($base,$datasetname))
		else ()
	)
    (: and get the OID of the ARMCD variable - can be different in each dataset :)
    let $armcdoid := (
        for $a in $definedoc//odm:ItemDef[@Name='ARMCD']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
    )
    (: start iterating over all records in the dataset :)
    for $record in $datasetdoc//odm:ItemGroupData
    let $recnum := $record/@data:ItemGroupDataSeq
    (: and get the value of the ARMCD variable :)
    let $armcdvalue := $record/odm:ItemData[@ItemOID=$armcdoid]/@Value
    (: and check whether not more than 20 characters  :)
    where string-length($armcdvalue) > 20
    return <error rule="SD1004" dataset="{data($name)}" variable="ARMCD" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">Invalid value for ARMCD={data($armcdvalue)} in dataset {data($datasetname)} - it has more than 20 characters</error>			
		]]>
	</rulexquery>
</sdsrule>

<!-- TODO: algorithm can be considerably improved by grouping. SE is however a relative small dataset,
		so validation usually uses less than 5 seconds anyway -->
<sdsrule originator="FDA" id="SD1027" standard="SDTM" last-update="2020-08-08">
	<ruledescription>ELEMENT must be unique for a given ETCD</ruledescription>
	<ruledetaileddescription>Description of Element (ELEMENT) must have a unique value for a given value of Element Code (ETCD) within the domain</ruledetaileddescription>
	<igversion>3.1.2</igversion>
	<igversion>3.1.3</igversion>
	<igversion>3.2</igversion>
	<domain>SE</domain>
	<domain>TA</domain>
	<!--domain>TV</domain-->
	<synonym context="CDISC">CG0154</synonym>
	<rulexquery><![CDATA[
(: Rule SD1027 - ELEMENT present in dataset and ETCD present in dataset then ELEMENT and ETCD have a one-to-one relationship
Applicable to SE, TA, TE :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external;
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: Get the SE, TA and TE datasets:)
let $datasets := $definedoc//odm:ItemGroupDef[@Name='SE' or @Name='TA' or @Name='TE']
(: and iterate over these :)
for $datasetdef in $datasets
    (: get the dataset name and location :)
    let $name := $datasetdef/@Name
	let $datasetname := (
		if($defineversion = '2.1') then $datasetdef/def21:leaf/@xlink:href
		else $datasetdef/def:leaf/@xlink:href
	)
    let $datasetdoc := (
		if($datasetname) then doc(concat($base,$datasetname))
		else ()
	)
    (: get the OIDs of ETCD and ELEMENT :)
    let $etcdoid := (
        for $a in $definedoc//odm:ItemDef[@Name='ETCD']/@OID 
        where $a = $datasetdef/odm:ItemRef/@ItemOID
        return $a 
    )
    let $elementoid := (
        for $a in $definedoc//odm:ItemDef[@Name='ELEMENT']/@OID 
        where $a =$datasetdef/odm:ItemRef/@ItemOID
        return $a 
    )
    (: iterate over all records that have BOTH ETCD and ELEMENT populated :)
    for $record in $datasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$etcdoid] and odm:ItemData[@ItemOID=$elementoid]]
        let $recnum := $record/@data:ItemGroupDataSeq
        (: and get the values of ETCD and ELEMENT :)
        let $etcdvalue := $record/odm:ItemData[@ItemOID=$etcdoid]/@Value
        let $elementvalue := $record/odm:ItemData[@ItemOID=$elementoid]/@Value
        (: iterate over all further records that DO have ETCD and ELEMENT populated AND for which ETCD corresponds to the current value of ETCD :)
        for $record2 in $datasetdoc//odm:ItemGroupData[@data:ItemGroupDataSeq > $recnum][odm:ItemData[@ItemOID=$etcdoid] and odm:ItemData[@ItemOID=$elementoid] and odm:ItemData[@ItemOID=$etcdoid]/@Value=$etcdvalue]
            let $recnum2 := $record2/@data:ItemGroupDataSeq
            (: and again, get the values of ETCD and ELEMENT :)
            let $etcdvalue2 := $record2/odm:ItemData[@ItemOID=$etcdoid]/@Value (: should be identical to $etcdvalue :)
            let $elementvalue2 := $record2/odm:ItemData[@ItemOID=$elementoid]/@Value
            (: ELEMENT must be equal to ELEMENT of the the earlier record :)
            where not($elementvalue=$elementvalue2)
            return <error rule="SD1027" dataset="{data($name)}" variable="ETCD" rulelastupdate="2020-08-08" recordnumber="{data($recnum2)}">Non-unique combination for ELEMENT and ETCD. Record {data($recnum)} has ETCD='{data($etcdvalue)}' and and ELEMENT='{data($elementvalue)}' whereas record {data($recnum2)} has ETCD='{data($etcdvalue2)}' and has ELEMENT='{data($elementvalue2)}' in dataset {data($name)}</error>			
		]]>
	</rulexquery>
</sdsrule>

<sdsrule originator="FDA" id="SD1065" standard="SDTM" last-update="2020-08-08">
	<ruledescription>RDOMAIN must be populated when IDVAR is populated</ruledescription>
	<ruledetaileddescription>Value of Related Domain Abbreviation (RDOMAIN) variable must be populated, when value of Identifying Variable (IDVAR) variable is populated</ruledetaileddescription>
	<igversion>3.1.2</igversion>
	<igversion>3.1.3</igversion>
	<igversion>3.2</igversion>
	<domain>CO</domain>
	<synonym context="CDISC">CG0166</synonym>
	<rulexquery><![CDATA[
(: Rule SD1065 - CO domain - When IDVARVAL != null then RDOMAIN != null :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external;
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/SEND_Nimble/' :)
(: let $define := 'define_DSXML.xml' :)
let $definedoc := doc(concat($base,$define))
(: get the CO dataset :)
let $coitemgroupdef := $definedoc//odm:ItemGroupDef[@Name='CO']
let $codatasetlocation := (
	if($defineversion = '2.1') then $coitemgroupdef/def21:leaf/@xlink:href
	else $coitemgroupdef/def:leaf/@xlink:href
)
let $codatasetdoc := (
	if($codatasetlocation) then doc(concat($base,$codatasetlocation))
	else ()
)
(: get the OIDs of the RDOMAIN and IDVARVAL variables :)
let $rdomainoid := (
    for $a in $definedoc//odm:ItemDef[@Name='RDOMAIN']/@OID 
    where $a = $coitemgroupdef/odm:ItemRef/@ItemOID
    return $a 
)
let $idvarvaloid := (
    for $a in $definedoc//odm:ItemDef[@Name='IDVARVAL']/@OID 
    where $a = $coitemgroupdef/odm:ItemRef/@ItemOID
    return $a 
)
(: iterate over all CO records for which there IS a IDVARVAL data point :)
for $record in $codatasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$idvarvaloid and @Value]]
    let $recnum := $record/@data:ItemGroupDataSeq
    (: get the IDVARVAL value :)
    let $idvarval := $record/odm:ItemData[@ItemOID=$idvarvaloid]/@Value
    (: get the value of RDOMAIN (if any) :)
    let $rdomain := $record/odm:ItemData[@ItemOID=$rdomainoid]/@Value
    (: When IDVARVAL != null then RDOMAIN != null :)
    where not($rdomain)  (: there is no value for RDOMAIN :)
    return <error rule="SD1065" dataset="CO" variable="RDOMAIN" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">IDVARVAL={data($idvarval)} but RDOMAIN=null</error>			
		]]>
	</rulexquery>
</sdsrule>

<!-- Rule SD1120: Comments in SUPPQUAL domain. This is a stupid rule, as there is no traceability to the CRF (would work when using ODM instead of aCRF-PDF)  -->

<sdsrule originator="FDA" id="SD1008" standard="SDTM" last-update="2020-08-08">
	<ruledescription>CODTC must be NULL when RDOMAIN, IDVAR and IDVARVAL are populated</ruledescription>
	<ruledetaileddescription>The value of Date/Time of Comment (CODTC) should be NULL, when comments are related to a specific parent record or group of parent records in a domain (RDOMAIN, IDVAR and IDVARVAL are populated)</ruledetaileddescription>
	<igversion>3.1.2</igversion>
	<igversion>3.1.3</igversion>
	<igversion>3.2</igversion>
	<domain>CO</domain>
	<synonym context="CDISC">CG0168</synonym>
	<rulexquery><![CDATA[
(: Rule SD1008 - CODTC must be NULL when RDOMAIN, IDVAR and IDVARVAL are populated :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;  
declare variable $define external; 
declare variable $defineversion external;
let $definedoc := doc(concat($base,$define))
(: get the CO dataset :)
let $coitemgroupdef := $definedoc//odm:ItemGroupDef[@Name='CO' or @Domain='CO']
let $codatasetlocation := (
	if($defineversion = '2.1') then $coitemgroupdef/def21:leaf/@xlink:href
	else $coitemgroupdef/def:leaf/@xlink:href
)
let $codatasetdoc := (
	if($codatasetlocation) then doc(concat($base,$codatasetlocation))
	else ()
)
(: get the OIDs of the CODTC, IDVAR, IDVARVAL and RDOMAIN variables :)
let $codtcoid := (
    for $a in $definedoc//odm:ItemDef[@Name='CODTC']/@OID 
    where $a = $coitemgroupdef/odm:ItemRef/@ItemOID
    return $a 
)
let $idvaroid := (
    for $a in $definedoc//odm:ItemDef[@Name='IDVAR']/@OID 
    where $a = $coitemgroupdef/odm:ItemRef/@ItemOID
    return $a 
)
let $idvarvaloid := (
    for $a in $definedoc//odm:ItemDef[@Name='IDVARVAL']/@OID 
    where $a = $coitemgroupdef/odm:ItemRef/@ItemOID
    return $a 
)
let $rdomainoid := (
    for $a in $definedoc//odm:ItemDef[@Name='RDOMAIN']/@OID 
    where $a = $coitemgroupdef/odm:ItemRef/@ItemOID
    return $a 
)
(: iterate over all CO records for which IDVAR != null, IDVARVAL != null and RDOMAIN != null :)
for $record in $codatasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$idvaroid and @Value] and odm:ItemData[@ItemOID=$idvarvaloid and @Value] and odm:ItemData[@ItemOID=$rdomainoid and @Value]]
    let $recnum := $record/@data:ItemGroupDataSeq
    (: get the IDVAR value :)
    let $idvar := $record/odm:ItemData[@ItemOID=$idvaroid]/@Value
    (: get the value of CODTC (if any) :)
    let $codtc := $record/odm:ItemData[@ItemOID=$codtcoid]/@Value
    (: When IDVAR != null then CODTC = null :)
    where $codtc  (: there IS a value for CODTC :)
    return <warning rule="SD1008" dataset="CO" variable="CODTC" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">IDVAR={data($idvar)} but a value for CODTC={data($codtc)} was found</warning>					
	]]>
	</rulexquery>
</sdsrule>	

<!-- Rule SD1119: Inappropriate usage of variables in CO domain: not implemented as it is completely unclear what the rule exactly is. -->

<sdsrule originator="FDA" id="SD1261" standard="SDTM" last-update="2020-08-08">
	<ruledescription>SSDTC must be greater than or equal to DTHDTC when SSSTRESC is 'DEAD'</ruledescription>
	<ruledetaileddescription>Subject Status Date/Time of Assessment (SSDTC) must be greater than or equal to Date/Time of Death (DTHDTC) when the subject's status is 'DEAD'</ruledetaileddescription>
	<domain>SS</domain>
	<igversion>3.2</igversion>
	<synonym context="CDISC">CG0171</synonym>
	<rulexquery><![CDATA[
(: Rule SD1261: When SSSTRESC = 'DEAD' then SSDTC >= DM.DTHDTC :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external; 
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: Get the DM dataset :)
let $dmitemgroupdef := $definedoc//odm:ItemGroupDef[@Name='DM']
(: and the location of the DM dataset :)
let $dmdatasetlocation := (
	if($defineversion = '2.1') then $dmitemgroupdef/def21:leaf/@xlink:href
	else $dmitemgroupdef/def:leaf/@xlink:href
)
let $dmdatasetdoc := doc(concat($base,$dmdatasetlocation))
(: Get the SS (Subject Status) dataset and its location :)
let $ssitemgroupdef := $definedoc//odm:ItemGroupDef[@Name='SS']
let $ssdatasetlocation := (
	if($defineversion = '2.1') then $ssitemgroupdef/def21:leaf/@xlink:href
	else $ssitemgroupdef/def:leaf/@xlink:href
)
let $ssdatasetdoc := (
	if($ssdatasetlocation) then doc(concat($base,$ssdatasetlocation))
	else ()
)
(: we need the OID of DTHDTC and of USUBJID in DM :)
let $dmdthdtcoid := (
    for $a in $definedoc//odm:ItemDef[@Name='DTHDTC']/@OID 
    where $a = $dmitemgroupdef/odm:ItemRef/@ItemOID
    return $a
)
let $dmusubjidoid := (
    for $a in $definedoc//odm:ItemDef[@Name='USUBJID']/@OID 
    where $a = $dmitemgroupdef/odm:ItemRef/@ItemOID
    return $a
)
(: we need the OID of SSSTRESC, SSDTC and of USUBJID in SS :)
let $ssusubjidoid := (
    for $a in $definedoc//odm:ItemDef[@Name='USUBJID']/@OID 
    where $a = $ssitemgroupdef/odm:ItemRef/@ItemOID
    return $a
)
let $ssstresc := (
    for $a in $definedoc//odm:ItemDef[@Name='SSSTRESC']/@OID 
    where $a = $ssitemgroupdef/odm:ItemRef/@ItemOID
    return $a
)
let $ssdtcoid := (
    for $a in $definedoc//odm:ItemDef[@Name='SSDTC']/@OID 
    where $a = $ssitemgroupdef/odm:ItemRef/@ItemOID
    return $a
)
(: iterate over all records in SS for which SSSTRESC='DEAD' :)
for $record in $ssdatasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$ssstresc and @Value='DEAD']]
    let $recnumss := $record/@data:ItemGroupDataSeq
    (: get the value of USUBJID for this record :)
    let $ssusubjid := $record/odm:ItemData[@ItemOID=$ssusubjidoid]/@Value
    (: get the value of SSDTC for this record :)
    let $ssdtc := $record/odm:ItemData[@ItemOID=$ssdtcoid]/@Value
    (: get the DM record for this subject :)
    let $dmrecord := $dmdatasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$dmusubjidoid and @Value=$ssusubjid]]
    let $dmrecnum := $dmrecord/@data:ItemGroupDataSeq
    (: get the value of DTHDTC :)
    let $dthdtc := $dmrecord/odm:ItemData[@ItemOID=$dmdthdtcoid]/@Value
    (: SSDTC >= DM.DTHDTC :)
    where $ssdtc castable as xs:date and $dthdtc castable as xs:date and not(xs:date($ssdtc) >= xs:date($dthdtc))
    return <error rule="SD1261" variable="SSDTC" dataset="SS" recordnumber="{data($recnumss)}" rulelastupdate="2020-08-08">SSDTC={data($ssdtc)} is not before or equal to DM.DTHDTC={data($dthdtc)} for SSSTRESC='DEAD'</error>			
		]]>
	</rulexquery>
</sdsrule>


<sdsrule originator="FDA" id="SD1262" standard="SDTM" last-update="2020-08-08">
	<ruledescription>SSDTC must be greater than or equal to DSSTDTC when SSSTRESC = 'DEAD'</ruledescription>
	<ruledetaileddescription>Subject Status Date/Time of Assessment (SSDTC) must be greater than or equal to Start Date/Time of Disposition Event (DSSTDTC) when the subject's status is 'DEAD'</ruledetaileddescription>
	<igversion>3.2</igversion>
	<domain>SS</domain>
	<synonym context="CDISC">CG0172</synonym>
	<rulexquery><![CDATA[
(: Rule SD1262: When SSSTRESC = 'DEAD' then SSDTC >= DS.DSSTDTC :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
declare namespace functx = "http://www.functx.com";
(: sorting function - also sorts dates :)
declare function functx:sort
  ( $seq as item()* )  as item()* {

   for $item in $seq
   order by $item
   return $item
 } ;
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external; 
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: Get the DS dataset :)
let $dsitemgroupdef := $definedoc//odm:ItemGroupDef[@Name='DS']
(: and the location of the DS dataset :)
let $dsdatasetlocation := (
	if($defineversion = '2.1') then $dsitemgroupdef/def21:leaf/@xlink:href
	else $dsitemgroupdef/def:leaf/@xlink:href
)
let $dsdatasetdoc := doc(concat($base,$dsdatasetlocation))
(: Get the SS (Subject Status) dataset and its location :)
let $ssitemgroupdef := $definedoc//odm:ItemGroupDef[@Name='SS']
let $ssdatasetlocation := (
	if($defineversion = '2.1') then $ssitemgroupdef/def21:leaf/@xlink:href
	else $ssitemgroupdef/def:leaf/@xlink:href
)
let $ssdatasetdoc := (
	if($ssdatasetlocation) then doc(concat($base,$ssdatasetlocation))
	else ()
)
(: we need the OID of DSSTDTC and of USUBJID in DS :)
let $dsstdtcoid := (
    for $a in $definedoc//odm:ItemDef[@Name='DSSTDTC']/@OID 
    where $a = $dsitemgroupdef/odm:ItemRef/@ItemOID
    return $a
)
let $dsusubjidoid := (
    for $a in $definedoc//odm:ItemDef[@Name='USUBJID']/@OID 
    where $a = $dsitemgroupdef/odm:ItemRef/@ItemOID
    return $a
)
(: we need the OID of SSSTRESC, SSDTC and of USUBJID in SS :)
let $ssusubjidoid := (
    for $a in $definedoc//odm:ItemDef[@Name='USUBJID']/@OID 
    where $a = $ssitemgroupdef/odm:ItemRef/@ItemOID
    return $a
)
let $ssstresc := (
    for $a in $definedoc//odm:ItemDef[@Name='SSSTRESC']/@OID 
    where $a = $ssitemgroupdef/odm:ItemRef/@ItemOID
    return $a
)
let $ssdtcoid := (
    for $a in $definedoc//odm:ItemDef[@Name='SSDTC']/@OID 
    where $a = $ssitemgroupdef/odm:ItemRef/@ItemOID
    return $a
)
(: iterate over all records in SS for which SSSTRESC='DEAD' :)
for $record in $ssdatasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$ssstresc and @Value='DEAD']]
    let $recnumss := $record/@data:ItemGroupDataSeq
    (: get the value of USUBJID for this record :)
    let $ssusubjid := $record/odm:ItemData[@ItemOID=$ssusubjidoid]/@Value
    (: get the value of SSDTC for this record :)
    let $ssdtc := $record/odm:ItemData[@ItemOID=$ssdtcoid]/@Value
    (: get all the DSSTDTC values for this subject :)
    let $dsstdtcvalues := $dsdatasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$dsusubjidoid and @Value=$ssusubjid]]/odm:ItemData[@ItemOID=$dsstdtcoid]/@Value
    (: sort the DSSTDTC values and take the latest one :)
    let $latestdsstdtc := functx:sort($dsstdtcvalues)[last()]
    (: SSDTC >= DS.DSSTDTC :)
    where $ssdtc castable as xs:date and $latestdsstdtc castable as xs:date and not(xs:date($ssdtc) >= xs:date($latestdsstdtc))
    return <error rule="SD1262" variable="SSDTC" dataset="SS" recordnumber="{data($recnumss)}" rulelastupdate="2020-08-08">SSDTC={data($ssdtc)} is not before or equal to latest DSSTDTC={data($ssdtc)} for SSSTRESC='DEAD'</error>			
		]]>
	</rulexquery>
</sdsrule>

<sdsrule originator="FDA" id="SD1045" standard="SDTM" last-update="2020-08-08">
	<ruledescription>When IECAT = 'EXCLUSION' then IEORRES must be 'Y'</ruledescription>
	<ruledetaileddescription>I/E Criterion Original Result (IEORRES) must equal 'Y', when Inclusion/Exclusion Category (IECAT) is 'EXCLUSION'</ruledetaileddescription>
	<igversion>3.1.2</igversion>
	<igversion>3.1.3</igversion>
	<igversion>3.2</igversion>
	<domain>IE</domain>
	<synonym context="CDISC">CG0175</synonym>
	<rulexquery><![CDATA[
(: Rule SD1045 - When IECAT = 'EXCLUSION' then IEORRES = 'Y' :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :) 
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: get the IE dataset :)
for $iedataset in $definedoc//odm:ItemGroupDef[@Name="IE"]
let $iedatasetname := (
	if($defineversion = '2.1') then $iedataset/def21:leaf/@xlink:href
	else $iedataset/def:leaf/@xlink:href
)
let $iedatasetdoc := (
	if($iedatasetname) then doc(concat($base,$iedatasetname))
	else ()
)
(: and get the OID of the IEORRES and IECAT variables :)
let $ieorresoid := (
    for $a in $definedoc//odm:ItemDef[@Name='IEORRES']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name='IE']/odm:ItemRef/@ItemOID
        return $a
)
let $iecatoid := (
    for $a in $definedoc//odm:ItemDef[@Name='IECAT']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name='IE']/odm:ItemRef/@ItemOID
        return $a
)
(: iterate over all the records in the IE dataset :)
for $record in $iedatasetdoc//odm:ItemGroupData
    let $recnum := $record/@data:ItemGroupDataSeq
    (: and get the values for IEORRES and IECAT :)
    let $ieorresvalue := $record/odm:ItemData[@ItemOID=$ieorresoid]/@Value
    let $iecatvalue := $record/odm:ItemData[@ItemOID=$iecatoid]/@Value
    (: When IECAT = 'EXCLUSION' then IEORRES = 'Y' :)
    where $iecatvalue='EXCLUSION' and not($ieorresvalue='Y')
    return <error rule="SD1045" variable="IEORRES" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}" dataset="IE">For IECAT='EXCLUSION', IEORRES must be 'Y' but {data($ieorresvalue)} was found</error>			
]]>
	</rulexquery>
</sdsrule>

<sdsrule originator="FDA" id="SD1046" standard="SDTM" last-update="2020-08-08">
	<ruledescription>When IECAT = 'INCLUSION' then IEORRES must be 'N'</ruledescription>
	<ruledetaileddescription>I/E Criterion Original Result (IEORRES) must equal 'N', when Inclusion/Exclusion Category (IECAT) is 'INCLUSION'</ruledetaileddescription>
	<igversion>3.1.2</igversion>
	<igversion>3.1.3</igversion>
	<igversion>3.2</igversion>
	<domain>IE</domain>
	<synonym context="CDISC">CG0176</synonym>
	<rulexquery><![CDATA[
(: Rule SD1046 - When IECAT = 'INCLUSION' then IEORRES = 'N' :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: get the IE dataset :)
for $iedataset in $definedoc//odm:ItemGroupDef[@Name="IE"]
let $iedatasetname := (
	if($defineversion = '2.1') then $iedataset/def21:leaf/@xlink:href
	else $iedataset/def:leaf/@xlink:href
)
let $iedatasetdoc := (
	if($iedatasetname) then doc(concat($base,$iedatasetname))
	else ()
)
(: and get the OID of the IEORRES and IECAT variables :)
let $ieorresoid := (
    for $a in $definedoc//odm:ItemDef[@Name='IEORRES']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name='IE']/odm:ItemRef/@ItemOID
        return $a
)
let $iecatoid := (
    for $a in $definedoc//odm:ItemDef[@Name='IECAT']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name='IE']/odm:ItemRef/@ItemOID
        return $a
)
(: iterate over all the records in the IE dataset :)
for $record in $iedatasetdoc//odm:ItemGroupData
    let $recnum := $record/@data:ItemGroupDataSeq
    (: and get the values for IEORRES and IECAT :)
    let $ieorresvalue := $record/odm:ItemData[@ItemOID=$ieorresoid]/@Value
    let $iecatvalue := $record/odm:ItemData[@ItemOID=$iecatoid]/@Value
    (: When IECAT = 'INCLUSION' then IEORRES = 'N' :)
    where $iecatvalue='INCLUSION' and not($ieorresvalue='N')
    return <error rule="SD1046" variable="IEORRES" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}" dataset="IE" >For IECAT='INCLUSION', IEORRES must be 'N' but {data($ieorresvalue)} was found</error>			
		]]>
	</rulexquery>
</sdsrule>

<sdsrule originator="FDA" id="SD0085" standard="SDTM" last-update="2020-08-08">
	<ruledescription>IESTRESC and IEORRES must have the same value</ruledescription>
	<ruledetaileddescription>I/E Criterion Original Result (IEORRES) and I/E Criterion Result in Std Format (IESTRESC) should have the same value</ruledetaileddescription>
	<igversion>3.1.2</igversion>
	<igversion>3.1.3</igversion>
	<igversion>3.2</igversion>
	<domain>IE</domain>
	<synonym context="CDISC">CG0177</synonym>
	<rulexquery><![CDATA[
(: Rule SD0085 - IESTRESC = IEORRES :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: Get the IE data :)
let $iedataset := $definedoc//odm:ItemGroupDef[@Name='IE']
let $iedatasetname := (
	if($defineversion = '2.1') then $iedataset/def21:leaf/@xlink:href
	else $iedataset/def:leaf/@xlink:href
)
let $iedatasetdoc := (
	if($iedatasetname) then doc(concat($base,$iedatasetname))
	else ()
)
(: and the OID of the USUBJID in the IE dataset :)
let $ieusubjidoid := (
    for $a in $definedoc//odm:ItemDef[@Name='USUBJID']/@OID 
    where $a = $definedoc//odm:ItemGroupDef[@Name='IE']/odm:ItemRef/@ItemOID
    return $a
)
(: as well as the OIDs of IEORRES and IESTRESC :)
let $ieorresoid := (
    for $a in $definedoc//odm:ItemDef[@Name='IEORRES']/@OID 
    where $a = $definedoc//odm:ItemGroupDef[@Name='IE']/odm:ItemRef/@ItemOID
    return $a
)
let $iestrescoid := (
    for $a in $definedoc//odm:ItemDef[@Name='IESTRESC']/@OID 
    where $a = $definedoc//odm:ItemGroupDef[@Name='IE']/odm:ItemRef/@ItemOID
    return $a
)
(: iterate over all the records in the IE dataset :)
for $record in $iedatasetdoc//odm:ItemGroupData
    let $recnum := $record/@data:ItemGroupDataSeq
    let $usubjid := $record/odm:ItemData[@ItemOID=$ieusubjidoid]/@Value
    let $ieorres := $record/odm:ItemData[@ItemOID=$ieorresoid]/@Value
    let $iestresc := $record/odm:ItemData[@ItemOID=$iestrescoid]/@Value
    (: value for IEORRES and IESTRESC must be equal :)
    where not($ieorres = $iestresc)
    return <error rule="SD0085" dataset="IE" variable="IESTRESC" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">Mismatch between IEORRES and IESTRESC values for USUBJID={data($usubjid)}. IEORRES={data($ieorres)}, whereas IESTRESC={data($iestresc)}</error>			
		]]>
	</rulexquery>
</sdsrule>

	
<sdsrule originator="FDA" id="SD1016" standard="SDTM" last-update="2020-08-08">
	<ruledescription>Combination of IETESTCD, IETEST, and IECAT values must match entries in TI</ruledescription>
	<ruledetaileddescription>The combination of IETESTCD, IETEST, and IECAT values must match entries in the Trial Inclusion/Exclusion Criteria (TI) dataset</ruledetaileddescription>
	<igversion>3.1.2</igversion>
	<igversion>3.1.3</igversion>
	<igversion>3.2</igversion>
	<domain>IE</domain>
	<synonym context="CDISC">CG0178</synonym>
	<rulexquery><![CDATA[
(: Rule SD1016: The combination of Inclusion/Exclusion Criterion Short Name (IETESTCD), Criterion (IETEST), and Category (IECAT) values should match entries in the Trial Inclusion/Exclusion Criteria (TI) dataset :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
declare namespace functx = "http://www.functx.com";
(: function to find out whether a value is in a sequence (array) :)
declare function functx:is-value-in-sequence
  ( $value as xs:anyAtomicType? ,
    $seq as xs:anyAtomicType* )  as xs:boolean {
   $value = $seq
} ;
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/'  :)
(: let $define := 'define2-0-0-example-sdtm.xml'  :)
let $definedoc := doc(concat($base,$define))  
(: Get the IE dataset :)
let $iedataset := $definedoc//odm:ItemGroupDef[@Name='IE']
let $iedatasetname := (
	if($defineversion = '2.1') then $iedataset/def21:leaf/@xlink:href
	else $iedataset/def:leaf/@xlink:href
)
let $iedatasetdoc := (
	if($iedatasetname) then doc(concat($base,$iedatasetname))
	else ()
)
(: get the TI dataset :)
let $tidataset := $definedoc//odm:ItemGroupDef[@Name='TI']
let $tidatasetlocation := (
	if($defineversion = '2.1') then $tidataset/def21:leaf/@xlink:href
	else $tidataset/def:leaf/@xlink:href
)
let $tidatasetdoc := (
	if($tidatasetlocation) then doc(concat($base,$tidatasetlocation))
	else ()
)
(: get the OID of TI.TITESTCD, TI.IETEST, and TI.TICAT :)
let $titestcdoid := (
    for $a in $definedoc//odm:ItemDef[@Name='IETESTCD']/@OID 
    where $a = $definedoc//odm:ItemGroupDef[@Name='TI']/odm:ItemRef/@ItemOID
    return $a
)
let $titestoid := (
    for $a in $definedoc//odm:ItemDef[@Name='IETEST']/@OID 
    where $a = $definedoc//odm:ItemGroupDef[@Name='TI']/odm:ItemRef/@ItemOID
    return $a
)
let $ticatoid := (
    for $a in $definedoc//odm:ItemDef[@Name='TICAT']/@OID 
    where $a = $definedoc//odm:ItemGroupDef[@Name='TI']/odm:ItemRef/@ItemOID
    return $a
)
(: get all the values of TI.IETEST - this is a sequence (array) :)
(: let $tiietestvalues := $tidatasetdoc//odm:ItemGroupData/odm:ItemData[@ItemOID=$tiietestoid]/@Value :)
(: generate a temporary structure containing all the triples from TI :)
let $titriples := (
    for $record in $tidatasetdoc//odm:ItemGroupData
    let $titestcd := $record/odm:ItemData[@ItemOID=$titestcdoid]/@Value
    let $titest := $record/odm:ItemData[@ItemOID=$titestoid]/@Value
    let $ticat := $record/odm:ItemData[@ItemOID=$ticatoid]/@Value
    return <record titestcd="{$titestcd}" titest="{$titest}" ticat="{$ticat}" />
)

(: get the OID of IETESTCD, IETEST and IECAT in the IE dataset :)
let $ietestcdoid := (
    for $a in $definedoc//odm:ItemDef[@Name='IETESTCD']/@OID 
    where $a = $definedoc//odm:ItemGroupDef[@Name='IE']/odm:ItemRef/@ItemOID
    return $a
)
let $ietestoid := (
    for $a in $definedoc//odm:ItemDef[@Name='IETEST']/@OID 
    where $a = $definedoc//odm:ItemGroupDef[@Name='IE']/odm:ItemRef/@ItemOID
    return $a
)
let $iecatoid := (
    for $a in $definedoc//odm:ItemDef[@Name='IECAT']/@OID 
    where $a = $definedoc//odm:ItemGroupDef[@Name='IE']/odm:ItemRef/@ItemOID
    return $a
)
(: iterate over all the records in the IE dataset :)
for $record in $iedatasetdoc//odm:ItemGroupData
    let $recnum := $record/@data:ItemGroupDataSeq
    (: get the value of ITESTCD, IETEST and IECAT in IE :)
    let $ietestcd := $record/odm:ItemData[@ItemOID=$ietestcdoid]/@Value
    let $ietest := $record/odm:ItemData[@ItemOID=$ietestoid]/@Value
    let $iecat := $record/odm:ItemData[@ItemOID=$iecatoid]/@Value
    (: match with the values in the temporary structure  :)
    let $count := count ($titriples/record[@titestcd=$ietestcd and @titest=$ietest and @ticat=$iecat])
    (: there must be a match :)
    where $count = 0
    return <error rule="SD1016" dataset="IE" variable="IETEST" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">The combination IETESTCD='{data($ietestcd)}', IETEST='{data($ietest)}' and IECAT='{data($iecat)}' cannot be found in the TI dataset</error>
		]]>
	</rulexquery>
</sdsrule>

	
<sdsrule originator="FDA" id="SD0068" standard="SDTM" last-update="2020-08-08">
	<ruledescription>A value for IETESTCD must be present in TI</ruledescription>
	<ruledetaileddescription>A value for Inclusion/Exclusion Criterion Short Name (IETESTCD) must be present the Trial Inclusion/Exclusion Criteria (TI) domain</ruledetaileddescription>
	<igversion>3.1.2</igversion>
	<igversion>3.1.3</igversion>
	<igversion>3.2</igversion>
	<domain>IE</domain>
	<synonym context="CDISC">CG0179</synonym>
	<rulexquery><![CDATA[
(: Rule SD0068 - IETESTCD in TI.IETESTCD :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
declare namespace functx = "http://www.functx.com";
(: function to find out whether a value is in a sequence (array) :)
declare function functx:is-value-in-sequence
  ( $value as xs:anyAtomicType? ,
    $seq as xs:anyAtomicType* )  as xs:boolean {
   $value = $seq
} ;
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: Get the IE dataset :)
let $iedataset := $definedoc//odm:ItemGroupDef[@Name='IE']
let $iedatasetname := (
	if($defineversion = '2.1') then $iedataset/def21:leaf/@xlink:href
	else $iedataset/def:leaf/@xlink:href
)
let $iedatasetdoc := (
	if($iedatasetname) then doc(concat($base,$iedatasetname))
	else ()
)
(: get the TI dataset :)
let $tidataset := $definedoc//odm:ItemGroupDef[@Name='TI']
let $tidatasetlocation := (
	if($defineversion = '2.1') then $tidataset/def21:leaf/@xlink:href
	else $tidataset/def:leaf/@xlink:href
)
let $tidatasetdoc := (
	if($tidatasetlocation) then doc(concat($base,$tidatasetlocation))
	else ()
)
(: get the OID of TI.IETESTCD :)
let $tiietestcdoid := (
    for $a in $definedoc//odm:ItemDef[@Name='IETESTCD']/@OID 
    where $a = $definedoc//odm:ItemGroupDef[@Name='TI']/odm:ItemRef/@ItemOID
    return $a
)
(: get all the values of TI.IETESTCD - this is a sequence (array) :)
let $tiietestcdvalues := $tidatasetdoc//odm:ItemGroupData/odm:ItemData[@ItemOID=$tiietestcdoid]/@Value

(: get the OID of IETESTCD in the IE dataset :)
let $ieietestcdoid := (
    for $a in $definedoc//odm:ItemDef[@Name='IETESTCD']/@OID 
    where $a = $definedoc//odm:ItemGroupDef[@Name='IE']/odm:ItemRef/@ItemOID
    return $a
)
(: iterate over all the records in the IE dataset :)
for $record in $iedatasetdoc//odm:ItemGroupData
    let $recnum := $record/@data:ItemGroupDataSeq
    (: get the value of IETESTCD in IE :)
    let $ieietestcd := $record/odm:ItemData[@ItemOID=$ieietestcdoid]/@Value
    (: IETESTCD in TI.IETESTCD :)
    where not(functx:is-value-in-sequence($ieietestcd,$tiietestcdvalues))
    return <error rule="SD0068" dataset="IE" variable="IETESTCD" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">Value of IE.IETESTCD={data($ieietestcd)} cannot be found in the TI dataset</error>				
		]]>
	</rulexquery>
</sdsrule>


<!-- Rule SD1298: 
We use the following implementation: it is checked whether the value of LBTOXGR contains a number.
This is however a bit tricky  -->
<sdsrule originator="FDA" id="SD1298" standard="SDTM" last-update="2020-08-08">
	<ruledescription>LBTOXGR must be a numeric value when a numeric scale is used</ruledescription>
	<ruledetaileddescription>When a numeric scale is used (Grade 1, Grade 2, etc.), LBTOXGR should only contain the number</ruledetaileddescription>
	<igversion>3.1.2</igversion>
	<igversion>3.1.3</igversion>
	<igversion>3.2</igversion>
	<domain>LB</domain>
	<synonym context="CDISC">CG0185</synonym>
	<rulexquery><![CDATA[
(: Rule SD1298 - When Numeric scale used then LBTOXGR is a numeric value :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
declare namespace functx = "http://www.functx.com";
(: function counting the number of matches for a pattern 
 See http://www.xqueryfunctions.com/xq/alpha.html :)
declare function functx:number-of-matches
  ( $arg as xs:string? ,
    $pattern as xs:string )  as xs:integer {
   count(tokenize($arg,$pattern)) - 1
} ;
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdiscpilot01/' :)
(: let $define := 'define_2_0.xml' :)
let $definedoc := doc(concat($base,$define))
(: get the LB datasets (just in case it is splitted) :)
for $lbdatasetdef in $definedoc//odm:ItemGroupDef[starts-with(@Name,'LB') or @Domain='LB']
    let $name := $lbdatasetdef/@Name
    (: get the OID of the LBTOXGR variable :)
    let $lbtoxgroid := (
        for $a in $definedoc//odm:ItemDef[@Name='LBTOXGR']/@OID 
        where $a = $lbdatasetdef/odm:ItemRef/@ItemOID
        return $a
    )
    (: get the dataset location :)
    let $datasetlocation := (
		if($defineversion = '2.1') then $lbdatasetdef/def21:leaf/@xlink:href
		else $lbdatasetdef/def:leaf/@xlink:href
	)
    let $datasetdoc := (
		if($datasetlocation) then doc(concat($base,$datasetlocation))
		else ()
	)
    (: iterate over all records in the dataset that have a value for LBTOXGR (if there is one defined) :)
    for $record in $datasetdoc[$lbtoxgroid]//odm:ItemGroupData[odm:ItemData[@ItemOID=$lbtoxgroid and @Value]]
        let $recnum := $record/@data:ItemGroupDataSeq
        (: get the value of LBTOXGR (as string) :)
        let $lbtoxgr := $record/odm:ItemData[@ItemOID=$lbtoxgroid]/@Value
        (: give an error when the value is NOT a number, but does contain a number.
        The function functx:number-of-matches($lbtoxgr,'\d') counts the number of decimal characters :)
        where not($lbtoxgr castable as xs:double) and functx:number-of-matches($lbtoxgr,'\d') > 0
        return <error rule="SD1298" dataset="{data($name)}" variable="LBTOXGR" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">Value of LBTOXGR={data($lbtoxgr)} represents a numeric scale but is not a complete number</error>			
		]]>
	</rulexquery>
</sdsrule>

<sdsrule id="SD1061" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>MB dataset must be included, when an MS dataset is present</ruledescription>
<ruledetaileddescription>Microbiology Specimen (MB) dataset should be included, when a Microbiology Susceptibility Test (MS) dataset is present</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>MB</domain>
<domain>MS</domain>
<rulexquery><![CDATA[
(: ##### Not well tested yet ###### :)
(: Rule SD1061 - Missing MB dataset when MS dataset is present: Microbiology Specimen (MB) dataset should be included, when a Microbiology Susceptibility Test (MS) dataset is present :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
declare namespace request="http://exist-db.org/xquery/request";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external;
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: get the location of the MS dataset :)
let $msdatasetname := (
	if($defineversion = '2.1') then $definedoc//odm:ItemGroupDef[@Name='MS']/def21:leaf/@xlink:href
	else $definedoc//odm:ItemGroupDef[@Name='MS']/def:leaf/@xlink:href
)
let $msdatasetlocation := concat($base,$msdatasetname)
(: get the location of the MB dataset - when given anyway :)
let $mbdatasetname := (
	if($defineversion = '2.1') then $definedoc//odm:ItemGroupDef[@Name='MB']/def21:leaf/@xlink:href
	else $definedoc//odm:ItemGroupDef[@Name='MB']/def:leaf/@xlink:href
)
let $mbdatasetlocation := concat($base,$mbdatasetname)
(: when MB is present, MS may not be absent :)
where (doc-available($msdatasetlocation) and not(doc-available($mbdatasetlocation)))
return <warning rule="SD1061" dataset="MB" rulelastupdate="2020-08-08">Document {data($mbdatasetname)} for domain MB could not be found in collection {data($base)} although document {data($msdatasetname)} is present</warning>
]]></rulexquery>
</sdsrule>

<sdsrule originator="FDA" id="SD1263" standard="SDTM" last-update="2020-08-08">
	<ruledescription>When MBTESTCD = 'ORGANISM' then MBRESCAT may not be null</ruledescription>
	<ruledetaileddescription>MBRESCAT is expected to be populated in all records where a microorganism has been identified (MBTESTCD = 'ORGANISM'), excluding records where MBSTRESC equals 'NO GROWTH' or MBMETHOD equals 'GRAM STAIN'.</ruledetaileddescription>
	<igversion>3.1.2</igversion>
	<igversion>3.1.3</igversion>
	<igversion>3.2</igversion>
	<domain>MB</domain>
	<synonym context="CDISC">CG0193</synonym>
	<rulexquery><![CDATA[
(: Rule SD1263 - When MBTESTCD = 'ORGANISM' then MBRESCAT != null :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdiscpilot01/' :)
(: let $define := 'define_2_0.xml' :)
let $definedoc := doc(concat($base,$define))
(: get the MB dataset and its location :)
for $mbitemgroupdef in $definedoc//odm:ItemGroupDef[@Name='MB']
let $mbdatasetlocation := (
	if($defineversion = '2.1') then $mbitemgroupdef/def21:leaf/@xlink:href
	else $mbitemgroupdef/def:leaf/@xlink:href
)
let $mbdatasetdoc := (
	if($mbdatasetlocation) then doc(concat($base,$mbdatasetlocation))
	else ()
)
(: get the OID of MBTESTCD and of MBRESCAT :)
let $mbtestcdoid := (
    for $a in $definedoc//odm:ItemDef[@Name='MBTESTCD']/@OID 
        where $a = $mbitemgroupdef/odm:ItemRef/@ItemOID
        return $a
)
let $mbrescatoid := (
    for $a in $definedoc//odm:ItemDef[@Name='MBRESCAT']/@OID 
        where $a = $mbitemgroupdef/odm:ItemRef/@ItemOID
        return $a
)
(: iterate over all records for which MBTESTCD='ORGANISM' :)
for $record in $mbdatasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$mbtestcdoid and @Value='ORGANISM']]
    let $recnum := $record/@data:ItemGroupDataSeq
    (: get the value of MBRESCAT (if any) :)
    let $mbrescat := $record/odm:ItemData[@ItemOID=$mbrescatoid]/@Value
    (: When MBTESTCD = 'ORGANISM' then MBRESCAT != null :)
    where not($mbrescat)  (: meaning MBRESCAT=absent/null :)
    return <error rule="SD1263" dataset="MB" variable="MBRESCAT" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">MBTESTCD='ORGANISM' but MBRESCAT=null</error>	
    ]]> 
	</rulexquery>
</sdsrule>	
    
<sdsrule originator="FDA" id="SD1264" standard="SDTM" last-update="2020-08-08">
	<ruledescription>When IDVARVAL = null and USUBJID = null then the value of IDVAR  may not be '--SEQ'</ruledescription>
	<ruledetaileddescription>When the RELREC dataset is used to identify relationships between datasets (in which case USUBJID and IDVARVAL are left null), instead of relating individual records, --SEQ cannot be used for IDVAR because --SEQ only has meaning within a subject within a dataset, not across datasets</ruledetaileddescription>
	<igversion>3.1.2</igversion>
	<igversion>3.1.3</igversion>
	<igversion>3.2</igversion>
	<domain>RELREC</domain>
	<synonym context="CDISC">CG0201</synonym>
	<rulexquery><![CDATA[
(: Rule SD1264 - RELREC - When IDVARVAL = null and USUBJID = null then IDVAR  != --SEQ :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdiscpilot01/'  :)
(: let $define := 'define_2_0.xml' :)
let $definedoc := doc(concat($base,$define))
(: get the RELREC dataset definition :)
let $relrecitemgroupdef := $definedoc//odm:ItemGroupDef[@Name='RELREC']
(: get the dataset location :)
let $relrecdatasetlocation := (
	if($defineversion = '2.1') then $relrecitemgroupdef/def21:leaf/@xlink:href
	else $relrecitemgroupdef/def:leaf/@xlink:href
)
let $relrecdatasetdoc := (
	if($relrecdatasetlocation) then doc(concat($base,$relrecdatasetlocation))
	else ()
)
(: get the OID of USUBJID, IDVAR and IDVARVAL :)
let $usubjidoid := (
    for $a in $definedoc//odm:ItemDef[@Name='USUBJID']/@OID 
        where $a = $relrecitemgroupdef/odm:ItemRef/@ItemOID
        return $a
)
let $idvaroid := (
    for $a in $definedoc//odm:ItemDef[@Name='IDVAR']/@OID 
        where $a = $relrecitemgroupdef/odm:ItemRef/@ItemOID
        return $a
)
let $idvarvaloid := (
    for $a in $definedoc//odm:ItemDef[@Name='IDVARVAL']/@OID 
        where $a = $relrecitemgroupdef/odm:ItemRef/@ItemOID
        return $a
)
(: iterate over all the records in the dataset for which IDVARVAL = null and USUBJID = null :)
for $record in $relrecdatasetdoc//odm:ItemGroupData[not(odm:ItemData[@ItemOID=$idvarvaloid]) and not(odm:ItemData[@ItemOID=$usubjidoid])]
    let $recnum := $record/@data:ItemGroupDataSeq
    (: get the value of IDVAR :)
    let $idvar := $record/odm:ItemData[@ItemOID=$idvaroid]/@Value
    (: IDVAR is not allowed to be --SEQ :)
    where not(ends-with($idvar,'SEQ')) and string-length($idvar)=5
    return <error rule="SD1264" dataset="RELREC" variable="IDVAR" recordnumber="{data($recnum)}" rulelastupdate="2020-08-08" >IDVAR={data($idvar)} is not allowed as USUBJID=null and IDVARVAL=null</error>			
		]]>
	</rulexquery>
</sdsrule>

<sdsrule id="SD1066" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>IDVARVAL value may not be populated, when RELTYPE values is populated</ruledescription>
<ruledetaileddescription>Variable Value (IDVARVAL) variable value should not be populated, when Relationship Type (RELTYPE) variable value is populated</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>RELREC</domain>
<rulexquery><![CDATA[	
(: Rule SD1066 - IDVARVAL value is populated, when RELTYPE values is populated:
Variable Value (IDVARVAL) variable value should not be populated, when Relationship Type (RELTYPE) variable value is populated
:)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/'  :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: Get the location of the RELREC dataset :)
let $relrecdatasetname := (
	if($defineversion = '2.1') then $definedoc//odm:ItemGroupDef[@Name='RELREC']/def21:leaf/@xlink:href
	else $definedoc//odm:ItemGroupDef[@Name='RELREC']/def:leaf/@xlink:href
)
let $relrecdatasetdoc := (
	if($relrecdatasetname) then doc(concat($base,$relrecdatasetname))
	else ()
)
(: and get the OIDs of the IDVARVAL and RELTYPE variables :)
let $idvarvaloid := (
    for $a in $definedoc//odm:ItemDef[@Name='IDVARVAL']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name='RELREC']/odm:ItemRef/@ItemOID
        return $a 
)
let $reltypeoid := (
    for $a in $definedoc//odm:ItemDef[@Name='RELTYPE']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name='RELREC']/odm:ItemRef/@ItemOID
        return $a     
)
(: iterate over all the records in the dataset :)
for $record in $relrecdatasetdoc//odm:ItemGroupData
    let $recnum := $record/@data:ItemGroupDataSeq
    (: and get the IDVARVAL and RELTYPE data points :)
    let $idvarvalvalue := $record/odm:ItemData[@ItemOID=$idvarvaloid]/@Value
    let $reltypevalue := $record/odm:ItemData[@ItemOID=$reltypeoid]/@Value
    (: Variable Value (IDVARVAL) variable value should not be populated, when Relationship Type (RELTYPE) variable value is populated :)
    where $reltypevalue and $idvarvalvalue
    return <warning rule="SD1066" dataset="RELREC" variable="IDVARVAL" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">IDVARVAL value is populated (value={data($idvarvalvalue)}, when RELTYPE values is populated (value={data($reltypevalue)})</warning>
]]></rulexquery>
</sdsrule>

<sdsrule id="SD1067" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>USUBJID value may not be populated, when RELTYPE values is populated</ruledescription>
<ruledetaileddescription>Unique Subject Identifier (USUBJID) variable value should not be populated, when Relationship Type (RELTYPE) variable value is populated in RELREC</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>RELREC</domain>
<rulexquery><![CDATA[
(: Rule SD1067 - USUBJID value is populated, when RELTYPE values is populated:
Unique Subject Identifier (USUBJID) variable value should not be populated, when Relationship Type (RELTYPE) variable value is populated in RELREC
:)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: Get the location of the RELREC dataset :)
let $relrecdatasetname := (
	if($defineversion = '2.1') then $definedoc//odm:ItemGroupDef[@Name='RELREC']/def21:leaf/@xlink:href
	else $definedoc//odm:ItemGroupDef[@Name='RELREC']/def:leaf/@xlink:href
)
let $relrecdatasetdoc := (
	if($relrecdatasetname) then doc(concat($base,$relrecdatasetname))
	else ()
)
(: Get the OID of the RELTYPE and USUBJID variable :)
let $reltypeoid := (
    for $a in $definedoc//odm:ItemDef[@Name='RELTYPE']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name='RELREC']/odm:ItemRef/@ItemOID
        return $a 
)
let $usubjidoid := (
    for $a in $definedoc//odm:ItemDef[@Name='USUBJID']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name='RELREC']/odm:ItemRef/@ItemOID
        return $a 
)
(: iterate over all records in the dataset :)
for $record in $relrecdatasetdoc//odm:ItemGroupData
    let $recnum := $record/@data:ItemGroupDataSeq
    (: and get the values of RELTYPE and USUJID (when present) :)
    let $reltypevalue := $record/odm:ItemData[@ItemOID=$reltypeoid]/@Value
    let $usubjidvalue := $record/odm:ItemData[@ItemOID=$usubjidoid]/@Value
    (: Unique Subject Identifier (USUBJID) variable value should not be populated, 
    when Relationship Type (RELTYPE) variable value is populated in RELREC :)
    where $reltypevalue and $usubjidvalue
    return <warning rule="SD1067" dataset="RELREC" variable="USUBJID" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">USUBJID value is populated (value={data($usubjidvalue)}), when RELTYPE values is populated (value={data($reltypevalue)}) in dataset {data($relrecdatasetname)}</warning>
]]></rulexquery>
</sdsrule>

<sdsrule originator="FDA" id="SD1265" standard="SDTM" last-update="2020-08-08">
	<ruledescription>When QORIG = 'ASSIGNED' then QEVAL may not be null</ruledescription>
	<ruledetaileddescription>For subjective data (when QORIG='ASSIGNED'), QEVAL should be populated and the value should reflect the role of the person or institution assigning the value</ruledetaileddescription>
	<igversion>3.1.2</igversion>
	<igversion>3.1.3</igversion>
	<igversion>3.2</igversion>
	<domain>SUPPQUAL</domain>
	<synonym context="CDISC">CG0202</synonym>
	<rulexquery><![CDATA[
(: Rule SD1265 - SUPPxx - When QORIG = 'ASSIGNED' then QEVAL != null :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdiscpilot01/'  :)
(: let $define := 'define_2_0.xml' :)
let $definedoc := doc(concat($base,$define))
(: iterate over all the SUPPxx dataset definitions :)
for $suppitemgroupdef in $definedoc//odm:ItemGroupDef[starts-with(@Name,'SUPP')]
    let $name := $suppitemgroupdef/@Name
    (: get the dataset location :)
    let $suppdatasetlocation := (
		if($defineversion = '2.1') then $suppitemgroupdef/def21:leaf/@xlink:href
		else $suppitemgroupdef/def:leaf/@xlink:href
	)
    let $suppdatasetdoc := (
		if($suppdatasetlocation) then doc(concat($base,$suppdatasetlocation))
		else ()
	)
    (: we need the OID if QORIG and of QEVAL :)
    let $qorigoid := (
        for $a in $definedoc//odm:ItemDef[@Name='QORIG']/@OID 
        where $a = $suppitemgroupdef/odm:ItemRef/@ItemOID
        return $a
    )
    let $qevaloid := (
        for $a in $definedoc//odm:ItemDef[@Name='QEVAL']/@OID 
        where $a = $suppitemgroupdef/odm:ItemRef/@ItemOID
        return $a
    )
    (: iterate over all records for which QORIG = 'ASSIGNED' :)
    for $record in $suppdatasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$qorigoid and @Value='ASSIGNED']]
        let $recnum := $record/@data:ItemGroupDataSeq
        (: get the value of QEVAL (if any) :)
        let $qeval := $record/odm:ItemData[@ItemOID=$qevaloid]/@Value
        (: QEVAL must != null :)
        where not($qeval)  (: QEVAL is null :)
        return <error rule="SD1265" dataset="{data($name)}" variable="QEVAL" recordnumber="{data($recnum)}" rulelastupdate="2020-08-08" >QEVAL may not be null when QORIG='ASSIGNED'</error>			
		]]>
	</rulexquery>
</sdsrule>

<sdsrule id="SD1051" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>IDVAR and IDVARVAL may not populated for DM supplemental qualifiers</ruledescription>
<ruledetaileddescription>Both Identifying Variable (IDVAR) and Identifying Variable Value (IDVARVAL) should not be populated, when relating information to the Demographics (DM) domain</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>SUPPQUAL</domain>
<rulexquery><![CDATA[
(: Rule SD1051 - Unexpected value for IDVAR/IDVARVAL: 
Both Identifying Variable (IDVAR) and Identifying Variable Value (IDVARVAL) should not be populated, when relating information to the Demographics (DM) domain 
in SUPPxx datasets
:)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: iterate over all SUPPxx datasets :)
for $dataset in $definedoc//odm:ItemGroupDef[starts-with(@Name,'SUPP')]
    let $name := $dataset/@Name
    let $datasetname := (
		if($defineversion = '2.1') then $dataset/def21:leaf/@xlink:href
		else $dataset/def:leaf/@xlink:href
	)
    let $datasetdoc := (
		if($datasetname) then doc(concat($base,$datasetname))
		else ()
	)
    (: Get the OIDs of the IDVAR and IDVARVAL and RDOMAIN variables :)
    let $idvaroid := (
        for $a in $definedoc//odm:ItemDef[@Name='IDVAR']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a 
    )
    let $idvarvaloid := (
        for $a in $definedoc//odm:ItemDef[@Name='IDVARVAL']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a 
    )
    let $rdomainoid := (
        for $a in $definedoc//odm:ItemDef[@Name='IDVARVAL']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a 
    )
    (: iterate over all records in the dataset :)
    for $record in $datasetdoc//odm:ItemGroupData
        let $recnum := $record/@data:ItemGroupDataSeq
        (: and get the values for the IDVAR, IDVARVAL and RDOMAIN variables :)
        let $idvarvalue := $record/odm:ItemData[@ItemOID=$idvaroid]/@Value
        let $idvarvalvalue := $record/odm:ItemData[@ItemOID=$idvarvaloid]/@Value
        let $rdomainvalue := $record/odm:ItemData[@ItemOID=$rdomainoid]/@Value
        (: When RDOMAIN=DM IDVAR and IDVARVAL may not have a value :)
        where $rdomainvalue='DM' and ($idvarvalue or $idvarvalvalue)
        return <warning rule="SD1051" variable="IDVAR" dataset="{data($name)}" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">Unexpected value for IDVAR/IDVARVAL: IDVAR and/or IDVARVAL are populated although RDOMAIN=DM in dataset {data($datasetname)}</warning>
]]></rulexquery>
</sdsrule>

<!-- Rule SD0095: Invalid usage of SUPPQUAL for non-general-observation-class Domain.
NOT implemented as it is completely unclear what the rule is.  -->


<sdsrule id="SD1072" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>IDVAR value must be provided, when RDOMAIN value is provided</ruledescription>
<ruledetaileddescription>Value of Identifying Variable (IDVAR) variable must be populated, when Related Domain Abbreviation (RDOMAIN) variable value is provided, with the only exception of 'DM' value for RDOMAIN</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>RELREC</domain>
<domain>SUPPQUAL</domain>
<rulexquery><![CDATA[	
(: Rule SD1072 - Missing IDVAR value, when RDOMAIN value is provided:
Value of Identifying Variable (IDVAR) variable must be populated, when Related Domain Abbreviation (RDOMAIN) variable value is provided, with the only exception of 'DM' value for RDOMAIN
SUPPQUAL, RELREC
:)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: iterate over the RELREC and all SUPP-- datasets :)
for $dataset in $definedoc//odm:ItemGroupDef[@Name='RELREC' or starts-with(@Name,'SUPP')]
    let $name := $dataset/@Name
    let $datasetname := (
		if($defineversion = '2.1') then $dataset/def21:leaf/@xlink:href
		else $dataset/def:leaf/@xlink:href
	)
    let $datasetdoc := (	
		if($datasetname) then doc(concat($base,$datasetname))
		else ()
	)
    (: get the OID of the IDVAR and RDOMAIN variables :)
    let $idvaroid := (
        for $a in $definedoc//odm:ItemDef[@Name='IDVAR']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
    )
    let $rdomainoid := (
        for $a in $definedoc//odm:ItemDef[@Name='RDOMAIN']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
    )
    (: iterate over all records that have RDOMAIN populated :)
    for $record in $datasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$rdomainoid]]
        let $recnum := $record/@data:ItemGroupDataSeq
        (: and get the value of RDOMAIN and IDVAR (if any) :)
        let $idvarvalue := $record/odm:ItemData[@ItemOID=$idvaroid]/@Value
        let $rdomainvalue := $record/odm:ItemData[@ItemOID=$rdomainoid]/@Value
        (: IDVAR must be populated, unless RDOMAIN=DM :)
        where not($rdomainvalue='DM') and not($idvarvalue)
        return <error rule="SD1072" dataset="{data($name)}" variable="IDVAR" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">Missing IDVAR value, when RDOMAIN value is provided, RDOMAIN={data($rdomainvalue)}</error>	
]]></rulexquery>
</sdsrule>


<sdsrule id="SD1013" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>TAETORD may not be populated, when ETCD = 'UNPLAN'</ruledescription>
<ruledetaileddescription>Planned Order of Elements within Arm (TAETORD) should be NULL, when subject's experience for a particular period of time is represented as an unplanned Element (ETCD = 'UNPLAN')</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>SE</domain>
<rulexquery><![CDATA[
(: Rule SD1013 - TAETORD is populated, ETCD = 'UNPLAN':
Planned Order of Elements within Arm (TAETORD) should be NULL, when subject's experience for a particular period of time is represented as an unplanned Element (ETCD = 'UNPLAN')
SE dataset :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: get the SE dataset :)
let $sedataset := $definedoc//odm:ItemGroupDef[@Name='SE']
let $sedatasetname := (
	if($defineversion = '2.1') then $sedataset/def21:leaf/@xlink:href
	else $sedataset/def:leaf/@xlink:href
)
let $sedatasetdoc := (
	if($sedatasetname) then doc(concat($base,$sedatasetname))
	else ()
)
(: and get the OID of the TAETORD and ETCD variables :)
let $taetordoid := (
    for $a in $definedoc//odm:ItemDef[@Name='TAETORD']/@OID 
    where $a = $definedoc//odm:ItemGroupDef[@Name='SE']/odm:ItemRef/@ItemOID
    return $a    
)
let $etcdoid := (
    for $a in $definedoc//odm:ItemDef[@Name='ETCD']/@OID 
    where $a = $definedoc//odm:ItemGroupDef[@Name='SE']/odm:ItemRef/@ItemOID
    return $a
)
(: iterate over all records in the SE dataset that have ETCD=UNPLAN :)
for $record in $sedatasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$etcdoid and @Value='UNPLAN']]
    let $recnum := $record/@data:ItemGroupDataSeq
    (: and get the value of TAETORD - if any :)
    let $taetordvalue := $record/odm:ItemData[@ItemOID=$taetordoid]/@Value
    (: TAETORD must be null :)
    where string-length($taetordvalue) > 0
    return <warning rule="SD1013" dataset="SE" variable="TAETORD" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">TAETORD is populated, value={data($taetordvalue)} with ETCD='UNPLAN' in dataset SE</warning>
]]></rulexquery>
</sdsrule>



<sdsrule originator="FDA" id="SD1266" standard="SDTM" last-update="2020-08-08">
	<ruledescription>When SEUPDES = null then ETCD may not be 'UNPLAN'</ruledescription>
	<igversion>3.1.2</igversion>
	<igversion>3.1.3</igversion>
	<igversion>3.2</igversion>
	<domain>SE</domain>
	<synonym context="CDISC">CG0210</synonym>
	<rulexquery><![CDATA[
(: Rule SD1266 - SE - When SEUPDES = null then ETCD != 'UNPLAN'
 REMARK that SEUPDES is permissible :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdiscpilot01/'  :)
(: let $define := 'define_2_0.xml' :)
let $definedoc := doc(concat($base,$define))
(: get the SE dataset definition :)
let $seitemgroupdef := $definedoc//odm:ItemGroupDef[@Name='SE']
(: and the dataset location :)
let $sedatasetlocation := (
	if($defineversion = '2.1') then $seitemgroupdef/def21:leaf/@xlink:href
	else $seitemgroupdef/def:leaf/@xlink:href
)
let $sedatasetdoc := (
	if($sedatasetlocation) then doc(concat($base,$sedatasetlocation))
	else ()
)
(: we need the OID of ETCD and of SEUPDES :)
let $etcdoid := (
    for $a in $definedoc//odm:ItemDef[@Name='ETCD']/@OID 
        where $a = $seitemgroupdef/odm:ItemRef/@ItemOID
        return $a
)
let $seupdesoid := (
    for $a in $definedoc//odm:ItemDef[@Name='SEUPDES']/@OID 
        where $a = $seitemgroupdef/odm:ItemRef/@ItemOID
        return $a
)
(: iterate over all records where SEUPDES is null (i.e. absent as ItemData) :)
for $record in $sedatasetdoc//odm:ItemGroupData[not(odm:ItemData[@ItemOID=$seupdesoid])]
    let $recnum := $record/@data:ItemGroupDataSeq
    (: get the value of ETCD :)
    let $etcd := $record/odm:ItemData[@ItemOID=$etcdoid]/@Value
    (: ETCD must be != 'UNPLAN' :)
    where $etcd='UNPLAN'
    return <error rule="SD1266" dataset="SE" variable="ETCD" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">ETCD='UNPLAN' for SEUPDES=null</error>			
		]]>
	</rulexquery>
</sdsrule>

<sdsrule originator="FDA" id="SD0092" standard="SDTM" last-update="2020-08-08">
	<ruledescription>When ETCD='UNPLAN', SEUPDES may not be null</ruledescription>
	<igversion>3.1.2</igversion>
	<igversion>3.1.3</igversion>
	<igversion>3.2</igversion>
	<domain>SE</domain>
	<synonym context="CDISC">CG0211</synonym>
	<rulexquery><![CDATA[
(: Rule SD0092 - SE - When ETCD='UNPLAN', SEUPDES may not be null
 REMARK that SEUPDES is permissible :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdiscpilot01/'  :)
(: let $define := 'define_2_0.xml' :)
let $definedoc := doc(concat($base,$define))
(: get the SE dataset definition :)
let $seitemgroupdef := $definedoc//odm:ItemGroupDef[@Name='SE']
(: and the dataset location :)
let $sedatasetlocation := (
	if($defineversion = '2.1') then $seitemgroupdef/def21:leaf/@xlink:href
	else $seitemgroupdef/def:leaf/@xlink:href
)
let $sedatasetdoc := (
	if($sedatasetlocation) then doc(concat($base,$sedatasetlocation))
	else ()
)
(: we need the OID of ETCD and of SEUPDES :)
let $etcdoid := (
    for $a in $definedoc//odm:ItemDef[@Name='ETCD']/@OID 
        where $a = $seitemgroupdef/odm:ItemRef/@ItemOID
        return $a
)
let $seupdesoid := (
    for $a in $definedoc//odm:ItemDef[@Name='SEUPDES']/@OID 
        where $a = $seitemgroupdef/odm:ItemRef/@ItemOID
        return $a
)
(: iterate over all records where ETCD is 'UNPLAN' :)
for $record in $sedatasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$etcdoid]/@Value='UNPLAN']
    let $recnum := $record/@data:ItemGroupDataSeq
    (: get the value of SEUPDES, if any :)
    let $seupdes := $record/odm:ItemData[@ItemOID=$seupdesoid]/@Value
    (: SEUPDES must may not be null :)
    where not($seupdes)
    return <error rule="SD0092" dataset="SE" variable="ETCD" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">Missing value for SEUPDES, when ETCD='UNPLAN'</error>			
		]]>
	</rulexquery>
</sdsrule>


<sdsrule id="SD1017" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>VISITNUM value must match TV domain data</ruledescription>
<ruledetaileddescription>Visit Number (VISITNUM) values should match entries in the Trial Visits (TV) dataset, when they are planned visits (SVUPDES = NULL)</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>SV</domain>
<rulexquery><![CDATA[	
(: Rule SD1017 - VISITNUM value does not match TV domain data:
Visit Number (VISITNUM) values should match entries in the Trial Visits (TV) dataset, when they are planned visits (SVUPDES = NULL)
SV dataset
:)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
declare namespace xs="http://www.w3.org/2001/XMLSchema";
declare namespace functx = "http://www.functx.com";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
declare function functx:is-value-in-sequence
  ($value as xs:anyAtomicType? ,
    $seq as xs:anyAtomicType* )  as xs:boolean {
   $value = $seq
} ;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define)) 
(: get the TV dataset :)
let $tvdataset := $definedoc//odm:ItemGroupDef[@Name='TV']
let $tvdatasetname := (
	if($defineversion = '2.1') then $tvdataset/def21:leaf/@xlink:href
	else $tvdataset/def:leaf/@xlink:href
)
let $tvdatasetlocation := concat($base,$tvdatasetname)
(: and the OID of VISITNUM in TV :)
let $tvvisitnumoid := (
    for $a in $definedoc//odm:ItemDef[@Name='VISITNUM']/@OID 
    where $a = $definedoc//odm:ItemGroupDef[@Name='TV']/odm:ItemRef/@ItemOID
    return $a 
)
(: get all VISITNUM values as a sequence from the TV dataset :)
let $visitnumsequence := distinct-values(
    for $itemdata in doc($tvdatasetlocation)//odm:ItemGroupData/odm:ItemData[@ItemOID=$tvvisitnumoid]
    return $itemdata/@Value
)
(: get the SV dataset :)
let $svdataset := $definedoc//odm:ItemGroupDef[@Name='SV']
let $svdatasetname := (
	if($defineversion = '2.1') then $svdataset/def21:leaf/@xlink:href
	else $svdataset/def:leaf/@xlink:href
)
let $svdatasetdoc := (
	if($svdatasetname) then doc(concat($base,$svdatasetname))
	else ()
)
(: and get the OID of the VISITNUM and SVUPDES in SV :)
let $svvisitnumoid := (
    for $a in $definedoc//odm:ItemDef[@Name='VISITNUM']/@OID 
    where $a = $definedoc//odm:ItemGroupDef[@Name='SV']/odm:ItemRef/@ItemOID
    return $a 
)
let $svupdesoid := (
    for $a in $definedoc//odm:ItemDef[@Name='SVUPDES']/@OID 
    where $a = $definedoc//odm:ItemGroupDef[@Name='SV']/odm:ItemRef/@ItemOID
    return $a 
)
(: iterate over all records in SV where there is a value for VISITNUM and SVUPDES is null :)
for $record in $svdatasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$svvisitnumoid] and not(odm:ItemData[@ItemOID=$svupdesoid])]
    let $recnum := $record/@data:ItemGroupDataSeq
    (: get the value of VISITNUM :)
    let $svvisitnumvalue := $record/odm:ItemData[@ItemOID=$svvisitnumoid]/@Value
    (: and check whether in the list of VISITNUMs in TV :)
    where not(functx:is-value-in-sequence($svvisitnumvalue,$visitnumsequence))
    return <warning rule="SD1017" dataset="SV" variable="VISITNUM" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">VISITNUM value={data($svvisitnumvalue)} in SV does not match TV domain data</warning>	
]]></rulexquery>
</sdsrule>

<sdsrule id="SD1019" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>VISITDY may not be populated for an unplanned visit</ruledescription>
<ruledetaileddescription>Planned Study Day of Visit (VISITDY) should equal NULL for unplanned visits, where Description of Unplanned Visit (SVUPDES) is populated</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>SV</domain>
<rulexquery><![CDATA[
(: Rule SD1019 - VISITDY is populated for unplanned visit:
Planned Study Day of Visit (VISITDY) should equal NULL for unplanned visits, where Description of Unplanned Visit (SVUPDES) is populated
SV dataset
:)
(: REMARK that VISITDY is permissible! :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: get the SV dataset :)
let $svdataset := $definedoc//odm:ItemGroupDef[@Name='SV']
let $svdatasetname := (
	if($defineversion = '2.1') then $svdataset/def21:leaf/@xlink:href
	else $svdataset/def:leaf/@xlink:href
)
let $svdatasetdoc := (
	if($svdatasetname) then doc(concat($base,$svdatasetname))
	else ()
)
(: and get the OID of the VISITDY and SVUPDES variables :)
let $visitdyoid := (
    for $a in $definedoc//odm:ItemDef[@Name='VISITDY']/@OID 
    where $a = $definedoc//odm:ItemGroupDef[@Name='SV']/odm:ItemRef/@ItemOID
    return $a  
)
let $svsupdesoid := (
    for $a in $definedoc//odm:ItemDef[@Name='SVUPDES']/@OID 
    where $a = $definedoc//odm:ItemGroupDef[@Name='SV']/odm:ItemRef/@ItemOID
    return $a 
)
(: iterate over all records in the SV dataset that have SVUPDES populated, 
but only when SV-VISITDY has been defined - it is "permissible"! :)
for $record in $svdatasetdoc[$visitdyoid]//odm:ItemGroupData[odm:ItemData[@ItemOID=$svsupdesoid]]
    let $recnum := $record/@data:ItemGroupDataSeq
    (: and get the value of VISITDY - if any :)
    let $visitdyvalue := $record/odm:ItemData[@ItemOID=$visitdyoid]/@Value
    (: VISITDY must be null :)
    return <warning rule="SD1019" dataset="SV" variable="VISITDY" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">VISITDY is populated for unplanned visit, value for VISITDY={data($visitdyvalue)} in dataset SE</warning>	
]]></rulexquery>
</sdsrule>

<!-- TODO: use CDISC-Library, as the set of timing variables depends on the IG/model version -->
<sdsrule originator="FDA" id="SD1299" standard="SDTM" last-update="2020-08-08">
	<ruledescription>At least one timing variable must be present in the dataset</ruledescription>
	<ruledetaileddescription>All domains based on the general observation classes (Interventions, Events, Findings) should have at least one Timing variable present, except for: IE, SC</ruledetaileddescription>
	<igversion>3.1.2</igversion>
	<igversion>3.1.3</igversion>
	<igversion>3.2</igversion>
	<domain>INTERVENTIONS</domain>
	<domain>FINDINGS</domain>
	<domain>EVENTS</domain>
	<synonym context="CDISC">CG0219</synonym>
	<rulexquery><![CDATA[
(: Rule CG0220 - INTERVENTIONS, EVENTS, FINDINGS - At least one timing variable present in dataset :)
(: TODO: implement CDISC Library and different SDTMIG versions :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
declare namespace functx = "http://www.functx.com";
(: function to find out whether a value is in a sequence (array) :)
declare function functx:is-value-in-sequence
  ( $value as xs:anyAtomicType? ,
    $seq as xs:anyAtomicType* )  as xs:boolean {
   $value = $seq
} ;
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdiscpilot01/'  :)
(: let $define := 'define_2_0.xml' :)
let $definedoc := doc(concat($base,$define))
(: This is the list of timing variables for SDTM 1.4. 
Dashes will later be replaced by the domain name :)
let $timingvars := ('VISITNUM','VISIT','VISITDY','TAETORD','EPOCH','--DTC','--STDTC','--ENDTC','--DY','--STDY','--ENDY', 
'--DUR','--TPT','--TPTNUM','--ELTM','--TPTREF','--RFTDTC','--STRF','--ENRF','--EVLINT','--EVINTX','--STRTPT','--STTPT',
'--ENRTPT','--ENTPT','--STINT','--ENINT','--DETECT')
(: iterate over all INTERVENTIONS, EVENTS and FINDINGS dataset definitions, except for IE and SC :)
for $itemgroupdef in $definedoc//odm:ItemGroupDef[upper-case(@def:Class)='INTERVENTIONS' or upper-case(@def:Class)='EVENTS' or upper-case(@def:Class)='FINDINGS' 
		or upper-case(./def21:Class/@Name)='INTERVENTIONS' or upper-case(./def21:Class/@Name)='EVENTS' or upper-case(./def21:Class/@Name)='FINDINGS'][not(@Name='SC' or @Name='IE')]
    (: get the domain name and dataset name  :)
    let $name := $itemgroupdef/@Name
    let $domain := $itemgroupdef/@Domain
    (: generate the prefix - take either the domain name (preferred), 
    or the first 2 characters of the dataset name in case the domain name is missing :)
    let $prefix := (
        if($domain) then $domain
        else substring($name,1,2)
    )
    (: in the set of timing variables, replace the -- by the prefix :)
    let $timingvarsdomain := (
        for $s in $timingvars
            let $newtimingvar := (
                if(starts-with($s,'--')) then concat($prefix,substring($s,3))
                else $s
            )
            return $newtimingvar
    )
    (: return <test>{data($timingvarsdomain)}</test> :)
    (: create a list of the variables for this dataset :)
    let $datasetvars := (
        for $a in $itemgroupdef/odm:ItemRef/@ItemOID
        return $definedoc//odm:ItemDef[@OID=$a]/@Name
    )
    (: return <test>{data($datasetvars)}</test> :)
    (: count the number of variables in the intersection of allowed timing variables and the variables in this dataset :)
    let $intersection := distinct-values($timingvarsdomain[.=$datasetvars])
    (: return <test>{data($intersection)}</test> :)
    let $count := count($intersection)
    (: if the count of items in the intersection = 0, there is no timing variable in the dataset :)
    where $count=0
    return <error rule="SD1299" dataset="{data($name)}" rulelastupdate="2020-08-08">No timing variables have been defined in dataset {data($name)}</error>			
	]]></rulexquery>
</sdsrule>


<sdsrule id="SD1088" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>--STDY variable value must be populated when --STDTC and DM.RFSTDTC are provided</ruledescription>
<ruledetaileddescription>Study Day of Start (--STDY) variable value should be populated, when Start Study Date (--STDTC) and Subject Reference Start Date/Time (RFSTDTC) variables values are provided, and both of them include complete date part</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>INTERVENTIONS</domain>
<domain>EVENTS</domain>
<domain>FINDINGS</domain>
<domain>SE</domain>
<domain>SV</domain>
<rulexquery><![CDATA[
(: Rule SD1088 - --STDY variable value is not populated:
Study Day of Start (--STDY) variable value should be populated, when Start Study Date (--STDTC) and Subject Reference Start Date/Time (RFSTDTC) variables values are provided, and both of them include complete date part.
P.S. When variable --STDY not present at all, do not throw an error, is already covered by rule FDAC0128
:)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/'  :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define)) 
(: get the location of the DM dataset :)
let $dmdatasetname := (
	if($defineversion = '2.1') then $definedoc//odm:ItemGroupDef[@Name='DM']/def21:leaf/@xlink:href
	else $definedoc//odm:ItemGroupDef[@Name='DM']/def:leaf/@xlink:href
)
let $dmdatasetdoc := doc(concat($base,$dmdatasetname))
(: and the OIDs of the USUBJID and RFSTDTC variables :)
let $dmusubjidoid := (
    for $a in $definedoc//odm:ItemDef[@Name='USUBJID']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name='DM']/odm:ItemRef/@ItemOID
        return $a 
)
let $rfstdtcoid := (
    for $a in $definedoc//odm:ItemDef[@Name='RFSTDTC']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name='DM']/odm:ItemRef/@ItemOID
        return $a 
)
(: iterate over all datasets within INTERVENTIONS, EVENTS, FINDINGS, SE, SV :)
for $dataset in $definedoc//odm:ItemGroupDef[upper-case(@def:Class)='INTERVENTIONS' or upper-case(@def:Class)='EVENTS' or upper-case(@def:Class)='FINDINGS' 
		or upper-case(./def21:Class/@Name)='INTERVENTIONS' or upper-case(./def21:Class/@Name)='EVENTS' or upper-case(./def21:Class/@Name)='FINDINGS' or @Domain='SE']
    let $name := $dataset/@Name
    let $datasetname := (
		if($defineversion = '2.1') then $dataset/def21:leaf/@xlink:href
		else $dataset/def:leaf/@xlink:href
	)
    let $datasetdoc := (
		if($datasetname) then doc(concat($base,$datasetname))
		else ()
	)
    (: Get the OIDs of the SDTY and STDTC variable :)
    let $stdyoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'STDY')]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a  
    )
    let $stdyname := $definedoc//odm:ItemDef[@OID=$stdyoid]/@Name
    let $stdtcoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'STDTC')]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a  
    )
    let $stdtcname := $definedoc//odm:ItemDef[@OID=$stdtcoid]/@Name
    (: and the OID of USUBJID in this dataset (can be different from in DM) :)
    let $usubjidoid := (
        for $a in $definedoc//odm:ItemDef[@Name='USUBJID']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
    )
    (: iterate over all records in the dataset that DO have both a STDCT value  :)
    for $record in $datasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$stdtcoid]]
        let $recnum := $record/@data:ItemGroupDataSeq
        let $stdtcvalue := $record/odm:ItemData[@ItemOID=$stdtcoid]/@Value
        (: check whether it is a complete date :)
        let $iscompletestdtcdate := string-length($stdtcvalue) >= 10  (: boolean :)
        (: get the USUBJID and get the corresponding RFSTDTC in the DM dataset :)
        let $usubjidvalue := $record/odm:ItemData[@ItemOID=$usubjidoid]/@Value
        (: keeping the worst case scenario into account that there is more than 1 record for a USUBJID in DM - take the first :)
        let $rfstdtcvalue := $dmdatasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$dmusubjidoid and @Value=$usubjidvalue]][1]/odm:ItemData[@ItemOID=$rfstdtcoid]/@Value
        (: and check whether RFSTDTC is a complete date :)
        let $iscompleterfstdtcdate := string-length($rfstdtcvalue) >= 10
        (: Get the value of the STDY variable (if any) :)
        let $stdyvalue := $record/odm:ItemData[@ItemOID=$stdyoid]/@Value
        (: STDY MUST be populated when the variable is in the dataset and when both RFSTDTC AND STDTC are complete dates :)
        where $stdyoid and $iscompletestdtcdate and $iscompleterfstdtcdate and not($stdyvalue)
        return <error rule="SD1088" dataset="{data($name)}" variable="{data($stdyname)}" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">{data($stdyname)} variable is not populated although both {data($stdtcname)} (value={data($stdtcvalue)}) and RFSTDTC (value={data($rfstdtcvalue)}) are complete dates</error>	
]]></rulexquery>
</sdsrule>

<sdsrule id="SD1088-SD" last-update="2020-08-08" originator="FDA" standard="SDTM" requiresDomainOrDataset="Yes">
<ruledescription>--STDY variable value must be populated when --STDTC and DM.RFSTDTC are provided</ruledescription>
<ruledetaileddescription>Study Day of Start (--STDY) variable value should be populated, when Start Study Date (--STDTC) and Subject Reference Start Date/Time (RFSTDTC) variables values are provided, and both of them include complete date part</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>INTERVENTIONS</domain>
<domain>EVENTS</domain>
<domain>FINDINGS</domain>
<domain>SE</domain>
<domain>SV</domain>
<rulexquery><![CDATA[
(: Rule SD1088 - --STDY variable value is not populated:
Study Day of Start (--STDY) variable value should be populated, when Start Study Date (--STDTC) and Subject Reference Start Date/Time (RFSTDTC) variables values are provided, and both of them include complete date part.
P.S. When variable --STDY not present at all, do not throw an error, is already covered by rule FDAC0128
:)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
declare variable $datasetname external;
(: let $base := '/db/fda_submissions/cdisc01/'  :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define)) 
(: get the location of the DM dataset :)
let $dmdatasetname := (
	if($defineversion = '2.1') then $definedoc//odm:ItemGroupDef[@Name='DM']/def21:leaf/@xlink:href
	else $definedoc//odm:ItemGroupDef[@Name='DM']/def:leaf/@xlink:href
)
let $dmdatasetlocation := concat($base,$dmdatasetname)
(: and the OIDs of the USUBJID and RFSTDTC variables :)
let $dmusubjidoid := (
    for $a in $definedoc//odm:ItemDef[@Name='USUBJID']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name='DM']/odm:ItemRef/@ItemOID
        return $a 
)
let $rfstdtcoid := (
    for $a in $definedoc//odm:ItemDef[@Name='RFSTDTC']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name='DM']/odm:ItemRef/@ItemOID
        return $a 
)
(: iterate over all provided datasets within INTERVENTIONS, EVENTS, FINDINGS, SE, SV :)
for $dataset in $definedoc//odm:ItemGroupDef[@Name=$datasetname][upper-case(@def:Class)='INTERVENTIONS' or upper-case(@def:Class)='EVENTS' or upper-case(@def:Class)='FINDINGS' 
		or upper-case(./def21:Class/@Name)='INTERVENTIONS' or upper-case(./def21:Class/@Name)='EVENTS' or upper-case(./def21:Class/@Name)='FINDINGS' or @Domain='SE']
    let $name := $dataset/@Name
    let $dsname := (
		if($defineversion = '2.1') then $dataset/def21:leaf/@xlink:href
		else $dataset/def:leaf/@xlink:href
	)
    let $datasetdoc := (
		if($dsname) then doc(concat($base,$dsname))
		else ()
	)
    (: Get the OIDs of the SDTY and STDTC variable :)
    let $stdyoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'STDY')]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a  
    )
    let $stdyname := $definedoc//odm:ItemDef[@OID=$stdyoid]/@Name
    let $stdtcoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'STDTC')]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a  
    )
    let $stdtcname := $definedoc//odm:ItemDef[@OID=$stdtcoid]/@Name
    (: and the OID of USUBJID in this dataset (can be different from in DM) :)
    let $usubjidoid := (
        for $a in $definedoc//odm:ItemDef[@Name='USUBJID']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
    )
    (: iterate over all records in the dataset that DO have both a STDCT value  :)
    for $record in $datasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$stdtcoid]]
        let $recnum := $record/@data:ItemGroupDataSeq
        let $stdtcvalue := $record/odm:ItemData[@ItemOID=$stdtcoid]/@Value
        (: check whether it is a complete date :)
        let $iscompletestdtcdate := string-length($stdtcvalue) >= 10  (: boolean :)
        (: get the USUBJID and get the corresponding RFSTDTC in the DM dataset :)
        let $usubjidvalue := $record/odm:ItemData[@ItemOID=$usubjidoid]/@Value
        (: keeping the worst case scenario into account that there is more than 1 record for a USUBJID in DM - take the first :)
        let $rfstdtcvalue := doc($dmdatasetlocation)//odm:ItemGroupData[odm:ItemData[@ItemOID=$dmusubjidoid and @Value=$usubjidvalue]][1]/odm:ItemData[@ItemOID=$rfstdtcoid]/@Value
        (: and check whether RFSTDTC is a complete date :)
        let $iscompleterfstdtcdate := string-length($rfstdtcvalue) >= 10
        (: Get the value of the STDY variable (if any) :)
        let $stdyvalue := $record/odm:ItemData[@ItemOID=$stdyoid]/@Value
        (: STDY MUST be populated when the variable is in the dataset and when both RFSTDTC AND STDTC are complete dates :)
        where $stdyoid and $iscompletestdtcdate and $iscompleterfstdtcdate and not($stdyvalue)
        return <error rule="SD1088" dataset="{data($name)}" variable="{data($stdyname)}" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">{data($stdyname)} variable is not populated although both {data($stdtcname)} (value={data($stdtcvalue)}) and RFSTDTC (value={data($rfstdtcvalue)}) are complete dates</error>	
]]></rulexquery>
</sdsrule>


<sdsrule id="SD1090" last-update="2020-08-08" originator="FDA" standard="SDTM" timeintensive="Yes">
<ruledescription>--STDY must be correctly calculated</ruledescription>
<ruledetaileddescription>Study Day of Start (--STDY) variable value should be populated as defined by CDISC standards: --STDY = (date portion of --STDTC) - (date portion of RFSTDTC) +1 if --STDTC is on or after RFSTDTC; --STDY = (date portion of --STDTC) - (date portion of RFSTDTC) if --STDTC precedes RFSTDTC</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>INTERVENTIONS</domain>
<domain>EVENTS</domain>
<domain>FINDINGS</domain>
<domain>SE</domain>
<rulexquery><![CDATA[	
(: Rule SD1090 - Incorrect value for --STDY variable:
Study Day of Start (--STDY) variable value should be populated as defined by CDISC standards: --STDY = (date portion of --STDTC) - (date portion of RFSTDTC) +1 if --STDTC is on or after RFSTDTC; --STDY = (date portion of --STDTC) - (date portion of RFSTDTC) if --STDTC precedes RFSTDTC.
:)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: Get the location of the DM dataset :)
let $dmdataset := (
	if($defineversion = '2.1') then $definedoc//odm:ItemGroupDef[@Name='DM']/def21:leaf/@xlink:href
	else $definedoc//odm:ItemGroupDef[@Name='DM']/def:leaf/@xlink:href
)
let $dmdatasetlocation := concat($base,$dmdataset)
(: get the OID of the RFSTDTC variable :)
let $rfstdtcoid := (
    for $a in $definedoc//odm:ItemDef[@Name='RFSTDTC']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name='DM']/odm:ItemRef/@ItemOID
        return $a   
)
(: get the OID of the USUBJID variable :)
let $dmusubjidoid := (
    for $a in $definedoc//odm:ItemDef[@Name='USUBJID']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name='DM']/odm:ItemRef/@ItemOID
        return $a  
)
(: iterate over all other datasets :)
for $dataset in $definedoc//odm:ItemGroupDef[upper-case(@def:Class)='INTERVENTIONS' or upper-case(@def:Class)='EVENTS' or upper-case(@def:Class)='FINDINGS' 
		or upper-case(./def21:Class/@Name)='INTERVENTIONS' or upper-case(./def21:Class/@Name)='EVENTS' or upper-case(./def21:Class/@Name)='FINDINGS'
		or @Domain='SE']
    let $name := $dataset/@Name
    let $datasetname := (
		if($defineversion = '2.1') then $dataset/def21:leaf/@xlink:href
		else $dataset/def:leaf/@xlink:href
	)
    let $datasetdoc := (
		if($datasetname) then doc(concat($base,$datasetname))
		else ()
	)
    (: Get the OIDs of the --DY and --DTC variables  :)
    let $stdyoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'STDY') and string-length(@Name)=6]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
    )
    let $stdyname := concat($name,'STDY')
    let $stdtcoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'STDTC') and string-length(@Name)=7]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
    ) 
    let $stdtcname := $definedoc//odm:ItemDef[@OID=$stdtcoid]/@Name 
    (: and the OID of the USUBJID variable (might be different from the one in DM) :)
    let $usubjidoid := (
        for $a in $definedoc//odm:ItemDef[@Name='USUBJID']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
    )
    for $record in $datasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$stdtcoid]]
        let $recnum := $record/@data:ItemGroupDataSeq
        (: get the USUBJID value :)
        let $usubjidvalue := $record/odm:ItemData[@ItemOID=$usubjidoid]/@Value
        (: get the --DTC value :)
        let $stdtcvalue := $record/odm:ItemData[@ItemOID=$stdtcoid]/@Value
        (: and check whether it is a complete date - i.e. string-length at least 10 :)
        let $iscompletestdtcvalue := string-length($stdtcvalue) >= 10  (: boolean :)
        (: look up the record in the DM dataset and get the RFSTDTC :)
        (: taking the worst case scenario into account that there is more than one record for a USUBJID in DM - take the first one :)
        let $rfstdtcvalue := doc($dmdatasetlocation)//odm:ItemGroupData[odm:ItemData[@ItemOID=$dmusubjidoid and @Value=$usubjidvalue]][1]/odm:ItemData[@ItemOID=$rfstdtcoid]/@Value
        (: and check whether also RFSTDTC is complete :)
        let $iscompleterfstdtcvalue := string-length($rfstdtcvalue) >= 10 (: boolean :)
        let $stdyvalue := $record/odm:ItemData[@ItemOID=$stdyoid]/@Value
        (: now calculate the DY value but only when RFSTDTC and STDTC have a complete date part :)
        let $stdycalculated := (
            if ($iscompleterfstdtcvalue and $iscompletestdtcvalue) then
                (: if there is one, we must strip off the time part :)
                let $stdtcvalueshort := substring($stdtcvalue,1,10)
                let $rfstdtcvalueshort := substring($rfstdtcvalue,1,10)
                let $stdycalc := (
                    if($stdtcvalueshort castable as xs:date and $rfstdtcvalueshort castable as xs:date) then
                        days-from-duration(xs:date($stdtcvalueshort)-xs:date($rfstdtcvalueshort))
                    else()
                ) 
                return $stdycalc
            else ()  (: one or both dates are not complete dates :)
        )
        (: for the FDA there is no day 0, DY can only be <0 or >0, first day is day 1 :)
        let $stdycalculatedfda := (
            if($stdycalculated >= 0) then
                $stdycalculated + 1
            else (
                $stdycalculated    
            )
        )
        (: before comparing, also check whether the value is really an integer :)
        where $stdyvalue castable as xs:integer and $stdycalculatedfda and $stdyvalue and not($stdycalculatedfda = $stdyvalue)
        return <error dataset="{data($name)}" variable="{data($stdyname)}" rule="SD1090" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">Invalid value for {data($stdyname)}, calculated value={data($stdycalculatedfda)}, observed value={data($stdyvalue)}, in dataset {data($name)} </error>	
]]></rulexquery>
</sdsrule>

<sdsrule id="SD1090-SD" last-update="2020-08-08" originator="FDA" standard="SDTM" requiresDomainOrDataset="Yes">
<ruledescription>--STDY must be correctly calculated</ruledescription>
<ruledetaileddescription>Study Day of Start (--STDY) variable value should be populated as defined by CDISC standards: --STDY = (date portion of --STDTC) - (date portion of RFSTDTC) +1 if --STDTC is on or after RFSTDTC; --STDY = (date portion of --STDTC) - (date portion of RFSTDTC) if --STDTC precedes RFSTDTC</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>INTERVENTIONS</domain>
<domain>EVENTS</domain>
<domain>FINDINGS</domain>
<domain>SE</domain>
<rulexquery><![CDATA[	
(: Rule SD1090 - Incorrect value for --STDY variable:
Study Day of Start (--STDY) variable value should be populated as defined by CDISC standards: --STDY = (date portion of --STDTC) - (date portion of RFSTDTC) +1 if --STDTC is on or after RFSTDTC; --STDY = (date portion of --STDTC) - (date portion of RFSTDTC) if --STDTC precedes RFSTDTC.
:)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
declare variable $datasetname external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: Get the location of the DM dataset :)
let $dmdataset := (
	if($defineversion = '2.1') then $definedoc//odm:ItemGroupDef[@Name='DM']/def21:leaf/@xlink:href
	else $definedoc//odm:ItemGroupDef[@Name='DM']/def:leaf/@xlink:href
)
let $dmdatasetdoc := doc(concat($base,$dmdataset))
(: get the OID of the RFSTDTC variable :)
let $rfstdtcoid := (
    for $a in $definedoc//odm:ItemDef[@Name='RFSTDTC']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name='DM']/odm:ItemRef/@ItemOID
        return $a   
)
(: get the OID of the USUBJID variable :)
let $dmusubjidoid := (
    for $a in $definedoc//odm:ItemDef[@Name='USUBJID']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name='DM']/odm:ItemRef/@ItemOID
        return $a  
)
(: iterate over all other provided datasets :)
for $dataset in $definedoc//odm:ItemGroupDef[@Name=$datasetname][upper-case(@def:Class)='INTERVENTIONS' or upper-case(@def:Class)='EVENTS' or upper-case(@def:Class)='FINDINGS' 
		or upper-case(./def21:Class/@Name)='INTERVENTIONS' or upper-case(./def21:Class/@Name)='EVENTS' or upper-case(./def21:Class/@Name)='FINDINGS'
		or @Domain='SE']
    let $name := $dataset/@Name
    let $dsname := (
		if($defineversion = '2.1') then $dataset/def21:leaf/@xlink:href
		else $dataset/def:leaf/@xlink:href
	)
    let $datasetdoc := (
		if($dsname) then doc(concat($base,$dsname))
		else ()
	)
    (: Get the OIDs of the --DY and --DTC variables  :)
    let $stdyoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'STDY') and string-length(@Name)=6]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
    )
    let $stdyname := concat($name,'STDY')
    let $stdtcoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'STDTC') and string-length(@Name)=7]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
    ) 
    let $stdtcname := $definedoc//odm:ItemDef[@OID=$stdtcoid]/@Name 
    (: and the OID of the USUBJID variable (might be different from the one in DM) :)
    let $usubjidoid := (
        for $a in $definedoc//odm:ItemDef[@Name='USUBJID']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
    )
    for $record in $datasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$stdtcoid]]
        let $recnum := $record/@data:ItemGroupDataSeq
        (: get the USUBJID value :)
        let $usubjidvalue := $record/odm:ItemData[@ItemOID=$usubjidoid]/@Value
        (: get the --DTC value :)
        let $stdtcvalue := $record/odm:ItemData[@ItemOID=$stdtcoid]/@Value
        (: and check whether it is a complete date - i.e. string-length at least 10 :)
        let $iscompletestdtcvalue := string-length($stdtcvalue) >= 10  (: boolean :)
        (: look up the record in the DM dataset and get the RFSTDTC :)
        (: taking the worst case scenario into account that there is more than one record for a USUBJID in DM - take the first one :)
        let $rfstdtcvalue := $dmdatasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$dmusubjidoid and @Value=$usubjidvalue]][1]/odm:ItemData[@ItemOID=$rfstdtcoid]/@Value
        (: and check whether also RFSTDTC is complete :)
        let $iscompleterfstdtcvalue := string-length($rfstdtcvalue) >= 10 (: boolean :)
        let $stdyvalue := $record/odm:ItemData[@ItemOID=$stdyoid]/@Value
        (: now calculate the DY value but only when RFSTDTC and STDTC have a complete date part :)
        let $stdycalculated := (
            if ($iscompleterfstdtcvalue and $iscompletestdtcvalue) then
                (: if there is one, we must strip off the time part :)
                let $stdtcvalueshort := substring($stdtcvalue,1,10)
                let $rfstdtcvalueshort := substring($rfstdtcvalue,1,10)
                let $stdycalc := (
                    if($stdtcvalueshort castable as xs:date and $rfstdtcvalueshort castable as xs:date) then
                        days-from-duration(xs:date($stdtcvalueshort)-xs:date($rfstdtcvalueshort))
                    else()
                ) 
                return $stdycalc
            else ()  (: one or both dates are not complete dates :)
        )
        (: for the FDA there is no day 0, DY can only be <0 or >0, first day is day 1 :)
        let $stdycalculatedfda := (
            if($stdycalculated >= 0) then
                $stdycalculated + 1
            else (
                $stdycalculated    
            )
        )
        (: before comparing, also check whether the value is really an integer :)
        where $stdyvalue castable as xs:integer and $stdycalculatedfda and $stdyvalue and not($stdycalculatedfda = $stdyvalue)
        return <error dataset="{data($name)}" variable="{data($stdyname)}" rule="SD1090" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">Invalid value for {data($stdyname)}, calculated value={data($stdycalculatedfda)}, observed value={data($stdyvalue)}, in dataset {data($name)} </error>	
]]></rulexquery>
</sdsrule>

<sdsrule id="SD1089" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>STDY variable value may not be imputed</ruledescription>
<ruledetaileddescription>Study Day of Start (--STDY) variable value may not be imputed. It may be only populated, when both Start Study Date/Time (--STDTC) and Subject Reference Start Date/Time (RFSTDTC) variables values are provided, and both of them include complete date part.</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>INTERVENTIONS</domain>
<domain>EVENTS</domain>
<domain>FINDINGS</domain>
<domain>SE</domain>
<rulexquery><![CDATA[	
(: Rule SD1089 - --STDY variable value is imputed
Study Day of Start (--STDY) variable value should be not be imputed. It may be only populated, when both Start Study Date/Time (--STDTC) and Subject Reference Start Date/Time (RFSTDTC) variables values are provided, and both of them include complete date part.
:)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink"; 
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: get the location of the DM dataset :)
let $dmdatasetname := (
	if($defineversion = '2.1') then $definedoc//odm:ItemGroupDef[@Name='DM']/def21:leaf/@xlink:href
	else $definedoc//odm:ItemGroupDef[@Name='DM']/def:leaf/@xlink:href
)
let $dmdatasetdoc := doc(concat($base,$dmdatasetname))
(: and the OIDs of the USUBJID and RFSTDTC variables :)
let $dmusubjidoid := (
    for $a in $definedoc//odm:ItemDef[@Name='USUBJID']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name='DM']/odm:ItemRef/@ItemOID
        return $a 
)
let $rfstdtcoid := (
    for $a in $definedoc//odm:ItemDef[@Name='RFSTDTC']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name='DM']/odm:ItemRef/@ItemOID
        return $a 
)
(: iterate over all datasets :)
for $dataset in $definedoc//odm:ItemGroupDef[upper-case(@def:Class)='INTERVENTIONS' or upper-case(@def:Class)='EVENTS' or upper-case(@def:Class)='FINDINGS' 
		or upper-case(./def21:Class/@Name)='INTERVENTIONS' or upper-case(./def21:Class/@Name)='EVENTS' or upper-case(./def21:Class/@Name)='FINDINGS' 
		or @Domain='SE']
    let $name := $dataset/@Name
    let $datasetname := (
		if($defineversion = '2.1') then $dataset/def21:leaf/@xlink:href
		else $dataset/def:leaf/@xlink:href
	)
    let $datasetdoc := (
		if($datasetname) then doc(concat($base,$datasetname))
		else ()
	)
    (: Get the OIDs of the SDTY and STDTC variable :)
    let $stdyoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'STDY')]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a  
    )
    let $stdyname := $definedoc//odm:ItemDef[@OID=$stdyoid]/@Name
    let $stdtcoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'STDTC')]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a  
    )
    let $stdtcname := $definedoc//odm:ItemDef[@OID=$stdtcoid]/@Name
    (: and the OID of USUBJID in this dataset (can be different from in DM) :)
    let $usubjidoid := (
        for $a in $definedoc//odm:ItemDef[@Name='USUBJID']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
    )
    (: iterate over all records in the dataset that DO have both a STDCT value  :)
    for $record in $datasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$stdtcoid]]
        let $recnum := $record/@data:ItemGroupDataSeq
        let $stdtcvalue := $record/odm:ItemData[@ItemOID=$stdtcoid]/@Value
        (: check whether it is a complete date :)
        let $iscompletestdtcdate := string-length($stdtcvalue) >= 10  (: boolean :)
        (: get the USUBJID and get the corresponding RFSTDTC in the DM dataset :)
        let $usubjidvalue := $record/odm:ItemData[@ItemOID=$usubjidoid]/@Value
        (: taking the worst case scenario into account that there is more than one record for a USUBJID in DM - take the first one :)
        let $rfstdtcvalue := $dmdatasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$dmusubjidoid and @Value=$usubjidvalue]][1]/odm:ItemData[@ItemOID=$rfstdtcoid]/@Value
        (: and check whether RFSTDTC is a complete date :)
        let $iscompleterfstdtcdate := string-length($rfstdtcvalue) >= 10
        (: Get the value of the STDY variable (if any) :)
        let $stdyvalue := $record/odm:ItemData[@ItemOID=$stdyoid]/@Value
        (: STDY may NOT be populated when of RFSTDTC or STDTC is an incomplete date :)
        where $stdyoid and (not($iscompletestdtcdate) or not($iscompleterfstdtcdate)) and $stdyvalue
        return <error rule="SD1089" dataset="{data($name)}" variable="{data($stdyname)}" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">{data($stdyname)} variable value {data($stdyvalue)} is imputed although one of {data($stdtcname)} (value={data($stdtcvalue)}) or RFSTDTC (value={data($rfstdtcvalue)}) is not a  complete date</error>	
]]></rulexquery>
</sdsrule>

<sdsrule id="SD1089-SD" last-update="2020-08-08" originator="FDA" standard="SDTM" requiresDomainOrDataset="Yes">
<ruledescription>STDY variable value may not be imputed</ruledescription>
<ruledetaileddescription>Study Day of Start (--STDY) variable value may not be imputed. It may be only populated, when both Start Study Date/Time (--STDTC) and Subject Reference Start Date/Time (RFSTDTC) variables values are provided, and both of them include complete date part.</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>INTERVENTIONS</domain>
<domain>EVENTS</domain>
<domain>FINDINGS</domain>
<domain>SE</domain>
<rulexquery><![CDATA[	
(: Rule SD1089 - --STDY variable value is imputed
Study Day of Start (--STDY) variable value should be not be imputed. It may be only populated, when both Start Study Date/Time (--STDTC) and Subject Reference Start Date/Time (RFSTDTC) variables values are provided, and both of them include complete date part.
:)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink"; 
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
declare variable $datasetname external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: get the location of the DM dataset :)
let $dmdatasetname := (
	if($defineversion = '2.1') then $definedoc//odm:ItemGroupDef[@Name='DM']/def21:leaf/@xlink:href
	else $definedoc//odm:ItemGroupDef[@Name='DM']/def:leaf/@xlink:href
)
let $dmdatasetdoc := doc(concat($base,$dmdatasetname))
(: and the OIDs of the USUBJID and RFSTDTC variables :)
let $dmusubjidoid := (
    for $a in $definedoc//odm:ItemDef[@Name='USUBJID']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name='DM']/odm:ItemRef/@ItemOID
        return $a 
)
let $rfstdtcoid := (
    for $a in $definedoc//odm:ItemDef[@Name='RFSTDTC']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name='DM']/odm:ItemRef/@ItemOID
        return $a 
)
(: iterate over all provided datasets :)
for $dataset in $definedoc//odm:ItemGroupDef[@Name=$datasetname][upper-case(@def:Class)='INTERVENTIONS' or upper-case(@def:Class)='EVENTS' or upper-case(@def:Class)='FINDINGS' 
		or upper-case(./def21:Class/@Name)='INTERVENTIONS' or upper-case(./def21:Class/@Name)='EVENTS' or upper-case(./def21:Class/@Name)='FINDINGS'
		or @Domain='SE']
    let $name := $dataset/@Name
    let $dsname := (
		if($defineversion = '2.1') then $dataset/def21:leaf/@xlink:href
		else $dataset/def:leaf/@xlink:href
	)
    let $datasetdoc := (
		if($dsname) then doc(concat($base,$dsname))
		else ()
	)
    (: Get the OIDs of the SDTY and STDTC variable :)
    let $stdyoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'STDY')]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a  
    )
    let $stdyname := $definedoc//odm:ItemDef[@OID=$stdyoid]/@Name
    let $stdtcoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'STDTC')]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a  
    )
    let $stdtcname := $definedoc//odm:ItemDef[@OID=$stdtcoid]/@Name
    (: and the OID of USUBJID in this dataset (can be different from in DM) :)
    let $usubjidoid := (
        for $a in $definedoc//odm:ItemDef[@Name='USUBJID']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
    )
    (: iterate over all records in the dataset that DO have both a STDCT value  :)
    for $record in $datasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$stdtcoid]]
        let $recnum := $record/@data:ItemGroupDataSeq
        let $stdtcvalue := $record/odm:ItemData[@ItemOID=$stdtcoid]/@Value
        (: check whether it is a complete date :)
        let $iscompletestdtcdate := string-length($stdtcvalue) >= 10  (: boolean :)
        (: get the USUBJID and get the corresponding RFSTDTC in the DM dataset :)
        let $usubjidvalue := $record/odm:ItemData[@ItemOID=$usubjidoid]/@Value
        (: taking the worst case scenario into account that there is more than one record for a USUBJID in DM - take the first one :)
        let $rfstdtcvalue := $dmdatasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$dmusubjidoid and @Value=$usubjidvalue]][1]/odm:ItemData[@ItemOID=$rfstdtcoid]/@Value
        (: and check whether RFSTDTC is a complete date :)
        let $iscompleterfstdtcdate := string-length($rfstdtcvalue) >= 10
        (: Get the value of the STDY variable (if any) :)
        let $stdyvalue := $record/odm:ItemData[@ItemOID=$stdyoid]/@Value
        (: STDY may NOT be populated when of RFSTDTC or STDTC is an incomplete date :)
        where $stdyoid and (not($iscompletestdtcdate) or not($iscompleterfstdtcdate)) and $stdyvalue
        return <error rule="SD1089" dataset="{data($name)}" variable="{data($stdyname)}" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">{data($stdyname)} variable value {data($stdyvalue)} is imputed although one of {data($stdtcname)} (value={data($stdtcvalue)}) or RFSTDTC (value={data($rfstdtcvalue)}) is not a  complete date</error>
]]></rulexquery>
</sdsrule>


<sdsrule id="SD1092" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>--ENDY variable value must be populated when --ENDTC and DM.RFSTDTC are provided</ruledescription>
<ruledetaileddescription>Study Day of End (--ENDY) variable value should be populated, when End Study Date/Time (--ENDTC) and Subject Reference Start Date/Time (RFSTDTC) variables values are provided, and both of them include complete date part</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>INTERVENTIONS</domain>
<domain>EVENTS</domain>
<domain>FINDINGS</domain>
<domain>SE</domain>
<rulexquery><![CDATA[
(: Rule SD1092 - --ENDY variable value is not populated: 
Study Day of End (--ENDY) variable value should be populated, when End Study Date/Time (--ENDTC) and Subject Reference Start Date/Time (RFSTDTC) variables values are provided, and both of them include complete date part.
:)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define)) 
(: get the location of the DM dataset :)
let $dmdatasetname := (
	if($defineversion = '2.1') then $definedoc//odm:ItemGroupDef[@Name='DM']/def21:leaf/@xlink:href
	else $definedoc//odm:ItemGroupDef[@Name='DM']/def:leaf/@xlink:href
)
let $dmdatasetdoc := doc(concat($base,$dmdatasetname))
(: and the OIDs of the USUBJID and RFSTDTC variables :)
let $dmusubjidoid := (
    for $a in $definedoc//odm:ItemDef[@Name='USUBJID']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name='DM']/odm:ItemRef/@ItemOID
        return $a 
)
let $rfstdtcoid := (
    for $a in $definedoc//odm:ItemDef[@Name='RFSTDTC']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name='DM']/odm:ItemRef/@ItemOID
        return $a 
)
(: iterate over all datasets :)
for $dataset in $definedoc//odm:ItemGroupDef[upper-case(@def:Class)='INTERVENTIONS' or upper-case(@def:Class)='EVENTS' or upper-case(@def:Class)='FINDINGS' 
		or upper-case(./def21:Class/@Name)='INTERVENTIONS' or upper-case(./def21:Class/@Name)='EVENTS' or upper-case(./def21:Class/@Name)='FINDINGS'
		or @Domain='SE']
    let $name := $dataset/@Name
    let $datasetname := (
		if($defineversion = '2.1') then $dataset/def21:leaf/@xlink:href
		else $dataset/def:leaf/@xlink:href
	)
    let $datasetdoc := (
		if($datasetname) then doc(concat($base,$datasetname))
		else ()
	)
    (: Get the OIDs of the ENDY and ENDTC variable :)
    let $endyoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'ENDY')]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a  
    )
    let $endyname := $definedoc//odm:ItemDef[@OID=$endyoid]/@Name
    let $endtcoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'ENDTC')]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a  
    )
    let $endtcname := $definedoc//odm:ItemDef[@OID=$endtcoid]/@Name
    (: and the OID of USUBJID in this dataset (can be different from in DM) :)
    let $usubjidoid := (
        for $a in $definedoc//odm:ItemDef[@Name='USUBJID']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
    )
    (: iterate over all records in the dataset that DO have both an ENDCT value  :)
    for $record in $datasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$endtcoid]]
        let $recnum := $record/@data:ItemGroupDataSeq
        let $endtcvalue := $record/odm:ItemData[@ItemOID=$endtcoid]/@Value
        (: check whether it is a complete date :)
        let $iscompleteendtcdate := string-length($endtcvalue) >= 10  (: boolean :)
        (: get the USUBJID and get the corresponding RFSTDTC in the DM dataset :)
        let $usubjidvalue := $record/odm:ItemData[@ItemOID=$usubjidoid]/@Value
        (: taking the worst case scenario into account that there is more than 1 record for a USUBJID in DM - take the first one :)
        let $rfstdtcvalue := $dmdatasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$dmusubjidoid and @Value=$usubjidvalue]][1]/odm:ItemData[@ItemOID=$rfstdtcoid]/@Value
        (: and check whether RFSTDTC is a complete date :)
        let $iscompleterfstdtcdate := string-length($rfstdtcvalue) >= 10
        (: Get the value of the STDY variable (if any) :)
        let $endyvalue := $record/odm:ItemData[@ItemOID=$endyoid]/@Value
        (: ENDY MUST be populated when the variable is in the dataset and when both ENSTDTC AND ENDTC are complete dates :)
        where $endyoid and $iscompleteendtcdate and $iscompleterfstdtcdate and not($endyvalue)
        return <error rule="SD1092" dataset="{data($name)}" variable="{data($endyname)}" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">{data($endyname)} variable is not populated although both {data($endtcname)} (value={data($endtcvalue)}) and RFSTDTC (value={data($rfstdtcvalue)}) are complete dates</error>
]]></rulexquery>
</sdsrule>

<sdsrule id="SD1092-SD" last-update="2020-08-08" originator="FDA" standard="SDTM" requiresDomainOrDataset="Yes">
<ruledescription>--ENDY variable value must be populated when --ENDTC and DM.RFSTDTC are provided</ruledescription>
<ruledetaileddescription>Study Day of End (--ENDY) variable value should be populated, when End Study Date/Time (--ENDTC) and Subject Reference Start Date/Time (RFSTDTC) variables values are provided, and both of them include complete date part</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>INTERVENTIONS</domain>
<domain>EVENTS</domain>
<domain>FINDINGS</domain>
<domain>SE</domain>
<rulexquery><![CDATA[
(: Rule SD1092 - --ENDY variable value is not populated: 
Study Day of End (--ENDY) variable value should be populated, when End Study Date/Time (--ENDTC) and Subject Reference Start Date/Time (RFSTDTC) variables values are provided, and both of them include complete date part.
:)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
declare variable $datasetname external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define)) 
(: get the location of the DM dataset :)
let $dmdatasetname := (
	if($defineversion = '2.1') then $definedoc//odm:ItemGroupDef[@Name='DM']/def21:leaf/@xlink:href
	else $definedoc//odm:ItemGroupDef[@Name='DM']/def:leaf/@xlink:href
)
let $dmdatasetdoc := doc(concat($base,$dmdatasetname))
(: and the OIDs of the USUBJID and RFSTDTC variables :)
let $dmusubjidoid := (
    for $a in $definedoc//odm:ItemDef[@Name='USUBJID']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name='DM']/odm:ItemRef/@ItemOID
        return $a 
)
let $rfstdtcoid := (
    for $a in $definedoc//odm:ItemDef[@Name='RFSTDTC']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name='DM']/odm:ItemRef/@ItemOID
        return $a 
)
(: iterate over all provided datasets :)
for $dataset in $definedoc//odm:ItemGroupDef[@Name=$datasetname][upper-case(@def:Class)='INTERVENTIONS' or upper-case(@def:Class)='EVENTS' or upper-case(@def:Class)='FINDINGS' 
		or upper-case(./def21:Class/@Name)='INTERVENTIONS' or upper-case(./def21:Class/@Name)='EVENTS' or upper-case(./def21:Class/@Name)='FINDINGS'
		or @Domain='SE']
    let $name := $dataset/@Name
    let $dsname := (
		if($defineversion = '2.1') then $dataset/def21:leaf/@xlink:href
		else $dataset/def:leaf/@xlink:href
	)
    let $datasetdoc := (
		if($dsname) then doc(concat($base,$dsname))
		else ()
	)
    (: Get the OIDs of the ENDY and ENDTC variable :)
    let $endyoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'ENDY')]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a  
    )
    let $endyname := $definedoc//odm:ItemDef[@OID=$endyoid]/@Name
    let $endtcoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'ENDTC')]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a  
    )
    let $endtcname := $definedoc//odm:ItemDef[@OID=$endtcoid]/@Name
    (: and the OID of USUBJID in this dataset (can be different from in DM) :)
    let $usubjidoid := (
        for $a in $definedoc//odm:ItemDef[@Name='USUBJID']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
    )
    (: iterate over all records in the dataset that DO have both an ENDCT value  :)
    for $record in $datasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$endtcoid]]
        let $recnum := $record/@data:ItemGroupDataSeq
        let $endtcvalue := $record/odm:ItemData[@ItemOID=$endtcoid]/@Value
        (: check whether it is a complete date :)
        let $iscompleteendtcdate := string-length($endtcvalue) >= 10  (: boolean :)
        (: get the USUBJID and get the corresponding RFSTDTC in the DM dataset :)
        let $usubjidvalue := $record/odm:ItemData[@ItemOID=$usubjidoid]/@Value
        (: taking the worst case scenario into account that there is more than 1 record for a USUBJID in DM - take the first one :)
        let $rfstdtcvalue := $dmdatasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$dmusubjidoid and @Value=$usubjidvalue]][1]/odm:ItemData[@ItemOID=$rfstdtcoid]/@Value
        (: and check whether RFSTDTC is a complete date :)
        let $iscompleterfstdtcdate := string-length($rfstdtcvalue) >= 10
        (: Get the value of the STDY variable (if any) :)
        let $endyvalue := $record/odm:ItemData[@ItemOID=$endyoid]/@Value
        (: ENDY MUST be populated when the variable is in the dataset and when both ENSTDTC AND ENDTC are complete dates :)
        where $endyoid and $iscompleteendtcdate and $iscompleterfstdtcdate and not($endyvalue)
        return <error rule="SD1092" dataset="{data($name)}" variable="{data($endyname)}" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">{data($endyname)} variable is not populated although both {data($endtcname)} (value={data($endtcvalue)}) and RFSTDTC (value={data($rfstdtcvalue)}) are complete dates</error>	
]]></rulexquery>
</sdsrule>


<sdsrule id="SD1094" last-update="2020-08-08" originator="FDA" standard="SDTM" timeintensive="Yes">
<ruledescription>--ENDY must be correctly calculated</ruledescription>
<ruledetaileddescription>Study Day of End (--ENDY) variable value should be populated as defined by CDISC standards: --ENDY = (date portion of --STDTC) - (date portion of RFSTDTC) +1 if --ENDTC is on or after RFSTDTC; --ENTDY = (date portion of --ENDTC) - (date portion of RFSTDTC) if --ENDTC precedes RFSTDTC</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>INTERVENTIONS</domain>
<domain>EVENTS</domain>
<domain>FINDINGS</domain>
<domain>SE</domain>
<domain>SV</domain>
<rulexquery><![CDATA[	
(: Rule SD1094 - Incorrect value for --ENDY variable:
Study Day of End (--ENDY) variable value should be populated as defined by CDISC standards: --ENDY = (date portion of --STDTC) - (date portion of RFSTDTC) +1 if --ENDTC is on or after RFSTDTC; --ENTDY = (date portion of --ENDTC) - (date portion of RFSTDTC) if --ENDTC precedes RFSTDTC.
:)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink"; 
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/'  :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: Get the location of the DM dataset :)
let $dmdataset := (
	if($defineversion = '2.1') then $definedoc//odm:ItemGroupDef[@Name='DM']/def21:leaf/@xlink:href
	else $definedoc//odm:ItemGroupDef[@Name='DM']/def:leaf/@xlink:href
)
let $dmdatasetdoc := doc(concat($base,$dmdataset))
(: get the OID of the RFSTDTC variable :)
let $rfstdtcoid := (
    for $a in $definedoc//odm:ItemDef[@Name='RFSTDTC']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name='DM']/odm:ItemRef/@ItemOID
        return $a   
)
(: get the OID of the USUBJID variable :)
let $dmusubjidoid := (
    for $a in $definedoc//odm:ItemDef[@Name='USUBJID']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name='DM']/odm:ItemRef/@ItemOID
        return $a  
)
(: iterate over all other datasets :)
for $dataset in $definedoc//odm:ItemGroupDef[upper-case(@def:Class)='INTERVENTIONS' or upper-case(@def:Class)='EVENTS' or upper-case(@def:Class)='FINDINGS' 
		or upper-case(./def21:Class/@Name)='INTERVENTIONS' or upper-case(./def21:Class/@Name)='EVENTS' or upper-case(./def21:Class/@Name)='FINDINGS' 
		or @Domain='SE' or @Domain='SV']
    let $name := $dataset/@Name
    let $datasetname := (
		if($defineversion = '2.1') then $dataset/def21:leaf/@xlink:href
		else $dataset/def:leaf/@xlink:href
	)
    let $datasetdoc := (
		if($datasetname) then doc(concat($base,$datasetname))
		else ()
	)
    (: Get the OIDs of the ENDY and ENDTC variables  :)
    let $endyoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'ENDY') and string-length(@Name)=6]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
    )
    let $endyname := concat($name,'ENDY')  (: as in may not be in the set of variables :)
    let $endtcoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'ENDTC') and string-length(@Name)=7]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
    ) 
    let $endtcname := $definedoc//odm:ItemDef[@OID=$endtcoid]/@Name 
    (: and the OID of the USUBJID variable (might be different from the one in DM) :)
    let $usubjidoid := (
        for $a in $definedoc//odm:ItemDef[@Name='USUBJID']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
    )
    (: iterate over all the records that have an ENDTC data point  :)
    for $record in $datasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$endtcoid]]
        let $recnum := $record/@data:ItemGroupDataSeq
        (: get the USUBJID value :)
        let $usubjidvalue := $record/odm:ItemData[@ItemOID=$usubjidoid]/@Value
        (: get the --DTC value :)
        let $endtcvalue := $record/odm:ItemData[@ItemOID=$endtcoid]/@Value
        (: and check whether it is a complete date - i.e. string-length at least 10 :)
        let $iscompletestdtcvalue := string-length($endtcvalue) >= 10  (: boolean :)
        (: look up the record in the DM dataset and get the RFSTDTC :)
        (: taking worst case scenario into account that there is more than 1 record for a USUBJID in DM - take the first one :)
        let $rfstdtcvalue := $dmdatasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$dmusubjidoid and @Value=$usubjidvalue]][1]/odm:ItemData[@ItemOID=$rfstdtcoid]/@Value
        (: and check whether also RFSTDTC is complete :)
        let $iscompleterfstdtcvalue := string-length($rfstdtcvalue) >= 10 (: boolean :)
        let $endyvalue := $record/odm:ItemData[@ItemOID=$endyoid]/@Value
        (: now calculate the DY value but only when RFSTDTC and ENDTC have a complete date part :)
        let $endycalculated := (
            if ($iscompleterfstdtcvalue and $iscompletestdtcvalue) then
                (: if there is one, we must strip off the time part :)
                let $endtcvalueshort := substring($endtcvalue,1,10)
                let $rfstdtcvalueshort := substring($rfstdtcvalue,1,10)
                let $endycalc := (
                    if($endtcvalueshort castable as xs:date and $rfstdtcvalueshort castable as xs:date) then
                        days-from-duration(xs:date($endtcvalueshort)-xs:date($rfstdtcvalueshort))
                    else()
                ) 
                return $endycalc
            else ()  (: one or both dates are not complete dates :)
        )
        (: for the FDA there is no day 0, DY can only be <0 or >0, first day is day 1 :)
        let $endycalculatedfda := (
            if($endycalculated >= 0) then
                $endycalculated + 1
            else (
                $endycalculated    
            )
        )
        (: before comparing, check that the ENDY value is really an integer :)
        where $endyvalue castable as xs:integer and $endycalculatedfda and $endyvalue and not($endycalculatedfda = $endyvalue)
        return <error rule="SD1094" dataset="{data($name)}" variable="{data($endyname)}" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">Invalid value for {data($endyname)}, calculated value={data($endycalculatedfda)}, observed value={data($endyvalue)}, in dataset {data($datasetname)} </error>
]]></rulexquery>
</sdsrule>

<sdsrule id="SD1094-SD" last-update="2020-08-08" originator="FDA" standard="SDTM" requiresDomainOrDataset="Yes">
<ruledescription>--ENDY must be correctly calculated</ruledescription>
<ruledetaileddescription>Study Day of End (--ENDY) variable value should be populated as defined by CDISC standards: --ENDY = (date portion of --STDTC) - (date portion of RFSTDTC) +1 if --ENDTC is on or after RFSTDTC; --ENTDY = (date portion of --ENDTC) - (date portion of RFSTDTC) if --ENDTC precedes RFSTDTC</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>INTERVENTIONS</domain>
<domain>EVENTS</domain>
<domain>FINDINGS</domain>
<domain>SE</domain>
<domain>SV</domain>
<rulexquery><![CDATA[	
(: Rule SD1094 - Incorrect value for --ENDY variable:
Study Day of End (--ENDY) variable value should be populated as defined by CDISC standards: --ENDY = (date portion of --STDTC) - (date portion of RFSTDTC) +1 if --ENDTC is on or after RFSTDTC; --ENTDY = (date portion of --ENDTC) - (date portion of RFSTDTC) if --ENDTC precedes RFSTDTC.
:)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink"; 
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
declare variable $datasetname external;
(: let $base := '/db/fda_submissions/cdisc01/'  :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: Get the location of the DM dataset :)
let $dmdataset := (
	if($defineversion = '2.1') then $definedoc//odm:ItemGroupDef[@Name='DM']/def21:leaf/@xlink:href
	else $definedoc//odm:ItemGroupDef[@Name='DM']/def:leaf/@xlink:href
)
let $dmdatasetdoc := doc(concat($base,$dmdataset))
(: get the OID of the RFSTDTC variable :)
let $rfstdtcoid := (
    for $a in $definedoc//odm:ItemDef[@Name='RFSTDTC']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name='DM']/odm:ItemRef/@ItemOID
        return $a   
)
(: get the OID of the USUBJID variable :)
let $dmusubjidoid := (
    for $a in $definedoc//odm:ItemDef[@Name='USUBJID']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name='DM']/odm:ItemRef/@ItemOID
        return $a  
)
(: iterate over all other provided datasets :)
for $dataset in $definedoc//odm:ItemGroupDef[@Name=$datasetname][upper-case(@def:Class)='INTERVENTIONS' or upper-case(@def:Class)='EVENTS' or upper-case(@def:Class)='FINDINGS' 
		or upper-case(./def21:Class/@Name)='INTERVENTIONS' or upper-case(./def21:Class/@Name)='EVENTS' or upper-case(./def21:Class/@Name)='FINDINGS'
		or @Domain='SE' or @Domain='SV']
    let $name := $dataset/@Name
    let $dsname := (
		if($defineversion = '2.1') then $dataset/def21:leaf/@xlink:href
		else $dataset/def:leaf/@xlink:href
	)
    let $datasetdoc := (
		if($dsname) then doc(concat($base,$dsname))
		else ()
	)
    (: Get the OIDs of the ENDY and ENDTC variables  :)
    let $endyoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'ENDY') and string-length(@Name)=6]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
    )
    let $endyname := concat($name,'ENDY')  (: as in may not be in the set of variables :)
    let $endtcoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'ENDTC') and string-length(@Name)=7]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
    ) 
    let $endtcname := $definedoc//odm:ItemDef[@OID=$endtcoid]/@Name 
    (: and the OID of the USUBJID variable (might be different from the one in DM) :)
    let $usubjidoid := (
        for $a in $definedoc//odm:ItemDef[@Name='USUBJID']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
    )
    (: iterate over all the records that have an ENDTC data point  :)
    for $record in $datasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$endtcoid]]
        let $recnum := $record/@data:ItemGroupDataSeq
        (: get the USUBJID value :)
        let $usubjidvalue := $record/odm:ItemData[@ItemOID=$usubjidoid]/@Value
        (: get the --DTC value :)
        let $endtcvalue := $record/odm:ItemData[@ItemOID=$endtcoid]/@Value
        (: and check whether it is a complete date - i.e. string-length at least 10 :)
        let $iscompletestdtcvalue := string-length($endtcvalue) >= 10  (: boolean :)
        (: look up the record in the DM dataset and get the RFSTDTC :)
        (: taking worst case scenario into account that there is more than 1 record for a USUBJID in DM - take the first one :)
        let $rfstdtcvalue := $dmdatasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$dmusubjidoid and @Value=$usubjidvalue]][1]/odm:ItemData[@ItemOID=$rfstdtcoid]/@Value
        (: and check whether also RFSTDTC is complete :)
        let $iscompleterfstdtcvalue := string-length($rfstdtcvalue) >= 10 (: boolean :)
        let $endyvalue := $record/odm:ItemData[@ItemOID=$endyoid]/@Value
        (: now calculate the DY value but only when RFSTDTC and ENDTC have a complete date part :)
        let $endycalculated := (
            if ($iscompleterfstdtcvalue and $iscompletestdtcvalue) then
                (: if there is one, we must strip off the time part :)
                let $endtcvalueshort := substring($endtcvalue,1,10)
                let $rfstdtcvalueshort := substring($rfstdtcvalue,1,10)
                let $endycalc := (
                    if($endtcvalueshort castable as xs:date and $rfstdtcvalueshort castable as xs:date) then
                        days-from-duration(xs:date($endtcvalueshort)-xs:date($rfstdtcvalueshort))
                    else()
                ) 
                return $endycalc
            else ()  (: one or both dates are not complete dates :)
        )
        (: for the FDA there is no day 0, DY can only be <0 or >0, first day is day 1 :)
        let $endycalculatedfda := (
            if($endycalculated >= 0) then
                $endycalculated + 1
            else (
                $endycalculated    
            )
        )
        (: before comparing, check that the ENDY value is really an integer :)
        where $endyvalue castable as xs:integer and $endycalculatedfda and $endyvalue and not($endycalculatedfda = $endyvalue)
        return <error rule="SD1094" dataset="{data($name)}" variable="{data($endyname)}" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">Invalid value for {data($endyname)}, calculated value={data($endycalculatedfda)}, observed value={data($endyvalue)}, in dataset {data($datasetname)} </error>	
]]></rulexquery>
</sdsrule>


<sdsrule id="SD1093" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>--ENDY variable value may not be imputed</ruledescription>
<ruledetaileddescription>Study Day of End (--ENDY) variable value should be not be imputed. It may be only populated, when both End Study Date/Time (--ENDTC) and Subject Reference Start Date/Time (RFSTDTC) variables values are provided, and both of them include complete date part</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>INTERVENTIONS</domain>
<domain>EVENTS</domain>
<domain>FINDINGS</domain>
<domain>SE</domain>
<domain>SV</domain>
<rulexquery><![CDATA[
(: Rule SD1093 - --ENDY variable value is imputed:
Study Day of End (--ENDY) variable value should be not be imputed. It may be only populated, when both End Study Date/Time (--ENDTC) and Subject Reference Start Date/Time (RFSTDTC) variables values are provided, and both of them include complete date part.
:)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink"; 
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/'  :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: get the location of the DM dataset :)
let $dmdatasetname := (
	if($defineversion = '2.1') then $definedoc//odm:ItemGroupDef[@Name='DM']/def21:leaf/@xlink:href
	else $definedoc//odm:ItemGroupDef[@Name='DM']/def:leaf/@xlink:href
)
let $dmdatasetdoc := doc(concat($base,$dmdatasetname))
(: and the OIDs of the USUBJID and RFSTDTC variables :)
let $dmusubjidoid := (
    for $a in $definedoc//odm:ItemDef[@Name='USUBJID']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name='DM']/odm:ItemRef/@ItemOID
        return $a 
)
let $rfstdtcoid := (
    for $a in $definedoc//odm:ItemDef[@Name='RFSTDTC']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name='DM']/odm:ItemRef/@ItemOID
        return $a 
)
(: iterate over all datasets :)
for $dataset in $definedoc//odm:ItemGroupDef[upper-case(@def:Class)='INTERVENTIONS' or upper-case(@def:Class)='EVENTS' or upper-case(@def:Class)='FINDINGS' 
		or upper-case(./def21:Class/@Name)='INTERVENTIONS' or upper-case(./def21:Class/@Name)='EVENTS' or upper-case(./def21:Class/@Name)='FINDINGS'
		or @Domain='SE' or @Domain='SV']
    let $name := $dataset/@Name
    let $datasetname := (
		if($defineversion = '2.1') then $dataset/def21:leaf/@xlink:href
		else $dataset/def:leaf/@xlink:href
	)
    let $datasetdoc := (
		if($datasetname) then doc(concat($base,$datasetname))
		else ()
	)
    (: Get the OIDs of the SDTY and STDTC variable :)
    let $endyoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'ENDY')]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a  
    )
    let $endyname := $definedoc//odm:ItemDef[@OID=$endyoid]/@Name
    let $endtcoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'ENDTC')]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a  
    )
    let $endtcname := $definedoc//odm:ItemDef[@OID=$endtcoid]/@Name
    (: and the OID of USUBJID in this dataset (can be different from in DM) :)
    let $usubjidoid := (
        for $a in $definedoc//odm:ItemDef[@Name='USUBJID']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
    )
    (: iterate over all records in the dataset that DO have both a ENDCT value  :)
    for $record in $datasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$endtcoid]]
        let $recnum := $record/@data:ItemGroupDataSeq
        let $endtcvalue := $record/odm:ItemData[@ItemOID=$endtcoid]/@Value
        (: check whether it is a complete date :)
        let $iscompletestdtcdate := string-length($endtcvalue) >= 10  (: boolean :)
        (: get the USUBJID and get the corresponding RFSTDTC in the DM dataset :)
        let $usubjidvalue := $record/odm:ItemData[@ItemOID=$usubjidoid]/@Value
        (: taking worst case scenario into account that there is more than 1 record for a USUBJID in DM - take the first one :)
        let $rfstdtcvalue := $dmdatasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$dmusubjidoid and @Value=$usubjidvalue]][1]/odm:ItemData[@ItemOID=$rfstdtcoid]/@Value
        (: and check whether RFSTDTC is a complete date :)
        let $iscompleterfstdtcdate := string-length($rfstdtcvalue) >= 10
        (: Get the value of the STDY variable (if any) :)
        let $endyvalue := $record/odm:ItemData[@ItemOID=$endyoid]/@Value
        (: ENDY may NOT be populated when of RFSTDTC or ENDTC is an incomplete date :)
        where $endyoid and (not($iscompletestdtcdate) or not($iscompleterfstdtcdate)) and $endyvalue
        return <error rule="SD1093" dataset="{data($name)}" variable="{data($endyname)}" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">{data($endyname)} variable value {data($endyvalue)} is imputed although one of {data($endtcname)} (value={data($endtcvalue)}) or RFSTDTC (value={data($rfstdtcvalue)}) is not a complete date</error>
]]></rulexquery>
</sdsrule>

<sdsrule id="SD1093-SD" last-update="2020-08-08" originator="FDA" standard="SDTM" requiresDomainOrDataset="Yes">
<ruledescription>--ENDY variable value may not be imputed--ENDY variable value may not be imputed</ruledescription>
<ruledetaileddescription>Study Day of End (--ENDY) variable value should be not be imputed. It may be only populated, when both End Study Date/Time (--ENDTC) and Subject Reference Start Date/Time (RFSTDTC) variables values are provided, and both of them include complete date part</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>INTERVENTIONS</domain>
<domain>EVENTS</domain>
<domain>FINDINGS</domain>
<domain>SE</domain>
<domain>SV</domain>
<rulexquery><![CDATA[	
(: Rule SD1093 - --ENDY variable value is imputed:
Study Day of End (--ENDY) variable value should be not be imputed. It may be only populated, when both End Study Date/Time (--ENDTC) and Subject Reference Start Date/Time (RFSTDTC) variables values are provided, and both of them include complete date part.
:)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink"; 
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
declare variable $datasetname external;
(: let $base := '/db/fda_submissions/cdisc01/'  :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: get the location of the DM dataset :)
let $dmdatasetname := (
	if($defineversion = '2.1') then $definedoc//odm:ItemGroupDef[@Name='DM']/def21:leaf/@xlink:href
	else $definedoc//odm:ItemGroupDef[@Name='DM']/def:leaf/@xlink:href
)
let $dmdatasetdoc := doc(concat($base,$dmdatasetname))
(: and the OIDs of the USUBJID and RFSTDTC variables :)
let $dmusubjidoid := (
    for $a in $definedoc//odm:ItemDef[@Name='USUBJID']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name='DM']/odm:ItemRef/@ItemOID
        return $a 
)
let $rfstdtcoid := (
    for $a in $definedoc//odm:ItemDef[@Name='RFSTDTC']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name='DM']/odm:ItemRef/@ItemOID
        return $a 
)
(: iterate over all provided datasets :)
for $dataset in $definedoc//odm:ItemGroupDef[@Name=$datasetname][upper-case(@def:Class)='INTERVENTIONS' or upper-case(@def:Class)='EVENTS' or upper-case(@def:Class)='FINDINGS' 
		or upper-case(./def21:Class/@Name)='INTERVENTIONS' or upper-case(./def21:Class/@Name)='EVENTS' or upper-case(./def21:Class/@Name)='FINDINGS'
		or @Domain='SE' or @Domain='SV']
    let $name := $dataset/@Name
    let $datasetname := (
		if($defineversion = '2.1') then $dataset/def21:leaf/@xlink:href
		else $dataset/def:leaf/@xlink:href
	)
    let $datasetdoc := (
		if($datasetname) then doc(concat($base,$datasetname))
		else ()
	)
    (: Get the OIDs of the SDTY and STDTC variable :)
    let $endyoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'ENDY')]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a  
    )
    let $endyname := $definedoc//odm:ItemDef[@OID=$endyoid]/@Name
    let $endtcoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'ENDTC')]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a  
    )
    let $endtcname := $definedoc//odm:ItemDef[@OID=$endtcoid]/@Name
    (: and the OID of USUBJID in this dataset (can be different from in DM) :)
    let $usubjidoid := (
        for $a in $definedoc//odm:ItemDef[@Name='USUBJID']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
    )
    (: iterate over all records in the dataset that DO have both a ENDCT value  :)
    for $record in $datasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$endtcoid]]
        let $recnum := $record/@data:ItemGroupDataSeq
        let $endtcvalue := $record/odm:ItemData[@ItemOID=$endtcoid]/@Value
        (: check whether it is a complete date :)
        let $iscompletestdtcdate := string-length($endtcvalue) >= 10  (: boolean :)
        (: get the USUBJID and get the corresponding RFSTDTC in the DM dataset :)
        let $usubjidvalue := $record/odm:ItemData[@ItemOID=$usubjidoid]/@Value
        (: taking worst case scenario into account that there is more than 1 record for a USUBJID in DM - take the first one :)
        let $rfstdtcvalue := $dmdatasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$dmusubjidoid and @Value=$usubjidvalue]][1]/odm:ItemData[@ItemOID=$rfstdtcoid]/@Value
        (: and check whether RFSTDTC is a complete date :)
        let $iscompleterfstdtcdate := string-length($rfstdtcvalue) >= 10
        (: Get the value of the STDY variable (if any) :)
        let $endyvalue := $record/odm:ItemData[@ItemOID=$endyoid]/@Value
        (: ENDY may NOT be populated when of RFSTDTC or ENDTC is an incomplete date :)
        where $endyoid and (not($iscompletestdtcdate) or not($iscompleterfstdtcdate)) and $endyvalue
        return <error rule="SD1093" dataset="{data($name)}" variable="{data($endyname)}" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">{data($endyname)} variable value {data($endyvalue)} is imputed although one of {data($endtcname)} (value={data($endtcvalue)}) or RFSTDTC (value={data($rfstdtcvalue)}) is not a complete date</error>	
]]></rulexquery>
</sdsrule>

<sdsrule id="SD1030" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>Value for --STRF may not be populated, when RFSTDTC is NULL</ruledescription>
<ruledetaileddescription>Start Relative to Reference Period (--STRF) should not be populated for subjects with missing value for Reference Start Date/Time (RFSTDTC)</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>INTERVENTIONS</domain>
<domain>EVENTS</domain>
<domain>FINDINGS</domain>
<domain>SE</domain>
<domain>SV</domain>
<rulexquery><![CDATA[	
(: TODO: not tested yet - we need a good test submission :)
(: Rule SD1030 - Value for --STRF is populated, when RFSTDTC is NULL
Start Relative to Reference Period (--STRF) should not be populated for subjects with missing value for Reference Start Date/Time (RFSTDTC)
:)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink"; 
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: Get the location of the DM dataset :)
let $dmdataset := (
	if($defineversion = '2.1') then $definedoc//odm:ItemGroupDef[@Name='DM']/def21:leaf/@xlink:href
	else $definedoc//odm:ItemGroupDef[@Name='DM']/def:leaf/@xlink:href
)
let $dmdatasetdoc := doc(concat($base,$dmdataset))
(: get the OID of the RFSTDTC variable :)
let $rfstdtcoid := (
    for $a in $definedoc//odm:ItemDef[@Name='RFSTDTC']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name='DM']/odm:ItemRef/@ItemOID
        return $a   
)
(: get the OID of the USUBJID variable :)
let $dmusubjidoid := (
    for $a in $definedoc//odm:ItemDef[@Name='USUBJID']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name='DM']/odm:ItemRef/@ItemOID
        return $a  
)
(: iterate over all the datasets except within INTERVENTIONS, EVENTS, FINDINGS, SE, SV :)
for $dataset in $definedoc//odm:ItemGroupDef[upper-case(@def:Class)='INTERVENTIONS' or upper-case(@def:Class)='EVENTS' or upper-case(@def:Class)='FINDINGS' 
	or upper-case(./def21:Class/@Name)='INTERVENTIONS' or upper-case(./def21:Class/@Name)='EVENTS' or upper-case(./def21:Class/@Name)='FINDINGS' or @Domain='SE' or @Domain='SV']
    let $name := $dataset/@Name
    let $datasetname := (
		if($defineversion = '2.1') then $dataset/def21:leaf/@xlink:href
		else $dataset/def:leaf/@xlink:href
	)
    let $datasetdoc := (
		if($datasetname) then doc(concat($base,$datasetname))
		else ()
	)
    (: get the OID of the --STRF variable (if any) :)
    let $strfoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'STRF')]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a 
    )
    let $strfname := $definedoc//odm:ItemDef[@OID=$strfoid]/@Name
    (: get the OID of the USUBJID variable (can be different from within DM) :)
    let $usubjidoid := (
        for $a in $definedoc//odm:ItemDef[@Name='USUBJID']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a  
    )
    for $record in $datasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$strfoid]]
        let $recnum := $record/@data:ItemGroupDataSeq
        let $strfvalue := $record/odm:ItemData[@ItemOID=$strfoid]/@Value
        (: and get the local USUBJID :)
        let $usubjid := $record/odm:ItemData[@ItemOID=$usubjidoid]/@Value
        (: look up the record in the DM dataset and get the RFSTDTC value :)
        let $rfstdtcvalue := $dmdatasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$dmusubjidoid and @Value=$usubjid]]/odm:ItemData[@ItemOID=$rfstdtcoid]/@Value
        (: in case there is no RFSTDTC value, STRF may not be populated :)
        where not($rfstdtcvalue) and $strfvalue
        return <warning rule="SD1030" dataset="{data($name)}" variable="{data($strfname)}" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">Value for {data($strfname)} is populated(value={data($strfvalue)}), when RFSTDTC is NULL in dataset {data($datasetname)}</warning>
]]></rulexquery>
</sdsrule>

<sdsrule id="SD1030-SD" last-update="2020-08-08" originator="FDA" standard="SDTM" requiresDomainOrDataset="Yes">
<ruledescription>Value for --STRF may not be populated, when RFSTDTC is NULL</ruledescription>
<ruledetaileddescription>Start Relative to Reference Period (--STRF) should not be populated for subjects with missing value for Reference Start Date/Time (RFSTDTC)</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>INTERVENTIONS</domain>
<domain>EVENTS</domain>
<domain>FINDINGS</domain>
<domain>SE</domain>
<domain>SV</domain>
<rulexquery><![CDATA[	
(: TODO: not tested yet - we need a good test submission :)
(: Rule SD1030 - Value for --STRF is populated, when RFSTDTC is NULL
Start Relative to Reference Period (--STRF) should not be populated for subjects with missing value for Reference Start Date/Time (RFSTDTC)
:)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink"; 
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
declare variable $datasetname external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: Get the location of the DM dataset :)
let $dmdataset := (
	if($defineversion = '2.1') then $definedoc//odm:ItemGroupDef[@Name='DM']/def21:leaf/@xlink:href
	else $definedoc//odm:ItemGroupDef[@Name='DM']/def:leaf/@xlink:href
)
let $dmdatasetdoc := doc(concat($base,$dmdataset))
(: get the OID of the RFSTDTC variable :)
let $rfstdtcoid := (
    for $a in $definedoc//odm:ItemDef[@Name='RFSTDTC']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name='DM']/odm:ItemRef/@ItemOID
        return $a   
)
(: get the OID of the USUBJID variable :)
let $dmusubjidoid := (
    for $a in $definedoc//odm:ItemDef[@Name='USUBJID']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name='DM']/odm:ItemRef/@ItemOID
        return $a  
)
(: iterate over all the provided datasets except within INTERVENTIONS, EVENTS, FINDINGS, SE, SV :)
for $dataset in $definedoc//odm:ItemGroupDef[@Name=$datasetname][upper-case(@def:Class)='INTERVENTIONS' or upper-case(@def:Class)='EVENTS' or upper-case(@def:Class)='FINDINGS' 
		or upper-case(./def21:Class/@Name)='INTERVENTIONS' or upper-case(./def21:Class/@Name)='EVENTS' or upper-case(./def21:Class/@Name)='FINDINGS' 
		or @Domain='SE' or @Domain='SV']
    let $name := $dataset/@Name
    let $dsname := (
		if($defineversion = '2.1') then $dataset/def21:leaf/@xlink:href
		else $dataset/def:leaf/@xlink:href
	)
    let $datasetdoc := (
		if($dsname) then doc(concat($base,$dsname))
		else ()
	)
    (: get the OID of the --STRF variable (if any) :)
    let $strfoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'STRF')]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a 
    )
    let $strfname := $definedoc//odm:ItemDef[@OID=$strfoid]/@Name
    (: get the OID of the USUBJID variable (can be different from within DM) :)
    let $usubjidoid := (
        for $a in $definedoc//odm:ItemDef[@Name='USUBJID']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a  
    )
    for $record in $datasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$strfoid]]
        let $recnum := $record/@data:ItemGroupDataSeq
        let $strfvalue := $record/odm:ItemData[@ItemOID=$strfoid]/@Value
        (: and get the local USUBJID :)
        let $usubjid := $record/odm:ItemData[@ItemOID=$usubjidoid]/@Value
        (: look up the record in the DM dataset and get the RFSTDTC value :)
        let $rfstdtcvalue := $dmdatasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$dmusubjidoid and @Value=$usubjid]]/odm:ItemData[@ItemOID=$rfstdtcoid]/@Value
        (: in case there is no RFSTDTC value, STRF may not be populated :)
        where not($rfstdtcvalue) and $strfvalue
        return <warning rule="SD1030" dataset="{data($name)}" variable="{data($strfname)}" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">Value for {data($strfname)} is populated(value={data($strfvalue)}), when RFSTDTC is NULL in dataset {data($datasetname)}</warning>	
]]></rulexquery>
</sdsrule>

<!-- 2020-08-08: rule SD1031: End Relative to Reference Period (-ENRF) should not be populated for subjects with missing value for Reference End Date/Time (RFENDTC) -->
<sdsrule id="SD1031" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>Value for --ENRF may not be populated, when RFSTDTC is NULL</ruledescription>
<ruledetaileddescription>End Relative to Reference Period (--ENRF) should not be populated for subjects with missing value for Reference End Date/Time (RFENDTC)</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>INTERVENTIONS</domain>
<domain>EVENTS</domain>
<domain>FINDINGS</domain>
<domain>SE</domain>
<domain>SV</domain>
<rulexquery><![CDATA[	
(: TODO: not well tested yet - we need a good test submission :)
(: End Relative to Reference Period (--ENRF) should not be populated for subjects with missing value for Reference End Date/Time (RFENDTC)
:)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink"; 
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(:  declare variable $datasetname external; :)
(: let $base := '/db/fda_submissions/cdisc01/' 
let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: Get the location of the DM dataset :)
let $dmdataset := (
	if($defineversion = '2.1') then $definedoc//odm:ItemGroupDef[@Name='DM']/def21:leaf/@xlink:href
	else $definedoc//odm:ItemGroupDef[@Name='DM']/def:leaf/@xlink:href
)
let $dmdatasetdoc := doc(concat($base,$dmdataset))
(: get the OID of the RFENDTC variable :)
let $rfendtcoid := (
    for $a in $definedoc//odm:ItemDef[@Name='RFENDTC']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name='DM']/odm:ItemRef/@ItemOID
        return $a   
)
(: get the OID of the USUBJID variable :)
let $dmusubjidoid := (
    for $a in $definedoc//odm:ItemDef[@Name='USUBJID']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name='DM']/odm:ItemRef/@ItemOID
        return $a  
)
(: iterate over all the datasets within INTERVENTIONS, EVENTS, FINDINGS, SE, SV :)
for $dataset in $definedoc//odm:ItemGroupDef[upper-case(@def:Class)='INTERVENTIONS' or upper-case(@def:Class)='EVENTS' or upper-case(@def:Class)='FINDINGS' 
		or upper-case(./def21:Class/@Name)='INTERVENTIONS' or upper-case(./def21:Class/@Name)='EVENTS' or upper-case(./def21:Class/@Name)='FINDINGS'
		or @Domain='SE' or @Domain='SV']
    let $name := $dataset/@Name
    let $dsname := (
		if($defineversion = '2.1') then $dataset/def21:leaf/@xlink:href
		else $dataset/def:leaf/@xlink:href
	)
    let $datasetdoc := (
		if($dsname) then doc(concat($base,$dsname))
		else ()
	)
    (: get the OID of the --ENRF variable (if any) :)
    let $enrfoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'ENRF')]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a 
    )
    (: also get the name :)
    let $enrfname := $definedoc//odm:ItemDef[@OID=$enrfoid]/@Name
    (: get the OID of the USUBJID variable (can be different from within DM) :)
    let $usubjidoid := (
        for $a in $definedoc//odm:ItemDef[@Name='USUBJID']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a  
    )
    for $record in $datasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$enrfoid]]
        (: get record number and --ENRF value :)
        let $recnum := $record/@data:ItemGroupDataSeq
        let $enrfvalue := $record/odm:ItemData[@ItemOID=$enrfoid]/@Value
        (: and get the local USUBJID :)
        let $usubjid := $record/odm:ItemData[@ItemOID=$usubjidoid]/@Value
        (: look up the record in the DM dataset and get the RFSTDTC value :)
        let $rfendtcvalue := $dmdatasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$dmusubjidoid and @Value=$usubjid]]/odm:ItemData[@ItemOID=$rfendtcoid]/@Value
        (: in case there is no ENSTDTC value, ENRF may not be populated :)
        where not($rfendtcvalue) and $enrfvalue
        return <warning rule="SD1031" dataset="{data($name)}" variable="{data($enrfname)}" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">Value for {data($enrfname)} is populated(value={data($enrfvalue)}), when RFENDTC is NULL in dataset {data($name)}</warning>	
]]>
</rulexquery>
</sdsrule>

<!-- 2020-08-08: rule SD1031: End Relative to Reference Period (-ENRF) should not be populated for subjects with missing value for Reference End Date/Time (RFENDTC) -->
<sdsrule id="SD1031-SD" last-update="2020-08-08" originator="FDA" standard="SDTM" requiresDomainOrDataset="Yes">
<ruledescription>Value for --ENRF may not be populated, when RFENDTC is NULL</ruledescription>
<ruledetaileddescription>End Relative to Reference Period (--ENRF) should not be populated for subjects with missing value for Reference End Date/Time (RFENDTC)</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>INTERVENTIONS</domain>
<domain>EVENTS</domain>
<domain>FINDINGS</domain>
<domain>SE</domain>
<domain>SV</domain>
<rulexquery><![CDATA[	
(: TODO: not well tested yet - we need a good test submission :)
(: End Relative to Reference Period (--ENRF) should not be populated for subjects with missing value for Reference End Date/Time (RFENDTC)
:)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink"; 
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
declare variable $datasetname external; 
(: let $base := '/db/fda_submissions/cdisc01/' 
let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: Get the location of the DM dataset :)
let $dmdataset := (
	if($defineversion = '2.1') then $definedoc//odm:ItemGroupDef[@Name='DM']/def21:leaf/@xlink:href
	else $definedoc//odm:ItemGroupDef[@Name='DM']/def:leaf/@xlink:href
)
let $dmdatasetdoc := doc(concat($base,$dmdataset))
(: get the OID of the RFENDTC variable :)
let $rfendtcoid := (
    for $a in $definedoc//odm:ItemDef[@Name='RFENDTC']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name='DM']/odm:ItemRef/@ItemOID
        return $a   
)
(: get the OID of the USUBJID variable :)
let $dmusubjidoid := (
    for $a in $definedoc//odm:ItemDef[@Name='USUBJID']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name='DM']/odm:ItemRef/@ItemOID
        return $a  
)
(: iterate over all the provided datasets within INTERVENTIONS, EVENTS, FINDINGS, SE, SV :)
for $dataset in $definedoc//odm:ItemGroupDef[@Name=$datasetname][upper-case(@def:Class)='INTERVENTIONS' or upper-case(@def:Class)='EVENTS' or upper-case(@def:Class)='FINDINGS' 
		or upper-case(./def21:Class/@Name)='INTERVENTIONS' or upper-case(./def21:Class/@Name)='EVENTS' or upper-case(./def21:Class/@Name)='FINDINGS' 
		or @Domain='SE' or @Domain='SV']
    let $name := $dataset/@Name
    let $dsname := (
		if($defineversion = '2.1') then $dataset/def21:leaf/@xlink:href
		else $dataset/def:leaf/@xlink:href
	)
    let $datasetdoc := (
		if($dsname) then doc(concat($base,$dsname))
		else ()
	)
    (: get the OID of the --ENRF variable (if any) :)
    let $enrfoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'ENRF')]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a 
    )
    (: also get the name :)
    let $enrfname := $definedoc//odm:ItemDef[@OID=$enrfoid]/@Name
    (: get the OID of the USUBJID variable (can be different from within DM) :)
    let $usubjidoid := (
        for $a in $definedoc//odm:ItemDef[@Name='USUBJID']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a  
    )
    for $record in $datasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$enrfoid]]
        (: get record number and --ENRF value :)
        let $recnum := $record/@data:ItemGroupDataSeq
        let $enrfvalue := $record/odm:ItemData[@ItemOID=$enrfoid]/@Value
        (: and get the local USUBJID :)
        let $usubjid := $record/odm:ItemData[@ItemOID=$usubjidoid]/@Value
        (: look up the record in the DM dataset and get the RFSTDTC value :)
        let $rfendtcvalue := $dmdatasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$dmusubjidoid and @Value=$usubjid]]/odm:ItemData[@ItemOID=$rfendtcoid]/@Value
        (: in case there is no ENSTDTC value, ENRF may not be populated :)
        where not($rfendtcvalue) and $enrfvalue
        return <warning rule="SD1031" dataset="{data($name)}" variable="{data($enrfname)}" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">Value for {data($enrfname)} is populated(value={data($enrfvalue)}), when RFENDTC is NULL in dataset {data($name)}</warning>	
]]>
</rulexquery>
</sdsrule>

<sdsrule id="SD0003" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>Value of *DTC variable must be a valid ISO 8601 date/datime value</ruledescription>
<ruledetaileddescription>Value of Dates/Time variables (*DTC) must conform to the ISO 8601 date/datetime international standard</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>ALL</domain>
<rulexquery><![CDATA[
(: Rule SD0003 - Invalid ISO 8601 value for *DTC variable :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
declare namespace xs="http://www.w3.org/2001/XMLSchema";
(: need a function substring-after-last - see e.g. http://www.xqueryfunctions.com/xq/functx_substring-after-last.html :)
declare namespace functx = "http://www.functx.com";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external;
declare variable $defineversion external;
(: FUNCTION DECLARATIONS :)
declare function functx:escape-for-regex
  ( $arg as xs:string? )  as xs:string {
   replace($arg,
           '(\.|\[|\]|\\|\||\-|\^|\$|\?|\*|\+|\{|\}|\(|\))','\\$1')
 } ;
declare function functx:substring-after-last
  ( $arg as xs:string? ,
    $delim as xs:string )  as xs:string {
   replace ($arg,concat('^.*',functx:escape-for-regex($delim)),'')
 } ;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: iterate over all dataset declarations uzsing the ItemGroupDef nodes in the define.xml :)
for $itemgroupdef in $definedoc//odm:ItemGroupDef
	let $itemgroupdefoid := $itemgroupdef/@OID
	let $itemgroupdefname := $itemgroupdef/@Name
	(: get the dataset document :)
	let $datasetlocation := (
		if($defineversion = '2.1') then $itemgroupdef/def21:leaf/@xlink:href
		else $itemgroupdef/def:leaf/@xlink:href
	)
	let $datasetdoc := (
		if($datasetlocation) then doc(concat($base,$datasetlocation))
		else ()
	)
	(: get the Items for which the name ends with DTC :)
	for $itemoid in $itemgroupdef/odm:ItemRef/@ItemOID
		for $itemdefdatetimetype in $itemgroupdef/../odm:ItemDef[@OID=$itemoid][ends-with(@Name,'DTC')]
			let $itemoiddtc := $itemdefdatetimetype/@OID
			let $itemname := $itemgroupdef/../odm:ItemDef[@OID=$itemoid]/@Name
			(: and now find them in the dataset itself :)
			for $itemdata in $datasetdoc//odm:ItemData[@ItemOID=$itemoiddtc]
				let $recnum := $itemdata/../@data:ItemGroupDataSeq
				let $value := $itemdata/@Value
				
				(: allow for partial dates and datetimes :)
				let $testvalue :=
					(: only the year is given :)
					if(string-length($value) = 4) then concat($value,'-01-01')
					(: only the month is given :)
					else if(string-length($value) = 7) then concat($value,'-01')
					(: T is given but no hours - might bei invalid anyway :)
					else if(string-length($value) = 11) then concat($value,'00:00:00')
					(: only hour is given :)
					else if(string-length($value) = 13) then concat($value,':00:00')
					(: hour and minutes are given, but no seconds :)
					else if(string-length($value) = 16) then concat($value,':00')
					(: all ok anyway :)
					else ($value)
				where not($testvalue castable as xs:date) and not($testvalue castable as xs:dateTime )
				return <error rule="SD0003" dataset="{data($itemgroupdefname)}" variable="{data($itemname)}" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">invalid ISO-8601 value for variable {data($itemname)} value = {data($value)} in dataset {data($itemgroupdefname)}</error>	
]]></rulexquery>
</sdsrule>

<sdsrule id="SD0003-SD" last-update="2020-08-08" originator="FDA" standard="SDTM" requiresDomainOrDataset="Yes">
<ruledescription>Value of *DTC variable must be a valid ISO 8601 date/datime value</ruledescription>
<ruledetaileddescription>Value of Dates/Time variables (*DTC) must conform to the ISO 8601 date/datetime international standard</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>ALL</domain>
<rulexquery><![CDATA[
(: Rule SD0003 - Invalid ISO 8601 value for *DTC variable :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
declare namespace xs="http://www.w3.org/2001/XMLSchema";
(: need a function substring-after-last - see e.g. http://www.xqueryfunctions.com/xq/functx_substring-after-last.html :)
declare namespace functx = "http://www.functx.com";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external;
declare variable $defineversion external;
declare variable $datasetname external;
(: FUNCTION DECLARATIONS :)
declare function functx:escape-for-regex
  ( $arg as xs:string? )  as xs:string {
   replace($arg,
           '(\.|\[|\]|\\|\||\-|\^|\$|\?|\*|\+|\{|\}|\(|\))','\\$1')
 } ;
declare function functx:substring-after-last
  ( $arg as xs:string? ,
    $delim as xs:string )  as xs:string {
   replace ($arg,concat('^.*',functx:escape-for-regex($delim)),'')
 } ;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: iterate over all dataset declarations uzsing the ItemGroupDef nodes in the define.xml :)
for $itemgroupdef in $definedoc//odm:ItemGroupDef[@Name=$datasetname]
let $itemgroupdefoid := $itemgroupdef/@OID
let $itemgroupdefname := $itemgroupdef/@Name
(: get the dataset document :)
let $datasetlocation := (
	if($defineversion = '2.1') then $itemgroupdef/def21:leaf/@xlink:href
	else $itemgroupdef/def:leaf/@xlink:href
)
let $datasetdoc := (
	if($datasetlocation) then doc(concat($base,$datasetlocation))
	else ()
)
(: get the Items for which the name ends with DTC :)
for $itemoid in $itemgroupdef/odm:ItemRef/@ItemOID
    for $itemdefdatetimetype in $itemgroupdef/../odm:ItemDef[@OID=$itemoid][ends-with(@Name,'DTC')]
        let $itemoiddtc := $itemdefdatetimetype/@OID
        let $itemname := $itemgroupdef/../odm:ItemDef[@OID=$itemoid]/@Name
        (: and now find them in the dataset itself :)
        for $itemdata in $datasetdoc//odm:ItemData[@ItemOID=$itemoiddtc]
            let $recnum := $itemdata/../@data:ItemGroupDataSeq
            let $value := $itemdata/@Value
            
            (: allow for partial dates and datetimes :)
            let $testvalue :=
                (: only the year is given :)
                if(string-length($value) = 4) then concat($value,'-01-01')
                (: only the month is given :)
                else if(string-length($value) = 7) then concat($value,'-01')
                (: T is given but no hours - might bei invalid anyway :)
                else if(string-length($value) = 11) then concat($value,'00:00:00')
                (: only hour is given :)
                else if(string-length($value) = 13) then concat($value,':00:00')
                (: hour and minutes are given, but no seconds :)
                else if(string-length($value) = 16) then concat($value,':00')
                (: all ok anyway :)
                else ($value)
            where not($testvalue castable as xs:date) and not($testvalue castable as xs:dateTime )
            return <error rule="SD0003" dataset="{data($itemgroupdefname)}" variable="{data($itemname)}" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">Invalid ISO-8601 value for variable {data($itemname)} value = {data($value)} in dataset {data($itemgroupdefname)}</error>
]]></rulexquery>
</sdsrule>

<sdsrule id="SD1126" last-update="2020-08-08" originator="FDA" standard="SDTM" timeintensive="Yes">
<ruledescription>Value for --TPT must be consistent within --TPTNUM</ruledescription>
<ruledetaileddescription>All values of Planned Time Point Name (--TPT) variable should be the same for a given value of Planned Time Point Number (--TPTNUM) variable</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>INTERVENTIONS</domain>
<domain>EVENTS</domain>
<domain>FINDINGS</domain>
<rulexquery><![CDATA[	
(: Rule SD1126 - Inconsistent value for --TPT within --TPTNUM
All values of Planned Time Point Name (--TPT) variable should be the same for a given value of Planned Time Point Number (--TPTNUM) variable
:)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: iterate over all datasets :)
for $dataset in $definedoc//odm:ItemGroupDef[upper-case(@def:Class)='INTERVENTIONS' or upper-case(@def:Class)='EVENTS' or upper-case(@def:Class)='FINDINGS' or 
		upper-case(./def21:Class/@Name)='INTERVENTIONS' or upper-case(./def21:Class/@Name)='EVENTS' or upper-case(./def21:Class/@Name)='FINDINGS']
    let $name := $dataset/@Name
    let $datasetname := (
		if($defineversion = '2.1') then $dataset/def21:leaf/@xlink:href
		else $dataset/def:leaf/@xlink:href
	)
    let $datasetdoc := (
		if($datasetname) then doc(concat($base,$datasetname))
		else ()
	)
    (: and get the OIDs of --TPT and --TPTNUM :)
    let $tptoid := (
    for $a in $definedoc//odm:ItemDef[ends-with(@Name,'TPT')]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
    )
    let $tptname := $definedoc//odm:ItemDef[@OID=$tptoid]/@Name
    let $tptnumoid := (
    for $a in $definedoc//odm:ItemDef[ends-with(@Name,'TPTNUM')]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
    )
    let $tptnumname := $definedoc//odm:ItemDef[@OID=$tptnumoid]/@Name
    (: Get all unique values of --TPTNUM :)
    let $tptnumuniquevalues := distinct-values($datasetdoc//odm:ItemGroupData/odm:ItemData[@ItemOID=$tptnumoid]/@Value)
    (: iterate over the unique TPTNUM values and get the records :)
    for $tptnumuniquevalue in $tptnumuniquevalues
        (: get all the records :)
        let $uniquetptnumrecords := $datasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$tptnumoid][@Value=$tptnumuniquevalue]]
        (: get the unique values for TPT within each of the unique TPTNUM values, and count them :)
        let $uniquetptvalues := distinct-values($uniquetptnumrecords/odm:ItemData[@ItemOID=$tptoid]/@Value)
        let $uniquetptvaluescount := count($uniquetptvalues)
        (: the value of the count of unique TPT values for each TPTNUM must be 1 :)
        where not($uniquetptvaluescount = 1) 
        return <warning rule="SD1126" dataset="{data($name)}" variable="{data(tptname)}" rulelastupdate="2020-08-08">Inconsistent value for {data($tptname)} within {data($tptnumname)} in dataset {data($datasetname)}. {data($uniquetptvaluescount)} different {data($tptname)} values were found for {data($tptnumname)}={data($tptnumuniquevalue)}</warning>		
]]></rulexquery>
</sdsrule>

<sdsrule id="SD1126-SD" last-update="2020-08-08" originator="FDA" standard="SDTM" requiresDomainOrDataset="Yes">
<ruledescription>Value for --TPT must be consistent within --TPTNUM</ruledescription>
<ruledetaileddescription>All values of Planned Time Point Name (--TPT) variable should be the same for a given value of Planned Time Point Number (--TPTNUM) variable</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>INTERVENTIONS</domain>
<domain>EVENTS</domain>
<domain>FINDINGS</domain>
<rulexquery><![CDATA[	
(: Rule SD1126 - Inconsistent value for --TPT within --TPTNUM
All values of Planned Time Point Name (--TPT) variable should be the same for a given value of Planned Time Point Number (--TPTNUM) variable
:)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
declare variable $datasetname external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: iterate over all provided datasets :)
for $dataset in $definedoc//odm:ItemGroupDef[@Name=$datasetname][upper-case(@def:Class)='INTERVENTIONS' or upper-case(@def:Class)='EVENTS' or upper-case(@def:Class)='FINDINGS' or 
		upper-case(./def21:Class/@Name)='INTERVENTIONS' or upper-case(./def21:Class/@Name)='EVENTS' or upper-case(./def21:Class/@Name)='FINDINGS']
    let $name := $dataset/@Name
	let $dsname := (
		if($defineversion = '2.1') then $dataset/def21:leaf/@xlink:href
		else $dataset/def:leaf/@xlink:href
	)
    let $datasetdoc := (
		if($dsname) then doc(concat($base,$dsname))
		else ()
	)
    (: and get the OIDs of --TPT and --TPTNUM :)
    let $tptoid := (
    for $a in $definedoc//odm:ItemDef[ends-with(@Name,'TPT')]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
    )
    let $tptname := $definedoc//odm:ItemDef[@OID=$tptoid]/@Name
    let $tptnumoid := (
    for $a in $definedoc//odm:ItemDef[ends-with(@Name,'TPTNUM')]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
    )
    let $tptnumname := $definedoc//odm:ItemDef[@OID=$tptnumoid]/@Name
    (: Get all unique values of --TPTNUM :)
    let $tptnumuniquevalues := distinct-values($datasetdoc//odm:ItemGroupData/odm:ItemData[@ItemOID=$tptnumoid]/@Value)
    (: iterate over the unique TPTNUM values and get the records :)
    for $tptnumuniquevalue in $tptnumuniquevalues
        (: get all the records :)
        let $uniquetptnumrecords := $datasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$tptnumoid][@Value=$tptnumuniquevalue]]
        (: get the unique values for TPT within each of the unique TPTNUM values, and count them :)
        let $uniquetptvalues := distinct-values($uniquetptnumrecords/odm:ItemData[@ItemOID=$tptoid]/@Value)
        let $uniquetptvaluescount := count($uniquetptvalues)
        (: the value of the count of unique TPT values for each TPTNUM must be 1 :)
        where not($uniquetptvaluescount = 1) 
        return <warning rule="SD1126" dataset="{data($name)}" variable="{data(tptname)}" rulelastupdate="2020-08-08">Inconsistent value for {data($tptname)} within {data($tptnumname)} in dataset {data($datasetname)}. {data($uniquetptvaluescount)} different {data($tptname)} values were found for {data($tptnumname)}={data($tptnumuniquevalue)}</warning>	
]]></rulexquery>
</sdsrule>


<sdsrule id="SD1127" last-update="2020-08-08" originator="FDA" standard="SDTM" timeintensive="Yes">
<ruledescription>Value for --TPTNUM must be consistent within --TPT</ruledescription>
<ruledetaileddescription>All values of Planned Time Point Number (--TPTNUM) variable should be the same for a given value of Planned Time Point Name (--TPT) variable</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>INTERVENTIONS</domain>
<domain>FINDING</domain>
<domain>EVENTS</domain>
<rulexquery><![CDATA[	
(: Rule SD1127 - Inconsistent value for --TPTNUM within --TPT:
All values of Planned Time Point Number (--TPTNUM) variable should be the same for a given value of Planned Time Point Name (--TPT) variable :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: iterate over all datasets :)
for $dataset in $definedoc//odm:ItemGroupDef[upper-case(@def:Class)='INTERVENTIONS' or upper-case(@def:Class)='EVENTS' or upper-case(@def:Class)='FINDINGS' or 
		upper-case(./def21:Class/@Name)='INTERVENTIONS' or upper-case(./def21:Class/@Name)='EVENTS' or upper-case(./def21:Class/@Name)='FINDINGS']
    let $name := $dataset/@Name
    let $datasetname := (
		if($defineversion = '2.1') then $dataset/def21:leaf/@xlink:href
		else $dataset/def:leaf/@xlink:href
	)
    let $datasetdoc := (
		if($datasetname) then doc(concat($base,$datasetname))
		else ()
	)
    (: and get the OIDs of --TPT and --TPTNUM :)
    let $tptoid := (
    for $a in $definedoc//odm:ItemDef[ends-with(@Name,'TPT')]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
    )
    let $tptname := $definedoc//odm:ItemDef[@OID=$tptoid]/@Name
    let $tptnumoid := (
    for $a in $definedoc//odm:ItemDef[ends-with(@Name,'TPTNUM')]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
    )
    let $tptnumname := $definedoc//odm:ItemDef[@OID=$tptnumoid]/@Name
    (: Get all unique values of --TPT :)
    let $tptuniquevalues := distinct-values($datasetdoc//odm:ItemGroupData/odm:ItemData[@ItemOID=$tptoid]/@Value)
    (: iterate over the unique TPTNUM values and get the records :)
    for $tptuniquevalue in $tptuniquevalues
        (: get all the records :)
        let $uniquetptrecords := $datasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$tptoid][@Value=$tptuniquevalue]]
        (: get the unique values for TPTNUM within each of the unique TPT values, and count them :)
        let $uniquetptnumvalues := distinct-values($uniquetptrecords/odm:ItemData[@ItemOID=$tptnumoid]/@Value)
        let $uniquetptnumvaluescount := count($uniquetptnumvalues)
        (: the value of the count of unique TPT values for each TPTNUM must be 1 :)
        (: where not($uniquetptnumvaluescount = 1) :)
		where $uniquetptnumvaluescount > 1   (: take missing values into account :)
        return <warning rule="SD1127" dataset="{data($name)}" variable="{data($tptnumname)}" rulelastupdate="2020-08-08">Inconsistent value for {data($tptnumname)} within {data($tptname)} in dataset {data($datasetname)}. {data($uniquetptnumvaluescount)} different {data($tptnumname)} values were found for {data($tptname)}={data($tptuniquevalue)}</warning>	
]]></rulexquery>
</sdsrule>

<sdsrule id="SD1127-SD" last-update="2020-08-08" originator="FDA" standard="SDTM" requiresDomainOrDataset="Yes">
<ruledescription>Value for --TPTNUM must be consistent within --TPT</ruledescription>
<ruledetaileddescription>All values of Planned Time Point Number (--TPTNUM) variable should be the same for a given value of Planned Time Point Name (--TPT) variable</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>INTERVENTIONS</domain>
<domain>FINDING</domain>
<domain>EVENTS</domain>
<rulexquery><![CDATA[	
(: Rule SD1127 - Inconsistent value for --TPTNUM within --TPT:
All values of Planned Time Point Number (--TPTNUM) variable should be the same for a given value of Planned Time Point Name (--TPT) variable :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
declare variable $datasetname external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: iterate over all provided datasets :)
for $dataset in $definedoc//odm:ItemGroupDef[@Name=$datasetname][upper-case(@def:Class)='INTERVENTIONS' or upper-case(@def:Class)='EVENTS' or upper-case(@def:Class)='FINDINGS' or 
		upper-case(./def21:Class/@Name)='INTERVENTIONS' or upper-case(./def21:Class/@Name)='EVENTS' or upper-case(./def21:Class/@Name)='FINDINGS']
    let $name := $dataset/@Name
    let $dsname := (
		if($defineversion = '2.1') then $dataset/def21:leaf/@xlink:href
		else $dataset/def:leaf/@xlink:href
	)
    let $datasetdoc := (
		if($dsname) then doc(concat($base,$dsname))
		else ()
	)
    (: and get the OIDs of --TPT and --TPTNUM :)
    let $tptoid := (
    for $a in $definedoc//odm:ItemDef[ends-with(@Name,'TPT')]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
    )
    let $tptname := $definedoc//odm:ItemDef[@OID=$tptoid]/@Name
    let $tptnumoid := (
    for $a in $definedoc//odm:ItemDef[ends-with(@Name,'TPTNUM')]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
    )
    let $tptnumname := $definedoc//odm:ItemDef[@OID=$tptnumoid]/@Name
    (: Get all unique values of --TPT :)
    let $tptuniquevalues := distinct-values($datasetdoc//odm:ItemGroupData/odm:ItemData[@ItemOID=$tptoid]/@Value)
    (: iterate over the unique TPTNUM values and get the records :)
    for $tptuniquevalue in $tptuniquevalues
        (: get all the records :)
        let $uniquetptrecords := $datasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$tptoid][@Value=$tptuniquevalue]]
        (: get the unique values for TPTNUM within each of the unique TPT values, and count them :)
        let $uniquetptnumvalues := distinct-values($uniquetptrecords/odm:ItemData[@ItemOID=$tptnumoid]/@Value)
        let $uniquetptnumvaluescount := count($uniquetptnumvalues)
        (: the value of the count of unique TPT values for each TPTNUM must be 1 :)
        (: where not($uniquetptnumvaluescount = 1)  :)
		where $uniquetptnumvaluescount > 1  (: take missing values into account :)
        return <warning rule="SD1127" dataset="{data($name)}" variable="{data($tptnumname)}" rulelastupdate="2020-08-08">Inconsistent value for {data($tptnumname)} within {data($tptname)} in dataset {data($datasetname)}. {data($uniquetptnumvaluescount)} different {data($tptnumname)} values were found for {data($tptname)}={data($tptuniquevalue)}</warning>			
]]></rulexquery>
</sdsrule>

<!-- Rule SD2239: Inconsistent value for -TPT - Planned Time Point Name (-TPT) value must be consistent for all records with same Subject (USUBJID) and Assessment Date/Time (-DTC).
NOT IMPLEMENTED: this rule is NONSENSE -->

<sdsrule id="SD1125" last-update="2020-08-08" originator="FDA" standard="SDTM" timeintensive="Yes">
<ruledescription>Value for --TPT must be consistent within --ELTM</ruledescription>
<ruledetaileddescription>All values of Planned Elapsed Time (--ELTM) variable should be the same for a given value of Planned Time Point Name (--TPT) variable</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>INTERVENTIONS</domain>
<domain>EVENTS</domain>
<domain>FINDINGS</domain>
<rulexquery><![CDATA[	
(: Rule SD1125 - Inconsistent value for --TPT within --ELTM
All values of Planned Elapsed Time (--ELTM) variable should be the same for a given value of Planned Time Point Name (--TPT) variable (Interventions, Findings, Events)
OR: there is a 1:1 relationship
:)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: iterate over all datasets within INTERVENTIONS, EVENTS, FINDINGS :)
for $dataset in $definedoc//odm:ItemGroupDef[upper-case(@def:Class)='INTERVENTIONS' or upper-case(@def:Class)='EVENTS' or upper-case(@def:Class)='FINDINGS' 
		or upper-case(./def21:Class/@Name)='INTERVENTIONS' or upper-case(./def21:Class/@Name)='EVENTS' or upper-case(./def21:Class/@Name)='FINDINGS']
    let $name := $dataset/@Name
    let $datasetname := (
		if($defineversion = '2.1') then $dataset/def21:leaf/@xlink:href
		else $dataset/def:leaf/@xlink:href
	)
    let $datasetdoc := (
		if($datasetname) then doc(concat($base,$datasetname))
		else ()
	)
    (: and get the OIDs of --TPT and --ELTM :)
    let $tptoid := (
    for $a in $definedoc//odm:ItemDef[ends-with(@Name,'TPT')]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
    )
    let $tptname := $definedoc//odm:ItemDef[@OID=$tptoid]/@Name
    let $eltmoid := (
    for $a in $definedoc//odm:ItemDef[ends-with(@Name,'ELTM')]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
    )
    let $eltmname := $definedoc//odm:ItemDef[@OID=$eltmoid]/@Name
    (: Get all unique values of --TPT :)
    let $tptuniquevalues := distinct-values($datasetdoc//odm:ItemGroupData/odm:ItemData[@ItemOID=$tptoid]/@Value)
    (: iterate over the unique values and get the records :)
    for $tptuniquevalue in $tptuniquevalues
        (: get all the records :)
        let $uniquetptrecords := $datasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$tptoid][@Value=$tptuniquevalue]]
        let $uniquetptrecordcount := count($uniquetptrecords)
        (: get the unique values for ELTM within each of the unique TPT values, and count them :)
        let $uniqueeltmvalues := distinct-values($uniquetptrecords/odm:ItemData[@ItemOID=$eltmoid]/@Value)
        let $uniqueeltmvaluescount := count($uniqueeltmvalues)
        (: the value of the count of unique ELTM values for each TPT must be 1 :)
        where not($uniqueeltmvaluescount = 1)
        return <warning rule="SD1125" dataset="{data($name)}" variable="{data($eltmname)}" rulelastupdate="2020-08-08">Inconsistent value for {data($eltmname)} within {data($tptname)} in dataset {data($datasetname)}. {data($uniqueeltmvaluescount)} different {data($eltmname)} values were found for {data($tptname)}={data($tptuniquevalue)}</warning>		
]]></rulexquery>
</sdsrule>

<sdsrule id="SD1125-SD" last-update="2020-08-08" originator="FDA" standard="SDTM" requiresDomainOrDataset="Yes">
<ruledescription>Value for --TPT must be consistent within --ELTM</ruledescription>
<ruledetaileddescription>All values of Planned Elapsed Time (--ELTM) variable should be the same for a given value of Planned Time Point Name (--TPT) variable</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>INTERVENTIONS</domain>
<domain>EVENTS</domain>
<domain>FINDINGS</domain>
<rulexquery><![CDATA[	
(: Rule SD1125 - Inconsistent value for --TPT within --ELTM
All values of Planned Elapsed Time (--ELTM) variable should be the same for a given value of Planned Time Point Name (--TPT) variable (Interventions, Findings, Events)
OR: there is a 1:1 relationship
:)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
declare variable $datasetname external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: iterate over all datasets within INTERVENTIONS, EVENTS, FINDINGS :)
for $dataset in $definedoc//odm:ItemGroupDef[@Name=$datasetname][upper-case(@def:Class)='INTERVENTIONS' or upper-case(@def:Class)='EVENTS' or upper-case(@def:Class)='FINDINGS' 
		or upper-case(./def21:Class/@Name)='INTERVENTIONS' or upper-case(./def21:Class/@Name)='EVENTS' or upper-case(./def21:Class/@Name)='FINDINGS']
    let $name := $dataset/@Name
    let $dsname := (
		if($defineversion = '2.1') then $dataset/def21:leaf/@xlink:href
		else $dataset/def:leaf/@xlink:href
	)
    let $datasetdoc := (
		if($dsname) then doc(concat($base,$dsname))
		else ()
	)
    (: and get the OIDs of --TPT and --ELTM :)
    let $tptoid := (
    for $a in $definedoc//odm:ItemDef[ends-with(@Name,'TPT')]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
    )
    let $tptname := $definedoc//odm:ItemDef[@OID=$tptoid]/@Name
    let $eltmoid := (
    for $a in $definedoc//odm:ItemDef[ends-with(@Name,'ELTM')]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
    )
    let $eltmname := $definedoc//odm:ItemDef[@OID=$eltmoid]/@Name
    (: Get all unique values of --TPT :)
    let $tptuniquevalues := distinct-values($datasetdoc//odm:ItemGroupData/odm:ItemData[@ItemOID=$tptoid]/@Value)
    (: iterate over the unique values and get the records :)
    for $tptuniquevalue in $tptuniquevalues
        (: get all the records :)
        let $uniquetptrecords := $datasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$tptoid][@Value=$tptuniquevalue]]
        let $uniquetptrecordcount := count($uniquetptrecords)
        (: get the unique values for ELTM within each of the unique TPT values, and count them :)
        let $uniqueeltmvalues := distinct-values($uniquetptrecords/odm:ItemData[@ItemOID=$eltmoid]/@Value)
        let $uniqueeltmvaluescount := count($uniqueeltmvalues)
        (: the value of the count of unique ELTM values for each TPT must be 1 :)
        where not($uniqueeltmvaluescount = 1)
        return <warning rule="SD1125" dataset="{data($name)}" variable="{data($eltmname)}" rulelastupdate="2020-08-08">Inconsistent value for {data($eltmname)} within {data($tptname)} in dataset {data($datasetname)}. {data($uniqueeltmvaluescount)} different {data($eltmname)} values were found for {data($tptname)}={data($tptuniquevalue)}</warning>			
]]></rulexquery>
</sdsrule>

<sdsrule id="SD1009" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>The value of Element Code (ETCD) may not be more than 8 characters in length</ruledescription>
<ruledetaileddescription>The value of Element Code (ETCD) may not be more than 8 characters in length</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>SE</domain>
<rulexquery><![CDATA[
(: Rule SD1009 - Invalid value for ETCD:
The value of Element Code (ETCD) should be no more than 8 characters in length
:)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external;
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: Get the SE dataset :)
let $sedatasetname := (
	if($defineversion = '2.1') then $definedoc//odm:ItemGroupDef[@Name='SE']/def21:leaf/@xlink:href
	else $definedoc//odm:ItemGroupDef[@Name='SE']/def:leaf/@xlink:href
)
let $sedatasetdoc := (
	if($sedatasetname) then doc(concat($base,$sedatasetname))
	else ()
)
(: also need to find out the OID of ETCD :)
let $seetcdoid := (
        for $a in $definedoc//odm:ItemDef[@Name='ETCD']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name='SE']/odm:ItemRef/@ItemOID
        return $a
)
(: iterate over all records in the SE dataset :)
for $record in $sedatasetdoc//odm:ItemGroupData
    let $recnum := $record/@data:ItemGroupDataSeq
    (: Get the ETCD value :)
    let $etcdvalue := $record/odm:ItemData[@ItemOID=$seetcdoid]/@Value
    (: and check whether more than 8 characters :)
    where string-length($etcdvalue) > 8
    return <warning rule="SD1009" dataset="SE" variable="ETCD" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">Invalid value for ETCD in SE dataset {data($sedatasetname)} - Value {data($etcdvalue)} has more than 8 characters</warning>
]]></rulexquery>
</sdsrule>

<sdsrule id="SD1052" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>TAETORD must be unique within ARMCD</ruledescription>
<ruledetaileddescription>Order of Element within Arm (TAETORD) must have a unique value for a given value of Planned Arm Code (ARMCD) within the domain</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>TA</domain>
<rulexquery><![CDATA[
(: Rule SD1052 - Non-unique value for TAETORD within ARMCD:
Order of Element within Arm (TAETORD) must have a unique value for a given value of Planned Arm Code (ARMCD) within the domain
:)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external;
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: Get the TA dataset :)
let $tadatasetname := (
	if($defineversion = '2.1') then $definedoc//odm:ItemGroupDef[@Name='TA']/def21:leaf/@xlink:href
	else $definedoc//odm:ItemGroupDef[@Name='TA']/def:leaf/@xlink:href
)
let $tadatasetdoc := (
	if($tadatasetname) then doc(concat($base,$tadatasetname))
	else ()
)
(: also need to find out the OIDs of ARMCD and TAETORD :)
let $taarmcdoid := (
        for $a in $definedoc//odm:ItemDef[@Name='ARMCD']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name='TA']/odm:ItemRef/@ItemOID
        return $a
)
let $tataetordoid := (
        for $a in $definedoc//odm:ItemDef[@Name='TAETORD']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name='TA']/odm:ItemRef/@ItemOID
        return $a
)
(: iterate over all records in the TA dataset :)
for $record in $tadatasetdoc//odm:ItemGroupData
    let $recnum := $record/@data:ItemGroupDataSeq
    (: Get the value of ARMCD and TAETORD :)
    let $taarmcdvalue := $record/odm:ItemData[@ItemOID=$taarmcdoid]/@Value
    let $tataetordvalue := $record/odm:ItemData[@ItemOID=$tataetordoid]/@Value
    (: and have a second iteration over all subsequent record :)
    for $nextrecord in $tadatasetdoc//odm:ItemGroupData[@data:ItemGroupDataSeq > $recnum]
        let $recnumnext := $nextrecord/@data:ItemGroupDataSeq
        (: and look for duplicate values :)
        let $taarmcdvaluenext := $nextrecord/odm:ItemData[@ItemOID=$taarmcdoid]/@Value
        let $tataetordvaluenext := $nextrecord/odm:ItemData[@ItemOID=$tataetordoid]/@Value
        where $taarmcdvalue = $taarmcdvaluenext and $tataetordvalue = $tataetordvaluenext
    return <error rule="SD1052" dataset="TA" variable="TAETORD" rulelastupdate="2020-08-08" recordnumber="{data($recnumnext)}">Non-unique value for TAETORD within ARMCD. Records {data($recnum)} and {data($recnumnext)} have the same value for ARMCD={data($taarmcdvalue)} and TAETORD={data($tataetordvalue)} in TA dataset {data($tadatasetname)}</error>
]]></rulexquery>
</sdsrule>

<sdsrule originator="FDA" id="SD1267" standard="SDTM" last-update="2020-08-08">
	<ruledescription>TAETORD must be an integer</ruledescription>
	<ruledetaileddescription>Planned Order of Element within Arm (TAETORD) variable value must be an integer</ruledetaileddescription>
	<igversion>3.1.2</igversion>
	<igversion>3.1.3</igversion>
	<igversion>3.2</igversion>
	<domain>TA</domain>
	<synonym context="CDISC">CG0248</synonym>
	<rulexquery><![CDATA[
(: Rule SD1267 - TAETORD must be an integer :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external;
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: Get the TA dataset :)
let $tadatasetdef := $definedoc//odm:ItemGroupDef[@Name='TA']
let $tadatasetname := (
	if($defineversion = '2.1') then $tadatasetdef/def21:leaf/@xlink:href
	else $tadatasetdef/def:leaf/@xlink:href
)
let $tadatasetdoc := (
	if($tadatasetname) then doc(concat($base,$tadatasetname))
	else ()
)
(: also need to find the OID out of TAETORD :)
let $taetordoid := (
        for $a in $definedoc//odm:ItemDef[@Name='TAETORD']/@OID 
        where $a = $tadatasetdef/odm:ItemRef/@ItemOID
        return $a
)
(: iterate over all the records in the TA dataset :)
for $record in $tadatasetdoc//odm:ItemGroupData
    let $recnum := $record/@data:ItemGroupDataSeq
    (: get the values of ARM and of TAETORD :)
    let $taetord := $record/odm:ItemData[@ItemOID=$taetordoid]/@Value
    (: TAETORD is an integer :)
    where not($taetord castable as xs:integer)
        return <error rule="SD1267" dataset="TA" variable="TAETORD" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">TAETORD={data($taetord)} must be an integer</error>			
		]]>
	</rulexquery>
</sdsrule>

<sdsrule originator="FDA" id="SD1286" standard="SDTM" last-update="2020-08-08">
	<ruledescription>When TIVERS present in dataset then IETESTCD must be unique within TIVERS</ruledescription>
	<ruledetaileddescription>A value of Incl/Excl Criterion Short Name (IETESTCD) must be unique within a version of inclusion/exclusion criteria (TIVERS)</ruledetaileddescription>
	<igversion>3.1.2</igversion>
	<igversion>3.1.3</igversion>
	<igversion>3.2</igversion>
	<domain>TI</domain>
	<synonym context="CDISC">CG0255</synonym>
	<rulexquery><![CDATA[
(: Rule SD1286 - When TIVERS present in dataset then IETESTCD unique within TIVERS :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external;
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: Get the TI dataset :)
let $tidatasetdef := $definedoc//odm:ItemGroupDef[@Name='TI']
let $tidatasetname := (
	if($defineversion = '2.1') then $tidatasetdef/def21:leaf/@xlink:href
	else $tidatasetdef/def:leaf/@xlink:href
)
let $tidatasetdoc := (
	if($tidatasetname) then doc(concat($base,$tidatasetname))
	else ()
)
(: get the OID of IETESTCD and TIVERS variables (when present) :)
let $ietestcdoid := (
    for $a in $definedoc//odm:ItemDef[@Name='IETESTCD']/@OID 
        where $a = $tidatasetdef/odm:ItemRef/@ItemOID
        return $a
)
let $tiversoid := (
    for $a in $definedoc//odm:ItemDef[@Name='TIVERS']/@OID 
        where $a = $tidatasetdef/odm:ItemRef/@ItemOID
        return $a
)
(: iterate over all the records in TI :)
for $record in $tidatasetdoc[$ietestcdoid and $tiversoid]//odm:ItemGroupData
    let $recnum := $record/@data:ItemGroupDataSeq
    (: get the values of IETESTCD and of TIVERS (if present) :)
    let $ietestcd := $record/odm:ItemData[@ItemOID=$ietestcdoid]/@Value
    let $tivers := $record/odm:ItemData[@ItemOID=$tiversoid]/@Value
    (: iterate over all the subsequent record with the same TIVERS :)
    for $recordnext in $record/following-sibling::odm:ItemGroupData[odm:ItemData[@ItemOID=$tiversoid and @Value=$tivers]]
        let $recnumnext := $recordnext/@data:ItemGroupDataSeq
        (: get the value of IETESTCD in this subsequent record with the same value for TIVERS :)
        let $ietestcdnext := $recordnext/odm:ItemData[@ItemOID=$ietestcdoid]/@Value
        (: give an error when the 2 values of IETEST for the same value of TIVERS are identical :)
        where $ietestcd=$ietestcdnext
        return <error rule="SD1286" dataset="TI" variable="IETESTCD" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">Records {data($recnum)} and {data($recnumnext)} have the same value for IETESTCD='{data($ietestcd)}' and TIVERS={data($tivers)}</error>			
		]]>
	</rulexquery>
</sdsrule>

<sdsrule id="SD0020" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>Value for --PARMCD variable may not be longer than 8 characters</ruledescription>
<ruledetaileddescription>The value of Parameter Short Name (--PARMCD) variables should be no more than 8 characters in length</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>TS</domain>
<rulexquery><![CDATA[
(: Rule SD0020 - Invalid value for --PARMCD variable:
The value of Parameter Short Name (--PARMCD) variables should be no more than 8 characters in length :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
declare namespace xs="http://www.w3.org/2001/XMLSchema";
declare namespace functx = "http://www.functx.com";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external;
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: iterate over all TS datasets (there should be only one) :)
for $itemgroup in $definedoc//odm:ItemGroupDef[@Name='TS']
    (: Get the OID for the TSPARMCD variable :)
    let $testcdoid := (
        for $a in $definedoc//odm:ItemDef[@Name='TSPARMCD']/@OID
        where $a = $definedoc//odm:ItemGroupDef[@Name='TS']/odm:ItemRef/@ItemOID
        return $a
    )
    let $datasetname := (
		if($defineversion = '2.1') then $itemgroup/def21:leaf/@xlink:href
		else $itemgroup/def:leaf/@xlink:href
	)
    let $datasetdoc := (
		if($datasetname) then doc(concat($base,$datasetname))
		else ()
	)
    (: iterate over all the records within the TS dataset :)
    for $record in $datasetdoc//odm:ItemGroupData
        (: get the record number :)
        let $recnum := $record/@data:ItemGroupDataSeq
        (: and get the value of the TSPARM variable :)
        let $testvalue := $record/odm:ItemData[@ItemOID=$testcdoid]/@Value
        (: when it has more than 8 characters, give an error :)
        where string-length($testvalue) > 8
        return <error rule="SD0020" dataset="TS" variable="TSPARMCD" recordnumber="{data($recnum)}" rulelastupdate="2020-08-08">The value for TSPARMCD in dataset {data($datasetname)} has more than 8 characters: '{data($testvalue)}' was found</error>
]]></rulexquery>
</sdsrule>

<sdsrule id="SD0019" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>The value in TSPARM may not be longer than 40 characters</ruledescription>
<ruledetaileddescription>The value of Parameter (--PARM) variable should be no more than 40 characters in length</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>TS</domain>
<rulexquery><![CDATA[
(: Rule SD0019 - Invalid value for --PARM variable:
The value of Parameter (--PARM) variable should be no more than 40 characters in length :)
(: This rule is really outdated! Even CDISC has published test names with > 40 characters. The rule is a relict of the ancient XPT format. :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
declare namespace xs="http://www.w3.org/2001/XMLSchema";
declare namespace functx = "http://www.functx.com";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external;
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: iterate over all TS datasets (there should be only one) :)
for $itemgroup in $definedoc//odm:ItemGroupDef[@Name='TS']
    (: Get the OID for the TSPARM variable :)
    let $testoid := (
        for $a in $definedoc//odm:ItemDef[@Name='TSPARM']/@OID
        where $a = $definedoc//odm:ItemGroupDef[@Name='TS']/odm:ItemRef/@ItemOID
        return $a
    )
    (: we of course only look into datasets where there really is a --TEST :)
    let $datasetname := (
		if($defineversion = '2.1') then $itemgroup/def21:leaf/@xlink:href
		else $itemgroup/def:leaf/@xlink:href
	)
    let $datasetdoc := (
		if($datasetname) then doc(concat($base,$datasetname))
		else()
	)
    (: iterate over all the records within the TS dataset :)
    for $record in $datasetdoc//odm:ItemGroupData
        (: get the record number :)
        let $recnum := $record/@data:ItemGroupDataSeq
        (: and get the value of the TSPARM variable :)
        let $testvalue := $record/odm:ItemData[@ItemOID=$testoid]/@Value
        (: when it has more than 40 characters, give an error :)
        where string-length($testvalue) > 40
        return <error rule="SD0019" dataset="TS" variable="TSPARM" recordnumber="{data($recnum)}" rulelastupdate="2020-08-08">The value for TSPARM in dataset {data($datasetname)} has more than 40 characters: '{data($testvalue)}' was found</error>
]]></rulexquery>
</sdsrule>


<sdsrule id="SD2018" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>TSVALNF must be populated when TSVAL is missing</ruledescription>
<ruledetaileddescription>Value for Parameter Null Flavor (TSVALNF) variable must be populated, when Parameter Value (TSVAL) variable value is missing</ruledetaileddescription>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>TS</domain>
<rulexquery><![CDATA[	
(: Rule SD2018 - Missing TSVALNF value
Value for Parameter Null Flavor (TSVALNF) variable must be populated, when Parameter Value (TSVAL) variable value is missing.
:)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: Get the TS dataset :)
let $tsdataset := $definedoc//odm:ItemGroupDef[@Name='TS']
let $tsdatasetname := (
	if($defineversion = '2.1') then $tsdataset/def21:leaf/@xlink:href
	else $tsdataset/def:leaf/@xlink:href
)
let $tsdatasetdoc := (
	if($tsdatasetname) then doc(concat($base,$tsdatasetname))
	else ()
)
(: Get the OIDs of TSVAL and TSVALNF :)
let $tsvaloid := (
    for $a in $definedoc//odm:ItemDef[ends-with(@Name,'TSVAL')]/@OID 
    where $a = $definedoc//odm:ItemGroupDef[@Name='TS']/odm:ItemRef/@ItemOID
    return $a
)
let $tsvalnfoid := (
    for $a in $definedoc//odm:ItemDef[ends-with(@Name,'TSVALNF')]/@OID 
    where $a = $definedoc//odm:ItemGroupDef[@Name='TS']/odm:ItemRef/@ItemOID
    return $a
)
(: Iterate over all the records in the TS dataset :)
for $record in $tsdatasetdoc//odm:ItemGroupData
    let $recnum := $record/@data:ItemGroupDataSeq
    (: get the value of TSVAL and TSVALNF (if any) :)
    let $tsvalvalue := $record/odm:ItemData[@ItemOID=$tsvaloid]/@Value
    let $tsvalnfvalue := $record/odm:ItemData[@ItemOID=$tsvalnfoid]/@Value
    (: check whether TSVAL is populated except for when TSVALNF is populated :)
    where not($tsvalvalue) and not($tsvalnfvalue)
    return <error rule="SD2018" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">Missing TSVALNF value</error>	
]]></rulexquery>
</sdsrule>

<sdsrule id="SD2017" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>TSVAL may only be null when TSVALNF is populated</ruledescription>
<ruledetaileddescription>Value for Parameter Value (TSVAL) variable must be populated. TSVAL can only be null when Parameter Null Flavor (TSVALNF) variable value is populated</ruledetaileddescription>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>TS</domain>
<rulexquery><![CDATA[	
(: Rule SD2017 - Missing TSVAL value
Value for Parameter Value (TSVAL) variable must be populated. TSVAL can only be null when Parameter Null Flavor (TSVALNF) variable value is populated.
:)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: Get the TS dataset :)
let $tsdataset := $definedoc//odm:ItemGroupDef[@Name='TS']
let $tsdatasetname := (
	if($defineversion = '2.1') then $tsdataset/def21:leaf/@xlink:href
	else $tsdataset/def:leaf/@xlink:href
)
let $tsdatasetdoc := (
	if($tsdatasetname) then doc(concat($base,$tsdatasetname))
	else ()
)
(: Get the OIDs of TSVAL and TSVALNF :)
let $tsvaloid := (
    for $a in $definedoc//odm:ItemDef[ends-with(@Name,'TSVAL')]/@OID 
    where $a = $definedoc//odm:ItemGroupDef[@Name='TS']/odm:ItemRef/@ItemOID
    return $a
)
let $tsvalnfoid := (
    for $a in $definedoc//odm:ItemDef[ends-with(@Name,'TSVALNF')]/@OID 
    where $a = $definedoc//odm:ItemGroupDef[@Name='TS']/odm:ItemRef/@ItemOID
    return $a
)
(: Iterate over all the records in the TS dataset :)
for $record in $tsdatasetdoc//odm:ItemGroupData
    let $recnum := $record/@data:ItemGroupDataSeq
    (: get the value of TSVAL and TSVALNF (if any) :)
    let $tsvalvalue := $record/odm:ItemData[@ItemOID=$tsvaloid]/@Value
    let $tsvalnfvalue := $record/odm:ItemData[@ItemOID=$tsvalnfoid]/@Value
    (: check whether TSVAL is populated except for when TSVALNF is populated :)
    where not($tsvalnfvalue) and not($tsvalvalue)
    return <error rule="SD2017" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">Missing TSVAL value</error>
]]></rulexquery>
</sdsrule>

<sdsrule originator="FDA" id="SD1278" standard="SDTM" last-update="2020-08-08">
	<ruledescription>Value for TSVALCD must be consistent within TSVAL - When TSVAL != null and TSVALCD != null then TSVALCD and TSVAL have a one-to-one relationship.</ruledescription>
	<ruledetaileddescription>When TSVAL != null and TSVALCD != null then TSVALCD and TSVAL have a one-to-one relationship</ruledetaileddescription>
	<igversion>3.1.3</igversion>
	<igversion>3.2</igversion>
	<domain>TS</domain>
	<synonym context="CDISC">CG0265</synonym>
	<rulexquery><![CDATA[
(: Rule SD1278 - When TSVAL != null and TSVALCD != null then TSVALCD and TSVAL have a one-to-one relationship :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdiscpilot01/' :)
(: let $define := 'define_2_0.xml' :)
let $definedoc := doc(concat($base,$define)) 
(: iterate over all TS datasets - there should only be one :)
for $datasetdef in $definedoc//odm:ItemGroupDef[@Name='TS']
    let $name := $datasetdef/@Name
    let $datasetname := (
		if($defineversion = '2.1') then $datasetdef/def21:leaf/@xlink:href
		else $datasetdef/def:leaf/@xlink:href
	)
    let $datasetdoc := (
		if($datasetname) then doc(concat($base,$datasetname))
		else ()
	)
    (: and get the OIDs of TSVAL and TSVALCD :)
    let $tsvaloid := (
    for $a in $definedoc//odm:ItemDef[@Name='TSVAL']/@OID 
        where $a = $datasetdef/odm:ItemRef/@ItemOID
        return $a
    )
    let $tsvalcdoid := (
    for $a in $definedoc//odm:ItemDef[@Name='TSVALCD']/@OID 
        where $a = $datasetdef/odm:ItemRef/@ItemOID
        return $a
    )
    (: Get all unique values of TSVALCD :)
    let $tsvalcduniquevalues := distinct-values($datasetdoc//odm:ItemGroupData/odm:ItemData[@ItemOID=$tsvalcdoid]/@Value)
    (: iterate over the unique TSVALCD values and get the records :)
    for $tsvalcduniquevalue in $tsvalcduniquevalues
        (: get all the records :)
        let $uniquetsvalcdrecords := $datasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$tsvalcdoid][@Value=$tsvalcduniquevalue]]
        (: get the unique values for TSVAL within each of the unique TSVALCD values, and count them :)
        let $uniquetsvalvalues := distinct-values($uniquetsvalcdrecords/odm:ItemData[@ItemOID=$tsvaloid]/@Value)
        let $uniquetsvalvaluescount := count($uniquetsvalvalues)
        (: the value of the count of unique TSVAL values for each TSVALCD must be 1 :)
        where not($uniquetsvalvaluescount = 1) 
        return <error rule="SD1278" dataset="{data($name)}" variable="TSVALCD" rulelastupdate="2020-08-08">Inconsistent value for TSVAL within TSVALCD in dataset {data($datasetname)}. {data($uniquetsvalvaluescount)} different TSVAL values were found for TSVALCD={data($tsvalcduniquevalue)}</error>	
		]]>
	</rulexquery>
</sdsrule>
	
<sdsrule id="SD2252" last-update="2020-08-08" originator="FDA" standard="SDTM" webservice="Uses NLM UNII Web Service">
<ruledescription>CURTRT: TSVAL and TSVALCD values must be populated from the same name record in FDA Substance Registration System (SRS)</ruledescription>
<ruledetaileddescription>CURTRT: TSVAL and TSVALCD values must be populated from the same name record in FDA Substance Registration System (SRS)</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>TS</domain>
<rulexquery><![CDATA[
(: Rule SD2252 - TSVAL/TSVALCD value mismatch for CURTRT:
TSVAL and TSVALCD values must be populated from the same name record in FDA Substance Registration System (SRS)
:)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
declare namespace xs="http://www.w3.org/2001/XMLSchema";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external;
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: get the TS dataset :)
let $tsdataset := $definedoc//odm:ItemGroupDef[@Name='TS']
let $tsdatasetname := (
	if($defineversion = '2.1') then $tsdataset/def:leaf/@xlink:href
	else $tsdataset/def:leaf/@xlink:href
)
let $tsdatasetdoc := (
	if($tsdatasetname) then doc(concat($base,$tsdatasetname))
	else ()
)
(: get the OID of the TSPARMCD, TSVAL and TSVALCD variables :)
let $tsparmcdoid := (
    for $a in $definedoc//odm:ItemDef[@Name='TSPARMCD']/@OID 
    where $a = $definedoc//odm:ItemGroupDef[@Name='TS']/odm:ItemRef/@ItemOID
    return $a
)
let $tsvaloid := (
    for $a in $definedoc//odm:ItemDef[@Name='TSVAL']/@OID 
    where $a = $definedoc//odm:ItemGroupDef[@Name='TS']/odm:ItemRef/@ItemOID
    return $a
)
let $tsvalcdoid := (
    for $a in $definedoc//odm:ItemDef[@Name='TSVALCD']/@OID 
    where $a = $definedoc//odm:ItemGroupDef[@Name='TS']/odm:ItemRef/@ItemOID
    return $a
)
(: get all the records that have TSPARMCD=CURTRT :)
for $record in $tsdatasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$tsparmcdoid and @Value='CURTRT']]
    let $recnum := $record/@data:ItemGroupDataSeq
    (: get the value of the TSVAL and TSVALCD variable :)
    let $tsvalcdvalue := $record/odm:ItemData[@ItemOID=$tsvalcdoid]/@Value
    let $tsvalvalue := $record/odm:ItemData[@ItemOID=$tsvaloid]/@Value
    (: invoke the NLM webservice to check whether this is a valid preferred term :)
    let $webservice := concat('https://dailymed.nlm.nih.gov/dailymed/services/v2/uniis.xml?unii_code=',$tsvalcdvalue)
    (: the webservice returns an XML document with the structure /uniis/unii/unii_code and /uniis/unii/active_moiety :)
    let $webserviceresult := doc($webservice)
    let $activemoiety := $webserviceresult/uniis/unii/active_moiety
    (: the value of "active_moiety" must correspond to the value of TSVAL :)
    where not($activemoiety=$tsvalvalue)
    return <error rule="SD2252" dataset="TS" variable="TSVALCD" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">TSVAL/TSVALCD value mismatch for CURTRT, TSVALCD={data($tsvalcdvalue)}, TSVAL={data($tsvalvalue)} - NLM webservice provides active moeity={data($activemoiety)}</error>
]]></rulexquery>
</sdsrule>

<sdsrule id="SD2255" last-update="2020-08-08" originator="FDA" standard="SDTM" webservice="Uses NLM UNII Web Service">
<ruledescription>COMPTRT: TSVAL and TSVALCD values must be populated from the same name record in FDA Substance Registration System (SRS)</ruledescription>
<ruledetaileddescription>COMPTRT: TSVAL and TSVALCD values must be populated from the same name record in FDA Substance Registration System (SRS)</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>TS</domain>
<rulexquery><![CDATA[
(: Rule SD2255 - TSVAL/TSVALCD value mismatch for COMPTRT:
TSVAL and TSVALCD values must be populated from the same name record in FDA Substance Registration System (SRS)
:)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external;
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: get the TS dataset :)
let $tsdataset := $definedoc//odm:ItemGroupDef[@Name='TS']
let $tsdatasetname := (
	if($defineversion = '2.1') then $tsdataset/def:leaf/@xlink:href
	else $tsdataset/def:leaf/@xlink:href
)
let $tsdatasetdoc := (
	if($tsdatasetname) then doc(concat($base,$tsdatasetname))
	else ()
)
(: get the OID of the TSPARMCD, TSVAL and TSVALCD variables :)
let $tsparmcdoid := (
    for $a in $definedoc//odm:ItemDef[@Name='TSPARMCD']/@OID 
    where $a = $definedoc//odm:ItemGroupDef[@Name='TS']/odm:ItemRef/@ItemOID
    return $a
)
let $tsvaloid := (
    for $a in $definedoc//odm:ItemDef[@Name='TSVAL']/@OID 
    where $a = $definedoc//odm:ItemGroupDef[@Name='TS']/odm:ItemRef/@ItemOID
    return $a
)
let $tsvalcdoid := (
    for $a in $definedoc//odm:ItemDef[@Name='TSVALCD']/@OID 
    where $a = $definedoc//odm:ItemGroupDef[@Name='TS']/odm:ItemRef/@ItemOID
    return $a
)
(: get all the records that have TSPARMCD=COMPTRT :)
for $record in $tsdatasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$tsparmcdoid and @Value='COMPTRT']]
    let $recnum := $record/@data:ItemGroupDataSeq
    (: get the value of the TSVAL and TSVALCD variable :)
    let $tsvalcdvalue := $record/odm:ItemData[@ItemOID=$tsvalcdoid]/@Value
    let $tsvalvalue := $record/odm:ItemData[@ItemOID=$tsvaloid]/@Value
    (: invoke the NLM webservice to check whether this is a valid preferred term :)
    let $webservice := concat('https://dailymed.nlm.nih.gov/dailymed/services/v2/uniis.xml?unii_code=',$tsvalcdvalue)
    (: the webservice returns an XML document with the structure /uniis/unii/unii_code and /uniis/unii/active_moiety :)
    let $webserviceresult := doc($webservice)
    let $activemoiety := $webserviceresult/uniis/unii/active_moiety
    (: the value of "active_moiety" must correspond to the value of TSVAL :)
    where not($activemoiety=$tsvalvalue)
    return <error rule="SD2255" dataset="TS" variable="TSVALCD" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">TSVAL/TSVALCD value mismatch for COMPTRT, TSVALCD={data($tsvalcdvalue)}, TSVAL={data($tsvalvalue)} - NLM webservice provides active moeity={data($activemoiety)}</error>
]]></rulexquery>
</sdsrule>

<sdsrule id="SD2265" last-update="2020-08-08" originator="FDA" standard="SDTM" webservice="Uses NLM NDF-RT Web Service">
<ruledescription>PCLAS: TSVAL and TSVALCD values must be populated from the same  record in NDF-RT</ruledescription>
<ruledetaileddescription>PCLAS: TSVAL and TSVALCD values must be populated from the same  record in NDF-RT</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>TS</domain>
<rulexquery><![CDATA[
(: Rule SD2265 - TSVAL/TSVALCD value mismatch for PCLAS:
TSVAL and TSVALCD values must be populated from the same  record in NDF-RT
:)
(: uses NLM web service - see http://rxnav.nlm.nih.gov/NdfrtAPIREST.html
e.g. http://rxnav.nlm.nih.gov/REST/Ndfrt/allInfo.xml?nui=N0000175565
:)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: get the TS dataset :)
let $tsdataset := $definedoc//odm:ItemGroupDef[@Name='TS']
let $tsdatasetname := (
	if($defineversion = '2.1') then $tsdataset/def21:leaf/@xlink:href
	else $tsdataset/def:leaf/@xlink:href
)
let $tsdatasetdoc := (
	if($tsdatasetname) then doc(concat($base,$tsdatasetname))
	else ()
)
(: get the OID of the TSPARMCD, TSVAL and TSVALCD variables :)
let $tsparmcdoid := (
    for $a in $definedoc//odm:ItemDef[@Name='TSPARMCD']/@OID 
    where $a = $definedoc//odm:ItemGroupDef[@Name='TS']/odm:ItemRef/@ItemOID
    return $a
)
let $tsvaloid := (
    for $a in $definedoc//odm:ItemDef[@Name='TSVAL']/@OID 
    where $a = $definedoc//odm:ItemGroupDef[@Name='TS']/odm:ItemRef/@ItemOID
    return $a
)
let $tsvalcdoid := (
    for $a in $definedoc//odm:ItemDef[@Name='TSVALCD']/@OID 
    where $a = $definedoc//odm:ItemGroupDef[@Name='TS']/odm:ItemRef/@ItemOID
    return $a
)
(: get the records with TSPARMCD=PCLAS :)
for $pclassrecord in $tsdatasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$tsparmcdoid and @Value='PCLAS']]
    let $recnum := $pclassrecord/@data:ItemGroupDataSeq
    (: and get the value of TSVAL and TSVALCD :)
    let $tsvalvalue := $pclassrecord/odm:ItemData[@ItemOID=$tsvaloid]/@Value
    let $tsvalcdvalue := $pclassrecord/odm:ItemData[@ItemOID=$tsvalcdoid]/@Value
    (: just for testing: let $tsvalcdvalue := 'N0000175565' :)
    (: exclude the case that the value of TSVALCD is not a valid NDF-RT value :)
    (: check it using the NLM webservice :)
    let $webserviceresult := doc(concat('http://rxnav.nlm.nih.gov/REST/Ndfrt/allInfo.xml?nui=',$tsvalcdvalue))
    (: This returns an XML document with the structure /ndfrtdata/fullConcept/conceptName
    The latter text content must match the value of TSVAL :)
    let $conceptName := $webserviceresult/ndfrtdata/fullConcept/conceptName
    (: where not($tsvalvalue = $conceptName ) :)
    return <error rule="SD2265" variable="TSVAL" dataset="TS" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">Invalid TSVAL value='{data($tsvalvalue)}' for PCLAS with TSVALCD={data($tsvalcdvalue)} - value from NLM web service found='{data($conceptName)}'</error>
]]></rulexquery>
</sdsrule>

<sdsrule originator="FDA" id="SD1268" standard="SDTM" last-update="2020-08-08">
	<ruledescription>When TSVCDVER is populated then TSVCDREF must be populated</ruledescription>
	<ruledetaileddescription>The Name of the Reference Terminology (TSVCDREF) must be populated when the Version of the Reference Terminology (TSVCDVER) is populated</ruledetaileddescription>
	<igversion>3.1.2</igversion>
	<igversion>3.1.3</igversion>
	<igversion>3.2</igversion>
	<domain>TS</domain>
	<synonym context="CDISC">CG0266</synonym>
	<rulexquery><![CDATA[
(: Rule SD1268 - When TSVCDVER != null then TSVCDREF != null :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdiscpilot01/' :)
(: let $define := 'define_2_0.xml' :)
let $definedoc := doc(concat($base,$define))
(: iterate over all TS datasets - there should only be one :)
for $datasetdef in $definedoc//odm:ItemGroupDef[@Name='TS']
    let $name := $datasetdef/@Name
    let $datasetname := (
		if($defineversion = '2.1') then $datasetdef/def21:leaf/@xlink:href
		else $datasetdef/def:leaf/@xlink:href
	)
    let $datasetdoc := (
		if($datasetname) then doc(concat($base,$datasetname))
		else ()
	)
    (: and get the OIDs of TSVCDVER and TSVCDREF :)
    let $tsvcdveroid := (
    for $a in $definedoc//odm:ItemDef[@Name='TSVCDVER']/@OID 
        where $a = $datasetdef/odm:ItemRef/@ItemOID
        return $a
    )
    let $tsvcdrefoid := (
    for $a in $definedoc//odm:ItemDef[@Name='TSVCDREF']/@OID 
        where $a = $datasetdef/odm:ItemRef/@ItemOID
        return $a
    )
    (: iterate over all records for which TSVCDVER != null :)
    for $record in $datasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$tsvcdveroid]]
        let $recnum := $record/@data:ItemGroupDataSeq
        (: get the value of TSVCDVER :)
        let $tsvcdver := $record/odm:ItemData[@ItemOID=$tsvcdveroid]/@Value
        (: and the value of TSVCDREF (if any) :)
        let $tsvcdref := $record/odm:ItemData[@ItemOID=$tsvcdrefoid]/@Value
        (: TSVCDREF may not be null :)
        where not($tsvcdref) 
        return <error rule="SD1268" dataset="{data($name)}" variable="TSVCDREF" rulelastupdate="2020-08-08">TSVCDVER='{data($tsvcdver)}' but TSVCDREF=null</error>			
		]]>
	</rulexquery>
</sdsrule>

<!-- Rule SD1260: TSVCDVER must be populated when Reference Terminology is versioned. 
This rule cannot be implemented, as there is no way to know whether a reference terminology is versioned or not -->

<sdsrule id="SD1038" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>TSSEQ variable value must be unique within TSPARMCD</ruledescription>
<ruledetaileddescription>The value of Sequence Number (--SEQ) variable must be unique for each record within a domain and within each Unique Subject Identifier (USUBJID) or Pool Identifier (POOLID) variables value when they are present in the domain</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>TS</domain>
<rulexquery><![CDATA[
(: Rule SD1038 - Non-unique value for TSSEQ variable within TSPARMCD - 
Sequence Number (TSSEQ) must have a  unique value for a given value of Trial Summary Parameter Short Name (TSPARMCD) within the domain,
i.e. combination of TSSEQ and TSPARMCD must be unique :)
(:can be made much more faster using "GROUP BY" :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external;
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: get the OID of the TSSEQ and TSPARMCD variables in the define.xml :)
let $tsseq := $definedoc//odm:ItemDef[@Name='TSSEQ']/@OID
let $tsparmcd := $definedoc//odm:ItemDef[@Name='TSPARMCD']/@OID
(: get the TS dataset :)
let $tsdatasetname := (
	if($defineversion = '2.1') then $definedoc//odm:ItemGroupDef[@Name='TS']/def21:leaf/@xlink:href
	else $definedoc//odm:ItemGroupDef[@Name='TS']/def:leaf/@xlink:href
)
let $tsdatasetdoc := (
	if($tsdatasetname) then doc(concat($base,$tsdatasetname))
	else ()
)
(: now iterate over the TS records :)
for $d in $tsdatasetdoc//odm:ItemGroupData
    let $seqvalue := $d/odm:ItemData[@ItemOID=$tsseq]/@Value
    let $parmcdvalue := $d/odm:ItemData[@ItemOID=$tsparmcd]/@Value
    let $recnum := $d/@data:ItemGroupDataSeq
    (: and count the one with the (same) combination of TSSEQ and TSPARMCD in the dataset :)
    (: additional possibility to speed up: add [@data:ItemGroupDataSeq > $recnum] :)
    let $count := count($tsdatasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$tsseq and @Value=$seqvalue] and odm:ItemData[@ItemOID=$tsparmcd and @Value=$parmcdvalue]])
    where $count > 1
    return <error rule="SD1038" dataset="TS" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">Non-unique value for TSSEQ variable within TSPARMCD - combination of TSSEQ={data($seqvalue)} and TSPARMCD={data($parmcdvalue)} occurs {data($count)} times</error>
]]></rulexquery>
</sdsrule>

<!-- Remark that such rules can also/better be implemented using a RESTful web service, 
		as the number of TS parameters is increasing rapidly -->
<sdsrule originator="FDA" id="SD1295" standard="SDTM" last-update="2020-08-08">
	<ruledescription>TSVAL for ADDON record must be 'Y' or 'N'</ruledescription>
	<ruledetaileddescription>TSVAL for ADDON record must be 'Y' or 'N'</ruledetaileddescription>
	<igversion>3.1.2</igversion>
	<igversion>3.1.3</igversion>
	<igversion>3.2</igversion>
	<domain>TS</domain>
	<synonym context="CDISC">CG0269</synonym>
	<rulexquery><![CDATA[
(: Rule SD1295 - When TSPARMCD = 'ADDON' and TSVAL != null then TSVAL in ('N','Y') :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
declare namespace xs="http://www.w3.org/2001/XMLSchema";
declare namespace functx = "http://www.functx.com";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external;
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: iterate over all TS datasets (there should be only one) :)
for $itemgroup in $definedoc//odm:ItemGroupDef[@Name='TS']
    (: Get the OID for the TSVAL and TSPARMCD variables :)
    let $tsvaloid := (
        for $a in $definedoc//odm:ItemDef[@Name='TSVAL']/@OID
        where $a = $definedoc//odm:ItemGroupDef[@Name='TS']/odm:ItemRef/@ItemOID
        return $a
    )
    let $tsparmcdoid := (
        for $a in $definedoc//odm:ItemDef[@Name='TSPARMCD']/@OID
        where $a = $definedoc//odm:ItemGroupDef[@Name='TS']/odm:ItemRef/@ItemOID
        return $a
    )
    let $datasetname := (
		if($defineversion = '2.1') then $itemgroup/def21:leaf/@xlink:href
		else $itemgroup/def:leaf/@xlink:href
	)
    let $datasetdoc := (
		if($datasetname) then doc(concat($base,$datasetname))
		else ()
	)
    (: iterate over all the records within the TS dataset for which TSPARMCD='ADDON' TSVAL!=null.
    Normally, there should be only be 1 such record :)
   for $record in $datasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$tsvaloid] and odm:ItemData[@ItemOID=$tsparmcdoid and @Value='ADDON']]
        (: get the record number :)
        let $recnum := $record/@data:ItemGroupDataSeq
        (: and get the value of the TSVAL :)
        let $tsval := $record/odm:ItemData[@ItemOID=$tsvaloid]/@Value
        (: TSVAL must be one of 'Y' or 'N' :)
        where not($tsval='Y' or $tsval='N')
        return <error rule="SD1295" dataset="TS" variable="TSVAL" recordnumber="{data($recnum)}" rulelastupdate="2020-08-08">TSVAL='{data($tsval)}' for TSPARMCD='ADDON' is expected to be in ('N','Y')</error>			
		]]>
	</rulexquery>
</sdsrule>

<sdsrule id="SD1215" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>TSVAL value must be either ISO 8601 duration format or null, when TSPARMCD='AGEMAX'</ruledescription>
<ruledetaileddescription>TSVAL value must be either ISO 8601 duration format for time period (e.g. P80Y) or null, when TSPARMCD='AGEMAX'</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>TS</domain>
<rulexquery><![CDATA[	
(: Rule SD1215 - Invalid TSVAL value for AGEMAX:
TSVAL value must be either ISO 8601 format for time period (e.g. P80Y) or null, when TSPARMCD='AGEMAX'
:)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
declare namespace xs="http://www.w3.org/2001/XMLSchema";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define)) 
(: get the TS dataset :)
let $tsdataset := $definedoc//odm:ItemGroupDef[@Name='TS']
let $tsdatasetname := (
	if($defineversion = '2.1') then $tsdataset/def21:leaf/@xlink:href
	else $tsdataset/def:leaf/@xlink:href
)
let $tsdatasetdoc := (	
	if($tsdatasetname) then doc(concat($base,$tsdatasetname))
	else ()
)
(: get the OID of the TSPARMCD variable and of the TSVAL variable :)
let $tsparmcdoid := (
    for $a in $definedoc//odm:ItemDef[@Name='TSPARMCD']/@OID 
    where $a = $definedoc//odm:ItemGroupDef[@Name='TS']/odm:ItemRef/@ItemOID
    return $a
)
let $tsvaloid := (
    for $a in $definedoc//odm:ItemDef[@Name='TSVAL']/@OID 
    where $a = $definedoc//odm:ItemGroupDef[@Name='TS']/odm:ItemRef/@ItemOID
    return $a
)
(: get the record in the TS dataset that has TSPARMCD=AGEMAX :)
let $tsagemaxrecord := $tsdatasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$tsparmcdoid and @Value='AGEMAX']] 
(: and get the TSVAL value :)
let $tsvalvalue := $tsagemaxrecord/odm:ItemData[@ItemOID=$tsvaloid]/@Value
(: TSVAL must be either null or in ISO-8601 "duration" format :)
where $tsvalvalue and not($tsvalvalue castable as xs:duration)
return <error rule="SD1215" dataset="TS" variable="TSVAL" rulelastupdate="2020-08-08" recordnumber="{data($tsagemaxrecord)}">Invalid (non-ISO8601 duration) TSVAL value='{data($tsvalvalue)}' for AGEMAX in dataset TS</error>	
]]></rulexquery>
</sdsrule>

<sdsrule originator="FDA" id="SD1223" standard="SDTM" last-update="2020-08-08">
	<ruledescription>TSVAL for RANDOM record must be 'Y' or 'N'.</ruledescription>
	<ruledetaileddescription>TSVAL for RANDOM record must be 'Y' or 'N'.</ruledetaileddescription>
	<igversion>3.1.2</igversion>
	<igversion>3.1.3</igversion>
	<igversion>3.2</igversion>
	<domain>TS</domain>
	<rulexquery><![CDATA[
(: Rule SD1223 - When TSPARMCD = 'RANDOM' and TSVAL != null then TSVAL in ('N','Y') :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
declare namespace xs="http://www.w3.org/2001/XMLSchema";
declare namespace functx = "http://www.functx.com";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external;
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: iterate over all TS datasets (there should be only one) :)
for $itemgroup in $definedoc//odm:ItemGroupDef[@Name='TS']
    (: Get the OID for the TSVAL and TSPARMCD variables :)
    let $tsvaloid := (
        for $a in $definedoc//odm:ItemDef[@Name='TSVAL']/@OID
        where $a = $definedoc//odm:ItemGroupDef[@Name='TS']/odm:ItemRef/@ItemOID
        return $a
    )
    let $tsparmcdoid := (
        for $a in $definedoc//odm:ItemDef[@Name='TSPARMCD']/@OID
        where $a = $definedoc//odm:ItemGroupDef[@Name='TS']/odm:ItemRef/@ItemOID
        return $a
    )
    let $datasetname := (
		if($defineversion = '2.1') then $itemgroup/def21:leaf/@xlink:href
		else $itemgroup/def:leaf/@xlink:href
	)
    let $datasetdoc := (	
		if($datasetname) then doc(concat($base,$datasetname))
		else ()
	)
    (: iterate over all the records within the TS dataset for which TSPARMCD='RANDOM' TSVAL!=null.
    Normally, there should be only be 1 such record :)
   for $record in $datasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$tsparmcdoid and @Value='RANDOM']]
        (: get the record number :)
        let $recnum := $record/@data:ItemGroupDataSeq
        (: and get the value of the TSVAL :)
        let $tsval := $record/odm:ItemData[@ItemOID=$tsvaloid]/@Value
        (: TSVAL must be in ('N','Y') :)
        where not($tsval='N' or $tsval='Y')
        return <error rule="SD1223" dataset="TS" variable="TSVAL" recordnumber="{data($recnum)}" rulelastupdate="2020-08-08">TSVAL='{data($tsval)}' for TSPARMCD='RANDOM' is expected to be in ('N','Y')</error>							
		]]>
	</rulexquery>
</sdsrule>

<sdsrule originator="FDA" id="SD1306" standard="SDTM" last-update="2020-08-08">
	<ruledescription>When a record exists where TSPARMCD = 'HLTSUBJI' and TSVAL = 'Y' then for TSPARMCD = 'TDIGRP', TSVAL must be null</ruledescription>
	<ruledetaileddescription>When the study population is healthy subjects (TSPARMCD = 'HLTSUBJI' and TSVAL = 'Y'), the Diagnosis Group parameter should be provided with a null Parameter Value (TSVAL should be null when TSPARMCD = 'TDIGRP')</ruledetaileddescription>
	<igversion>3.1.3</igversion>
	<igversion>3.2</igversion>
	<domain>TS</domain>
	<synonym context="CDISC">CG0272</synonym>
	<rulexquery><![CDATA[
(: Rule CG0272 - When TSPARMCD = 'TDIGRP'  and record exists where TSPARMCD = 'HLTSUBJI' and TSVAL = 'Y' then TSVAL = null :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
declare namespace xs="http://www.w3.org/2001/XMLSchema";
declare namespace functx = "http://www.functx.com";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external;
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: iterate over all TS datasets (there should be only one) :)
for $itemgroup in $definedoc//odm:ItemGroupDef[@Name='TS']
    (: Get the OID for the TSVAL and TSPARMCD variables :)
    let $tsvaloid := (
        for $a in $definedoc//odm:ItemDef[@Name='TSVAL']/@OID
        where $a = $definedoc//odm:ItemGroupDef[@Name='TS']/odm:ItemRef/@ItemOID
        return $a
    )
    let $tsparmcdoid := (
        for $a in $definedoc//odm:ItemDef[@Name='TSPARMCD']/@OID
        where $a = $definedoc//odm:ItemGroupDef[@Name='TS']/odm:ItemRef/@ItemOID
        return $a
    )
    (: get the dataset :)
    let $datasetname := (
		if($defineversion = '2.1') then $itemgroup/def21:leaf/@xlink:href
		else $itemgroup/def:leaf/@xlink:href
	)
    let $datasetdoc := (	
		if($datasetname) then doc(concat($base,$datasetname))
		else ()
	)
    (: look for a record with TSPARMCD='HLTSUBJI' and TSVAL='Y' :)
    let $hltsubjects := $datasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$tsparmcdoid and @Value='HLTSUBJI'] and odm:ItemData[@ItemOID=$tsvaloid and @Value='Y']]  (: the record itself or null :)
    (: look for a record with TSPARMCD='TDIGRP' :)
    let $tdigrprecord :=  $datasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$tsparmcdoid and @Value='TDIGRP']]
    let $recnum := $tdigrprecord/@data:ItemGroupDataSeq
    let $tdigrpvalue := $tdigrprecord/odm:ItemData[@ItemOID=$tsvaloid]/@Value
    (: TSPARMCD = 'TDIGRP' must be null :)
    where $hltsubjects and (not($tdigrpvalue) or string-length($tdigrpvalue)=0 )
    return <error rule="SD1306" dataset="TS" variable="TSVAL" rulelastupdate="2020-08-08">TSVAL='{data($tdigrpvalue)}' for TSPARMCD='TDIGRP' is expected to be null as a record with TSPARMCD='HLTSUBJI and TSVAL='Y' exists</error>		]]>
	</rulexquery>
</sdsrule>

<sdsrule id="SD1307" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>TDIGRP record must be populated in TS, when HLTSUBJI = 'N'</ruledescription>
<ruledetaileddescription>TDIGRP record must be populated in TS, when HLTSUBJI = 'N' - 'Diagnosis Group' (TDIGRP) record must be populated in Trial Summay (TS) domain, when study population is unhealthy subjects (HLTSUBJI = 'N'). It is expected for SDTM IG v3.1.2 data and required for data in all more recent SDTM versions</ruledetaileddescription>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>TS</domain>
<rulexquery><![CDATA[
(: Rule SD1307 - Missing TDIGRP Trial Summary Parameter:
'Diagnosis Group' (TDIGRP) record must be populated in Trial Summay (TS) domain, when study population is unhealthy subjects (HLTSUBJI = 'N'). It is expected for SDTM IG v3.1.2 data and required for data in all more recent SDTM versions.
:)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
declare namespace xs="http://www.w3.org/2001/XMLSchema";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: get the TS dataset :)
let $tsdataset := $definedoc//odm:ItemGroupDef[@Name='TS']
let $tsdatasetname := (
	if($defineversion = '2.1') then $tsdataset/def21:leaf/@xlink:href
	else $tsdataset/def:leaf/@xlink:href
)
let $tsdatasetdoc := (	
	if($tsdatasetname) then doc(concat($base,$tsdatasetname))
	else ()
)
(: get the OID of the TSPARMCD variable :)
let $tsparmcdoid := (
    for $a in $definedoc//odm:ItemDef[@Name='TSPARMCD']/@OID 
    where $a = $definedoc//odm:ItemGroupDef[@Name='TS']/odm:ItemRef/@ItemOID
    return $a
)
(: count the number of records for which TSPARMCD=TDIGRP :)
let $count := count($tsdatasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$tsparmcdoid and @Value='TDIGRP']])
(: The number of TDIGRP records may not be 0 :)
where $count = 0
return <error rule="SD1307" dataset="TS" rulelastupdate="2020-08-08">Missing TDIGRP Trial Summary Parameter in dataset TS</error>
]]></rulexquery>
</sdsrule>


<sdsrule originator="FDA" id="SD1308" standard="SDTM" last-update="2020-08-08">
	<ruledescription>When a record exists where TSPARMCD = 'ADDON' and TSVAL = 'Y' then for TSPARMCD = 'CURTRT', TSVAL must be populated</ruledescription>
	<ruledetaileddescription>When test product is added to existing treatment (TSPARMCD = 'ADDON' and TSVAL = 'Y'), the Current Therapy or Treatment parameter should be provided with Parameter Value populated (TSVAL should not be null when TSPARMCD = 'CURTRT')</ruledetaileddescription>
	<igversion>3.1.3</igversion>
	<igversion>3.2</igversion>
	<domain>TS</domain>
	<synonym context="CDISC">CG0275</synonym>
	<rulexquery><![CDATA[
(: Rule SD1308 - When TSPARMCD = 'CURTRT' and record exists where TSPARMCD = 'ADDON' and TSVAL = 'Y' then TSVAL != null :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
declare namespace xs="http://www.w3.org/2001/XMLSchema";
declare namespace functx = "http://www.functx.com";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external;
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: iterate over all TS datasets (there should be only one) :)
for $itemgroup in $definedoc//odm:ItemGroupDef[@Name='TS']
    (: Get the OID for the TSVAL and TSPARMCD variables :)
    let $tsvaloid := (
        for $a in $definedoc//odm:ItemDef[@Name='TSVAL']/@OID
        where $a = $definedoc//odm:ItemGroupDef[@Name='TS']/odm:ItemRef/@ItemOID
        return $a
    )
    let $tsparmcdoid := (
        for $a in $definedoc//odm:ItemDef[@Name='TSPARMCD']/@OID
        where $a = $definedoc//odm:ItemGroupDef[@Name='TS']/odm:ItemRef/@ItemOID
        return $a
    )
    (: get the dataset :)
    let $datasetname := (
		if($defineversion = '2.1') then $itemgroup/def21:leaf/@xlink:href
		else $itemgroup/def:leaf/@xlink:href
	)
    let $datasetdoc := (
		if($datasetname) then doc(concat($base,$datasetname))
		else ()
	)
    (: look for a record with TSPARMCD='ADDON' and TSVAL='Y' :)
    let $addonyesrecord := $datasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$tsparmcdoid and @Value='ADDON'] and odm:ItemData[@ItemOID=$tsvaloid and @Value='Y']]  (: the record itself or null :)
    (: look for a record with TSPARMCD='CURTRT' :)
    let $curtrtrecord :=  $datasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$tsparmcdoid and @Value='CURTRT']]
    let $recnum := $curtrtrecord/@data:ItemGroupDataSeq
    let $curtrtvalue := $curtrtrecord/odm:ItemData[@ItemOID=$tsvaloid]/@Value
    (: TSPARMCD = 'CURTRT' must not be null :)
    where $addonyesrecord and (not($curtrtvalue))  (: give an error when TSVAL for TSPARMCD=CURTRT is null  :)
    return <error rule="SD1308" dataset="TS" variable="TSVAL" rulelastupdate="2020-08-08">Null value for TSVAL for TSPARMCD='CURTRT' found although a record with TSPARMCD='ADDON and TSVAL='Y' exists</error>			
		]]>
	</rulexquery>
</sdsrule>
	
<sdsrule originator="FDA" id="SD1309" standard="SDTM" last-update="2020-08-08">
	<ruledescription>When a record exists where TSPARMCD = 'STYPE' and TSVAL = 'INTERVENTIONAL' then for TSPARMCD = 'TRT', TSVAL must be populated</ruledescription>
	<ruledetaileddescription>When the study type is interventional (TSPARMCD = 'STYPE' and TSVAL = 'INTERVENTIONAL'), the Investigational Therapy or Treatment parameter should be provided with Parameter Value populated (TSVAL should not be null when TSPARMCD = 'TRT')</ruledetaileddescription>
	<domain>TS</domain>
	<igversion>3.1.3</igversion>
	<igversion>3.2</igversion>
	<synonym context="CDISC">CG0276</synonym>
	<rulexquery><![CDATA[
(: Rule SD1309 - When TSPARMCD = 'TRT' and record exists where TSPARMCD = 'STYPE' and TSVAL = 'INTERVENTIONAL' then TSVAL != null :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
declare namespace xs="http://www.w3.org/2001/XMLSchema";
declare namespace functx = "http://www.functx.com";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external;
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: iterate over all TS datasets (there should be only one) :)
for $itemgroup in $definedoc//odm:ItemGroupDef[@Name='TS']
    (: Get the OID for the TSVAL and TSPARMCD variables :)
    let $tsvaloid := (
        for $a in $definedoc//odm:ItemDef[@Name='TSVAL']/@OID
        where $a = $definedoc//odm:ItemGroupDef[@Name='TS']/odm:ItemRef/@ItemOID
        return $a
    )
    let $tsparmcdoid := (
        for $a in $definedoc//odm:ItemDef[@Name='TSPARMCD']/@OID
        where $a = $definedoc//odm:ItemGroupDef[@Name='TS']/odm:ItemRef/@ItemOID
        return $a
    )
    (: get the dataset :)
    let $datasetname := (
		if($defineversion = '2.1') then $itemgroup/def21:leaf/@xlink:href
		else $itemgroup/def:leaf/@xlink:href
	)
    let $datasetdoc := (
		if($datasetname) then doc(concat($base,$datasetname))
		else ()
	)
    (: look for a record with TSPARMCD='STYPE' and TSVAL='INTERVENTIONAL' :)
    let $interventionalrecord := $datasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$tsparmcdoid and @Value='STYPE'] and odm:ItemData[@ItemOID=$tsvaloid and @Value='INTERVENTIONAL']]  (: the record itself or null :)
    (: look for a record with TSPARMCD='TRT' :)
    let $trtrecord :=  $datasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$tsparmcdoid and @Value='CURTRT']]
    let $recnum := $trtrecord/@data:ItemGroupDataSeq
    let $trtvalue := $trtrecord/odm:ItemData[@ItemOID=$tsvaloid]/@Value
    (: TSVAL for TSPARMCD = 'TRT' must not be null :)
    where $interventionalrecord and (not($trtvalue))  (: give an error when TSVAL for TSPARMCD=TRT is null  :)
    return <error rule="SD1309" dataset="TS" variable="TSVAL" rulelastupdate="2020-08-08">Null value for TSVAL for TSPARMCD='TRT' found although a record with TSPARMCD='STYPE and TSVAL='INTERVENTIONAL' exists</error>			
		]]>
	</rulexquery>
</sdsrule>
	
<sdsrule id="SD1310" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>INTMODEL record must be present when STYPE='INTERVENTIONAL'</ruledescription>
<ruledetaileddescription>'Intervention Model' (INTMODEL) record must be populated in Trial Summay (TS) domain, when study type is 'INTERVENTIONAL'. It is expected for SDTM IG v3.1.2 data and required for data in all more recent SDTM versions</ruledetaileddescription>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>TS</domain>
<rulexquery><![CDATA[
(: TODO: testing :)
(: Rule SD1310 - Missing INTMODEL Trial Summary Parameter:
'Intervention Model' (INTMODEL) record must be populated in Trial Summay (TS) domain, when study type is 'INTERVENTIONAL'. It is expected for SDTM IG v3.1.2 data and required for data in all more recent SDTM versions.
:)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: get the TS dataset :)
let $tsdataset := $definedoc//odm:ItemGroupDef[@Name='TS']
let $tsdatasetname := (
	if($defineversion = '2.1') then $tsdataset/def21:leaf/@xlink:href
	else $tsdataset/def:leaf/@xlink:href
)
let $tsdatasetdoc := (
	if($tsdatasetname) then doc(concat($base,$tsdatasetname))
	else ()
)
(: get the OID of the TSPARMCD and TSVAL :)
let $tsparmcdoid := (
    for $a in $definedoc//odm:ItemDef[@Name='TSPARMCD']/@OID 
    where $a = $definedoc//odm:ItemGroupDef[@Name='TS']/odm:ItemRef/@ItemOID
    return $a
)
let $tsvaloid := (
    for $a in $definedoc//odm:ItemDef[@Name='TSVAL']/@OID 
    where $a = $definedoc//odm:ItemGroupDef[@Name='TS']/odm:ItemRef/@ItemOID
    return $a
)
(: get the record with TSPARMCD=STYPE and TSVAL=INTERVENTIONAL (if any) :)
let $stypeinterventionalrecord := $tsdatasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$tsparmcdoid and @Value='STYPE']][odm:ItemData[@ItemOID=$tsvaloid and @Value='INTERVENTIONAL']]
(: get the record with TSPARMCD=INTMODEL :)
let $intmodelrecord := $tsdatasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$tsparmcdoid and @Value='INTMODEL']]
(: it is not sufficient that there is a record with TSPARMCD=INTMODEL , TSVAL MUST also be populated :)
let $intmodelvalue := $intmodelrecord/odm:ItemData[@ItemOID=$tsvaloid]/@Value
(: when STYPE=INTERVENTIONAL, there MUST be an INDIC record with a TSVAL populated :)
where $stypeinterventionalrecord and (not($intmodelrecord) or not($intmodelvalue))
return <error rule="SD1310" dataset="TS" rulelastupdate="2020-08-08">Missing INTMODEL Trial Summary Parameter: No populated TSPARMCD=INTMODEL record was found although a record with TSPARMCD=STYPE and TSVAL=INTERVENTIONAL is present</error>
]]></rulexquery>
</sdsrule>

<sdsrule id="SD1311" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>INTTYPE record must be present when STYPE='INTERVENTIONAL'</ruledescription>
<ruledetaileddescription>'Intervention Type' (INTTYPE) record must be populated in Trial Summay (TS) domain, when study type is 'INTERVENTIONAL'. It is expected for SDTM IG v3.1.2 data and required for data in all more recent SDTM versions</ruledetaileddescription>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>TS</domain>
<rulexquery><![CDATA[
(: Rule SD1311 - Missing INTTYPE Trial Summary Parameter:
'Intervention Type' (INTTYPE) record must be populated in Trial Summay (TS) domain, when study type is 'INTERVENTIONAL'. It is expected for SDTM IG v3.1.2 data and required for data in all more recent SDTM versions.
:)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define)) 
(: get the TS dataset :)
let $tsdataset := $definedoc//odm:ItemGroupDef[@Name='TS']
let $tsdatasetname := (
	if($defineversion = '2.1') then $tsdataset/def21:leaf/@xlink:href
	else $tsdataset/def:leaf/@xlink:href
)
let $tsdatasetdoc := (
	if($tsdatasetname) then doc(concat($base,$tsdatasetname))
	else ()
)
(: get the OID of the TSPARMCD and TSVAL :)
let $tsparmcdoid := (
    for $a in $definedoc//odm:ItemDef[@Name='TSPARMCD']/@OID 
    where $a = $definedoc//odm:ItemGroupDef[@Name='TS']/odm:ItemRef/@ItemOID
    return $a
)
let $tsvaloid := (
    for $a in $definedoc//odm:ItemDef[@Name='TSVAL']/@OID 
    where $a = $definedoc//odm:ItemGroupDef[@Name='TS']/odm:ItemRef/@ItemOID
    return $a
)
(: get the record with TSPARMCD=STYPE and TSVAL=INTERVENTIONAL (if any) :)
let $stypeinterventionalrecord := $tsdatasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$tsparmcdoid and @Value='STYPE']][odm:ItemData[@ItemOID=$tsvaloid and @Value='INTERVENTIONAL']]
(: get the record with TSPARMCD=INTTYPE :)
let $inttyperecord := $tsdatasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$tsparmcdoid and @Value='INTTYPE']]
(: it is not sufficient that there is a record with TSPARMCD=INTTYPE , TSVAL MUST also be populated :)
let $inttypevalue := $inttyperecord/odm:ItemData[@ItemOID=$tsvaloid]/@Value
(: when STYPE=INTERVENTIONAL, there MUST be an INTTYPE record with a TSVAL populated :)
where $stypeinterventionalrecord and (not($inttyperecord) or not($inttypevalue))
return <error rule="SD1311" dataset="TS" rulelastupdate="2020-08-08">Missing INTTYPE Trial Summary Parameter: No populated TSPARMCD=INTTYPE record was found although a record with TSPARMCD=STYPE and TSVAL=INTERVENTIONAL is present</error>
]]></rulexquery>
</sdsrule>

<sdsrule id="SD1312" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>PCLAS must be populated when STYPE='INTERVENTIONAL'</ruledescription>
<ruledetaileddescription>When the study type is interventional (TSPARMCD = 'STYPE' and TSVAL = 'INTERVENTIONAL'), the Pharmacologic Class parameter should be provided with Parameter Value populated (TSVAL should not be null when TSPARMCD = 'PCLAS')</ruledetaileddescription>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>TS</domain>
<rulexquery><![CDATA[
(: Rule SD1312 - Missing PCLAS Trial Summary Parameter:
'Pharmacological Class of Investigational Therapy' (PCLAS) record must be populated in Trial Summay (TS) domain, when study type is 'INTERVENTIONAL' and if Intervention Type (INTTYPE) is one for which pharmacological class is applicable. It is expected for SDTM IG v3.1.2 data and required for data in all more recent SDTM versions.
:)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: get the TS dataset :)
let $tsdataset := $definedoc//odm:ItemGroupDef[@Name='TS']
let $tsdatasetname := (
	if($defineversion = '2.1') then $tsdataset/def21:leaf/@xlink:href
	else $tsdataset/def:leaf/@xlink:href
)
let $tsdatasetdoc := (
	if($tsdatasetname) then doc(concat($base,$tsdatasetname))
	else ()
)
(: get the OID of the TSPARMCD and TSVAL :)
let $tsparmcdoid := (
    for $a in $definedoc//odm:ItemDef[@Name='TSPARMCD']/@OID 
    where $a = $definedoc//odm:ItemGroupDef[@Name='TS']/odm:ItemRef/@ItemOID
    return $a
)
let $tsvaloid := (
    for $a in $definedoc//odm:ItemDef[@Name='TSVAL']/@OID 
    where $a = $definedoc//odm:ItemGroupDef[@Name='TS']/odm:ItemRef/@ItemOID
    return $a
)
(: get the record with TSPARMCD=STYPE :)
let $styperecord := $tsdatasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$tsparmcdoid and @Value='STYPE']]
(: and the value for TSVAL of the record with TSPARMCD=STYPE :)
let $stypevalue := $styperecord/odm:ItemData[@ItemOID=$tsvaloid]/@Value
(: get the record for which TSPARMCD=PCLASS :)
let $pclassrecord := $tsdatasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$tsparmcdoid and @Value='PCLASS']]
(: and the value :)
let $pclassvalue := $pclassrecord/odm:ItemData[@ItemOID=$tsvaloid]/@Value
(: when STYPE=INTERVENTIONAL, there MUST be a TSVAL for TSPARMCD=PCLASS :)
where $stypevalue='INTERVENTIONAL' and (not($pclassrecord) or not($pclassvalue))
return <error rule="SD1312" dataset="TS" rulelastupdate="2020-08-08">Missing PCLAS Trial Summary Parameter, although there is a record with TSPARMCD=STYPE and TSVAL=INTERVENTIONAL</error>
]]></rulexquery>
</sdsrule>

<sdsrule originator="FDA" id="SD1269" standard="SDTM" last-update="2020-08-08">
	<ruledescription>TSVAL must be between 0 and 1, when TSPARMCD = 'RANDQT'</ruledescription>
	<ruledetaileddescription>For the trial summary parameter Randomization Quotient (TSPARMCD = 'RANDQT'), the value must always be a number between 0 and 1, with some cases where the ratio might be equal to 1</ruledetaileddescription>
	<igversion>3.1.2</igversion>
	<igversion>3.1.3</igversion>
	<igversion>3.2</igversion>
	<domain>TS</domain>
	<synonym context="CDISC">CG0280</synonym>
	<rulexquery><![CDATA[
(: Rule SD1269 - When TSPARMCD = 'RANDQT' and TSVAL !=  null then TSVAL > 0 and TSVAL <= 1 :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
declare namespace xs="http://www.w3.org/2001/XMLSchema";
declare namespace functx = "http://www.functx.com";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external;
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: iterate over all TS datasets (there should be only one) :)
for $itemgroup in $definedoc//odm:ItemGroupDef[@Name='TS']
    (: Get the OID for the TSVAL and TSPARMCD variables :)
    let $tsvaloid := (
        for $a in $definedoc//odm:ItemDef[@Name='TSVAL']/@OID
        where $a = $definedoc//odm:ItemGroupDef[@Name='TS']/odm:ItemRef/@ItemOID
        return $a
    )
    let $tsparmcdoid := (
        for $a in $definedoc//odm:ItemDef[@Name='TSPARMCD']/@OID
        where $a = $definedoc//odm:ItemGroupDef[@Name='TS']/odm:ItemRef/@ItemOID
        return $a
    )
    (: get the dataset :)
    let $tsdatasetname := (
		if($defineversion = '2.1') then $itemgroup/def21:leaf/@xlink:href
		else $itemgroup/def:leaf/@xlink:href
	)
    let $tsdatasetdoc := (
		if($tsdatasetname) then doc(concat($base,$tsdatasetname))
		else ()
	)
    (: look for a record with TSPARMCD='RANDQT' - we assume there is only 1 :)
    for $randqtrecord in $tsdatasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$tsparmcdoid and @Value='RANDQT']]
        let $recnum := $randqtrecord/@data:ItemGroupDataSeq
        (: get the value of TSVAL in this record (if any) :)
        let $tsval := $randqtrecord/odm:ItemData[@ItemOID=$tsvaloid]/@Value
        (: TSVAL !=  null then TSVAL > 0 and TSVAL <= 1 :)
        where $tsval and not($tsval castable as xs:double and xs:double($tsval) > 0 and xs:double($tsval) <=1 )
        return <error rule="SD1269" dataset="TS" variable="TSVAL" rulelastupdate="2020-08-08">TSVAL={data($tsval)} whereas a number &gt;0 and &lt;=1 was expected</error>
	]]></rulexquery>
</sdsrule>

<sdsrule originator="FDA" id="SD1296" standard="SDTM" last-update="2020-08-08">
	<ruledescription>TSVAL for ADAPT record must be 'Y' or 'N'</ruledescription>
	<ruledetaileddescription>TSVAL for ADAPT record must be 'Y' or 'N'.</ruledetaileddescription>
	<igversion>3.1.2</igversion>
	<igversion>3.1.3</igversion>
	<igversion>3.2</igversion>
	<domain>TS</domain>
	<synonym context="CDISC">CG0282</synonym>
	<rulexquery><![CDATA[
(: Rule SD1296 - When TSPARMCD = ADAPT then TSVAL in ('N','Y') :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
declare namespace xs="http://www.w3.org/2001/XMLSchema";
declare namespace functx = "http://www.functx.com";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external;
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: iterate over all TS datasets (there should be only one) :)
for $itemgroup in $definedoc//odm:ItemGroupDef[@Name='TS']
    (: Get the OID for the TSVAL and TSPARMCD variables :)
    let $tsvaloid := (
        for $a in $definedoc//odm:ItemDef[@Name='TSVAL']/@OID
        where $a = $definedoc//odm:ItemGroupDef[@Name='TS']/odm:ItemRef/@ItemOID
        return $a
    )
    let $tsparmcdoid := (
        for $a in $definedoc//odm:ItemDef[@Name='TSPARMCD']/@OID
        where $a = $definedoc//odm:ItemGroupDef[@Name='TS']/odm:ItemRef/@ItemOID
        return $a
    )
    let $tsdatasetname := (
		if($defineversion = '2.1') then $itemgroup/def21:leaf/@xlink:href
		else $itemgroup/def:leaf/@xlink:href
	)
    let $tsdatasetdoc := (
		if($tsdatasetname) then doc(concat($base,$tsdatasetname))
		else ()
	)
    (: iterate over all the records within the TS dataset for which TSPARMCD='ADAPT'
    Normally, there should be only be 1 such record :)
   for $record in $tsdatasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$tsparmcdoid and @Value='ADAPT']]
        (: get the record number :)
        let $recnum := $record/@data:ItemGroupDataSeq
        (: and get the value of the TSVAL :)
        let $tsval := $record/odm:ItemData[@ItemOID=$tsvaloid]/@Value
        (: TSVAL must be in ('N','Y') :)
        where not($tsval='N' or $tsval='Y')
        return <error rule="SD1296" dataset="TS" variable="TSVAL" recordnumber="{data($recnum)}" rulelastupdate="2020-08-08">TSVAL='{data($tsval)}' for TSPARMCD='ADAPT' is expected to be in ('N','Y')</error>				
		]]>
	</rulexquery>
</sdsrule>

<sdsrule id="SD2245" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>TSVAL variable value must be in ISO 8601 format, when TSPARMCD='DCUTDTC'</ruledescription>
<ruledetaileddescription>TSVAL variable value must be in ISO 8601 format, when TSPARMCD='DCUTDTC'</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>TS</domain>
<rulexquery><![CDATA[
(: TODO: test on real dataset :)
(: Rule SD2245 - Invalid TSVAL value for DCUTDTC:
TSVAL variable value must be in ISO 8601 format, when TSPARMCD='DCUTDTC'
:)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: get the TS dataset :)
let $tsdataset := $definedoc//odm:ItemGroupDef[@Name='TS']
let $tsdatasetname := (
	if($defineversion = '2.1') then $tsdataset/def21:leaf/@xlink:href
	else $tsdataset/def:leaf/@xlink:href
)
let $tsdatasetdoc := (
	if($tsdatasetname) then doc(concat($base,$tsdatasetname))
	else ()
)
(: get the OID of the TSPARMCD and TSVAL variables :)
let $tsparmcdoid := (
    for $a in $definedoc//odm:ItemDef[@Name='TSPARMCD']/@OID 
    where $a = $definedoc//odm:ItemGroupDef[@Name='TS']/odm:ItemRef/@ItemOID
    return $a
)
let $tsvaloid := (
    for $a in $definedoc//odm:ItemDef[@Name='TSVAL']/@OID 
    where $a = $definedoc//odm:ItemGroupDef[@Name='TS']/odm:ItemRef/@ItemOID
    return $a
)
(: get the records with TSPARMCD=DCUTDTC :)
for $dcutdtcrecord in $tsdatasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$tsparmcdoid and @Value='DCUTDTC']]
    let $recnum := $dcutdtcrecord/@data:ItemGroupDataSeq
    (: get the value of TSVAL :)
    let $tsvalvalue := $dcutdtcrecord/odm:ItemData[@ItemOID=$tsvaloid]/@Value
    (: the TSVAL value must be a valid date :)
    where not($tsvalvalue castable as xs:date)
    return <error rule="SD2245" dataset="TS" variable="TSVAL" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">Invalid TSVAL value for DCUTDTC: value={data($tsvalvalue)} is not a valid ISO-8601 date</error>
]]></rulexquery>
</sdsrule>

<sdsrule id="SD2246" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>TSVAL variable value must be numeric, when TSPARMCD='NARMS'</ruledescription>
<ruledetaileddescription>TSVAL variable value must be numeric, when TSPARMCD='NARMS'</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>TS</domain>
<rulexquery><![CDATA[
(: Rule SD2246 - Invalid TSVAL value for NARMS:
TSVAL variable value must be numeric, when TSPARMCD='NARMS'
:)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: get the TS dataset :)
let $tsdataset := $definedoc//odm:ItemGroupDef[@Name='TS']
let $tsdatasetname := (
	if($defineversion = '2.1') then $tsdataset/def21:leaf/@xlink:href
	else $tsdataset/def:leaf/@xlink:href
)
let $tsdatasetdoc := (
	if($tsdatasetname) then doc(concat($base,$tsdatasetname))
	else ()
)
(: get the OID of the TSPARMCD and TSVAL :)
let $tsparmcdoid := (
    for $a in $definedoc//odm:ItemDef[@Name='TSPARMCD']/@OID 
    where $a = $definedoc//odm:ItemGroupDef[@Name='TS']/odm:ItemRef/@ItemOID
    return $a
)
let $tsvaloid := (
    for $a in $definedoc//odm:ItemDef[@Name='TSVAL']/@OID 
    where $a = $definedoc//odm:ItemGroupDef[@Name='TS']/odm:ItemRef/@ItemOID
    return $a
)
(: get the records with TSPARMCD=NARMS (if any) :)
for $narmsrecord in $tsdatasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$tsparmcdoid and @Value='NARMS']]
    let $recnum := $narmsrecord/@data:ItemGroupDataSeq
    (: get the value of TSVAL :)
    let $tsvalvalue := $narmsrecord/odm:ItemData[@ItemOID=$tsvaloid]/@Value
    (: it must be numeric - we however translate this as that it must be a non-negative integer :)
    where not($tsvalvalue castable as xs:integer) or not(number($tsvalvalue) > -1)
    return <error rule="SD2246" dataset="TS" variable="TSVAL" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">Invalid TSVAL value for NARMS. A non-negative integer is expected, value found={data($tsvalvalue)}</error>
]]></rulexquery>
</sdsrule>

<sdsrule id="SD2247" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>TSVAL variable value must be in ISO 8601 date format, when TSPARMCD='SSTDTC'</ruledescription>
<ruledetaileddescription>TSVAL variable value must be in ISO 8601 date format, when TSPARMCD='SSTDTC'</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>TS</domain>
<rulexquery><![CDATA[
(: Rule SD2247 - Invalid TSVAL value for SSTDTC:
TSVAL variable value must be in ISO 8601 format, when TSPARMCD='SSTDTC' :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: get the TS dataset :)
let $tsdataset := $definedoc//odm:ItemGroupDef[@Name='TS']
let $tsdatasetname := (
	if($defineversion = '2.1') then $tsdataset/def21:leaf/@xlink:href
	else $tsdataset/def:leaf/@xlink:href
)
let $tsdatasetdoc := (
	if($tsdatasetname) then doc(concat($base,$tsdatasetname))
	else ()
)
(: get the OID of the TSPARMCD and TSVAL :)
let $tsparmcdoid := (
    for $a in $definedoc//odm:ItemDef[@Name='TSPARMCD']/@OID 
    where $a = $definedoc//odm:ItemGroupDef[@Name='TS']/odm:ItemRef/@ItemOID
    return $a
)
let $tsvaloid := (
    for $a in $definedoc//odm:ItemDef[@Name='TSVAL']/@OID 
    where $a = $definedoc//odm:ItemGroupDef[@Name='TS']/odm:ItemRef/@ItemOID
    return $a
)
(: get the record with TSPARMCD=SSTDTC and get the value :)
let $sstdtcrecord := $tsdatasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$tsparmcdoid and @Value='SSTDTC']]
let $recnum := $sstdtcrecord/@data:ItemGroupDataSeq
(: and get the value for TSVAL :)
let $ssdtcval := $sstdtcrecord/odm:ItemData[@ItemOID=$tsvaloid]/@Value
(: and get whether it is an ISO-8601 date :)
where $sstdtcrecord and not($ssdtcval castable as xs:date)
(: if it is present but not an ISO-8601 date, report an error :)
return <error rule="SD2247" dataset="TS" variable="TSVAL" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">Invalid TSVAL value {data($ssdtcval)} for Trial Summary Parameter SSTDTC Trial Summary Parameter TS</error>
]]></rulexquery>
</sdsrule>

<sdsrule id="SD2248" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>TSVAL variable value must be in ISO 8601 date format, when TSPARMCD='SETDTC'</ruledescription>
<ruledetaileddescription>TSVAL variable value must be in ISO 8601 date format, when TSPARMCD='SETDTC'</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>TS</domain>
<rulexquery><![CDATA[
(: Rule SD2248 - Invalid TSVAL value for SENDTC:
TSVAL variable value must be in ISO 8601 format, when TSPARMCD='SETDTC' :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: get the TS dataset :)
let $tsdataset := $definedoc//odm:ItemGroupDef[@Name='TS']
let $tsdatasetname := (
	if($defineversion = '2.1') then $tsdataset/def21:leaf/@xlink:href
	else $tsdataset/def:leaf/@xlink:href
)
let $tsdatasetdoc := (
	if($tsdatasetname) then doc(concat($base,$tsdatasetname))
	else ()
)
(: get the OID of the TSPARMCD and TSVAL :)
let $tsparmcdoid := (
    for $a in $definedoc//odm:ItemDef[@Name='TSPARMCD']/@OID 
    where $a = $definedoc//odm:ItemGroupDef[@Name='TS']/odm:ItemRef/@ItemOID
    return $a
)
let $tsvaloid := (
    for $a in $definedoc//odm:ItemDef[@Name='TSVAL']/@OID 
    where $a = $definedoc//odm:ItemGroupDef[@Name='TS']/odm:ItemRef/@ItemOID
    return $a
)
(: get the record with TSPARMCD=SETDTC and get the value :)
let $setdtcrecord := $tsdatasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$tsparmcdoid and @Value='SETDTC']]
let $recnum := $setdtcrecord/@data:ItemGroupDataSeq
(: and get the value for TSVAL :)
let $sedtcval := $setdtcrecord/odm:ItemData[@ItemOID=$tsvaloid]/@Value
(: and get whether it is an ISO-8601 date :)
where $setdtcrecord and not($sedtcval castable as xs:date)
(: if it is present but not an ISO-8601 date, report an error :)
return <error rule="SD2248" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">Invalid TSVAL value {data($sedtcval)} for Trial Summary Parameter SETDTC in dataset TS</error>
]]></rulexquery>
</sdsrule>

<sdsrule id="SD1226" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>INDIC Trial Summary Parameter must be present</ruledescription>
<ruledetaileddescription>'Trial Indication'  (INDIC) record must be populated in Trial Summary (TS) domain, when study type is 'INTERVENTIONAL'. It is required for SDTM IG v3.1.2 data and all more recent SDTM versions</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>TS</domain>
<rulexquery><![CDATA[
(: Rule SD1226 - Missing INDIC Trial Summary Parameter
'Trial Indication'  (INDIC) record must be populated in Trial Summary (TS) domain, when study type is 'INTERVENTIONAL'. It is required for SDTM IG v3.1.2 data and all more recent SDTM versions.
:)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external;
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: get the TS dataset :)
let $tsdataset := $definedoc//odm:ItemGroupDef[@Name='TS']
let $tsdatasetname := (
	if($defineversion = '2.1') then $tsdataset/def21:leaf/@xlink:href
	else $tsdataset/def:leaf/@xlink:href
)
let $tsdatasetdoc := (
	if($tsdatasetname) then doc(concat($base,$tsdatasetname))
	else ()
)
(: get the OID of the TSPARMCD and TSVAL :)
let $tsparmcdoid := (
    for $a in $definedoc//odm:ItemDef[@Name='TSPARMCD']/@OID 
    where $a = $definedoc//odm:ItemGroupDef[@Name='TS']/odm:ItemRef/@ItemOID
    return $a
)
let $tsvaloid := (
    for $a in $definedoc//odm:ItemDef[@Name='TSVAL']/@OID 
    where $a = $definedoc//odm:ItemGroupDef[@Name='TS']/odm:ItemRef/@ItemOID
    return $a
)
(: get the record with TSPARMCD=STYPE and TSVAL=INTERVENTIONAL (if any) :)
let $stypeinterventionalrecord := $tsdatasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$tsparmcdoid and @Value='STYPE']][odm:ItemData[@ItemOID=$tsvaloid and @Value='INTERVENTIONAL']]
(: get the record with TSPARMCD=INDIC :)
let $indicrecord := $tsdatasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$tsparmcdoid and @Value='INDIC']]
(: it is not sufficient that there is a record with TSPARMCD=INDIC , TSVAL MUST also be populated :)
let $indicvalue := $indicrecord/odm:ItemData[@ItemOID=$tsvaloid]/@Value
(: when STYPE=INTERVENTIONAL, there MUST be an INDIC record with a TSVAL populated :)
where $stypeinterventionalrecord and (not($indicrecord) or not($indicvalue))
return <error rule="SD1226" dataset="TS" rulelastupdate="2020-08-08">Missing INDIC Trial Summary Parameter in dataset TS: No populated TSPARMCD=INDIC record was found although a record with TSPARMCD=STYPE and TSVAL=INTERVENTIONAL is present</error>
]]></rulexquery>
</sdsrule>

<sdsrule id="SD2201" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>ADDON Trial Summary Parameter must be present - Added on to Existing Treatments' (ADDON) record must be populated in Trial Summay (TS) domain. It is expected for SDTM IG v3.1.2 data and required for data in all more recent SDTM versions</ruledescription>
<ruledetaileddescription></ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>TS</domain>
<rulexquery><![CDATA[
(: Rule SD2201 - Missing ADDON Trial Summary Parameter
'Added on to Existing Treatments' (ADDON) record must be populated in Trial Summay (TS) domain. It is expected for SDTM IG v3.1.2 data and required for data in all more recent SDTM versions.
:)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: get the TS dataset :)
let $tsdataset := $definedoc//odm:ItemGroupDef[@Name='TS']
let $tsdatasetname := (
	if($defineversion = '2.1') then $tsdataset/def21:leaf/@xlink:href
	else $tsdataset/def:leaf/@xlink:href
)
let $tsdatasetdoc := (
	if($tsdatasetname) then doc(concat($base,$tsdatasetname))
	else ()
)
(: get the OID of the TSPARMCD variable :)
let $tsparmcdoid := (
    for $a in $definedoc//odm:ItemDef[@Name='TSPARMCD']/@OID 
    where $a = $definedoc//odm:ItemGroupDef[@Name='TS']/odm:ItemRef/@ItemOID
    return $a
)
(: count the number of records in the TS dataset that have TSPARMCD=ADDON :)
let $count := count($tsdatasetdoc//odm:ItemGroupData/odm:ItemData[@ItemOID=$tsparmcdoid and @Value='ADDON'])
(: there must be more than 0 such records :)
where $count = 0
return <error rule="SD2201" dataset="TS" variable="TSPARMCD" rulelastupdate="2020-08-08">Missing ADDON Trial Summary Parameter in dataset TS</error>
]]></rulexquery>
</sdsrule>

<sdsrule id="SD2202" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>AGEMAX Trial Summary Parameter must be present</ruledescription>
<ruledetaileddescription>'Planned Maximum Age of Subjects' (AGEMAX) record must be populated in Trial Summay (TS) domain. It is expected for SDTM IG v3.1.2 data and required for data in all more recent SDTM versions</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>TS</domain>
<rulexquery><![CDATA[	
(: Rule SD2202 - Missing AGEMAX Trial Summary Parameter:
'Planned Maximum Age of Subjects' (AGEMAX) record must be populated in Trial Summay (TS) domain. It is expected for SDTM IG v3.1.2 data and required for data in all more recent SDTM versions.
:)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/'  :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: get the TS dataset :)
let $tsdataset := $definedoc//odm:ItemGroupDef[@Name='TS']
let $tsdatasetname := (
	if($defineversion = '2.1') then $tsdataset/def21:leaf/@xlink:href
	else $tsdataset/def:leaf/@xlink:href
)
let $tsdatasetdoc := (
	if($tsdatasetname) then doc(concat($base,$tsdatasetname))
	else ()
)
(: get the OID of the TSPARMCD variable :)
let $tsparmcdoid := (
    for $a in $definedoc//odm:ItemDef[@Name='TSPARMCD']/@OID 
    where $a = $definedoc//odm:ItemGroupDef[@Name='TS']/odm:ItemRef/@ItemOID
    return $a
)
(: count the number of records in the TS dataset that have TSPARMCD=AGEMAX :)
let $count := count($tsdatasetdoc//odm:ItemGroupData/odm:ItemData[@ItemOID=$tsparmcdoid and @Value='AGEMAX'])
(: there must be more at least 1 such record :)
where $count = 0
return <error rule="SD2202" dataset="TS" variable="TSPARMCD" rulelastupdate="2020-08-08">Missing AGEMAX Trial Summary Parameter in dataset TS</error>
]]></rulexquery>
</sdsrule>



<sdsrule id="SD2203" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>AGEMIN Trial Summary Parameter must be present</ruledescription>
<ruledetaileddescription>'Planned Minimum Age of Subject' (AGEMIN) record must be populated in Trial Summay (TS) domain. It is expected for SDTM IG v3.1.2 data and required for data in all more recent SDTM versions</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>TS</domain>
<rulexquery><![CDATA[	
(: Rule SD2203 - Missing AGEMIN Trial Summary Parameter:
'Planned Minimum Age of Subject' (AGEMIN) record must be populated in Trial Summay (TS) domain. It is expected for SDTM IG v3.1.2 data and required for data in all more recent SDTM versions.
:)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
declare namespace xs="http://www.w3.org/2001/XMLSchema";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: get the TS dataset :)
let $tsdataset := $definedoc//odm:ItemGroupDef[@Name='TS']
let $tsdatasetname := (
	if($defineversion = '2.1') then $tsdataset/def21:leaf/@xlink:href
	else $tsdataset/def:leaf/@xlink:href
)
let $tsdatasetdoc := (
	if($tsdatasetname) then doc(concat($base,$tsdatasetname))
	else ()
)
(: get the OID of the TSPARMCD variable :)
let $tsparmcdoid := (
    for $a in $definedoc//odm:ItemDef[@Name='TSPARMCD']/@OID 
    where $a = $definedoc//odm:ItemGroupDef[@Name='TS']/odm:ItemRef/@ItemOID
    return $a
)
(: count the number of records for which TSPARMCD=AGEMIN :)
let $count := count($tsdatasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$tsparmcdoid and @Value='AGEMIN']])
(: The number of AGEMIN records may not be 0 :)
where $count = 0
return <error rule="SD2203" dataset="TS" variable="TSPARMCD" rulelastupdate="2020-08-08">Missing AGEMIN Trial Summary Parameter in dataset TS</error>	
]]></rulexquery>
</sdsrule>

<sdsrule id="SD2204" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>LENGTH Trial Summary Parameter must be present</ruledescription>
<ruledetaileddescription>'Trial Length' (LENGTH) record must be populated in Trial Summay (TS) domain. It is expected for SDTM IG v3.1.2 data and required for data in all more recent SDTM versions</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>TS</domain>
<rulexquery><![CDATA[	
(: Rule SD2204 - Missing LENGTH Trial Summary Parameter:
'Trial Length' (LENGTH) record must be populated in Trial Summay (TS) domain. It is expected for SDTM IG v3.1.2 data and required for data in all more recent SDTM versions.
:)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
declare namespace xs="http://www.w3.org/2001/XMLSchema";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define)) 
(: get the TS dataset :)
let $tsdataset := $definedoc//odm:ItemGroupDef[@Name='TS']
let $tsdatasetname := (
	if($defineversion = '2.1') then $tsdataset/def21:leaf/@xlink:href
	else $tsdataset/def:leaf/@xlink:href
)
let $tsdatasetdoc := (
	if($tsdatasetname) then doc(concat($base,$tsdatasetname))
	else ()
)
(: get the OID of the TSPARMCD variable :)
let $tsparmcdoid := (
    for $a in $definedoc//odm:ItemDef[@Name='TSPARMCD']/@OID 
    where $a = $definedoc//odm:ItemGroupDef[@Name='TS']/odm:ItemRef/@ItemOID
    return $a
)
(: count the number of records for which TSPARMCD=LENGTH :)
let $count := count($tsdatasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$tsparmcdoid and @Value='LENGTH']])
(: The number of LENGTH records may not be 0 :)
where $count = 0
return <error rule="SD2204" dataset="TS" variable="TSPARMCD" rulelastupdate="2020-08-08">Missing LENGTH Trial Summary Parameter in dataset TS</error>	
]]></rulexquery>
</sdsrule>

<sdsrule id="SD2205" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>PLANSUB Trial Summary Parameter must be present</ruledescription>
<ruledetaileddescription>'Planned Number of Subjects' (PLANSUB) record must be populated in Trial Summary (TS) domain. It is expected for SDTM IG v3.1.2 data and required for data in all more recent SDTM versions</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>TS</domain>
<rulexquery><![CDATA[	
(: Rule SD2205 - Missing PLANSUB Trial Summary Parameter:
'Planned Number of Subjects' (PLANSUB) record must be populated in Trial Summay (TS) domain. It is expected for SDTM IG v3.1.2 data and required for data in all more recent SDTM versions.
:)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
declare namespace xs="http://www.w3.org/2001/XMLSchema";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: get the TS dataset :)
let $tsdataset := $definedoc//odm:ItemGroupDef[@Name='TS']
let $tsdatasetname := (
	if($defineversion = '2.1') then $tsdataset/def21:leaf/@xlink:href
	else $tsdataset/def:leaf/@xlink:href
)
let $tsdatasetdoc := (
	if($tsdatasetname) then doc(concat($base,$tsdatasetname))
	else ()
)
(: get the OID of the TSPARMCD variable :)
let $tsparmcdoid := (
    for $a in $definedoc//odm:ItemDef[@Name='TSPARMCD']/@OID 
    where $a = $definedoc//odm:ItemGroupDef[@Name='TS']/odm:ItemRef/@ItemOID
    return $a
)
(: count the number of records for which TSPARMCD=PLANSUB :)
let $count := count($tsdatasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$tsparmcdoid and @Value='PLANSUB']])
(: The number of PLANSUB records may not be 0 :)
where $count = 0
return <error rule="SD2205" dataset="TS" variable="TSPARMCD" rulelastupdate="2020-08-08">Missing PLANSUB Trial Summary Parameter in dataset TS</error>
]]></rulexquery>
</sdsrule>

<sdsrule id="SD2206" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>RANDOM Trial Summary Parameter must be present - </ruledescription>
<ruledetaileddescription>'Trial is Randomized' (RANDOM) record must be populated in Trial Summay (TS) domain. It is expected for SDTM IG v3.1.2 data and required for data in all more recent SDTM versions</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>TS</domain>
<rulexquery><![CDATA[	
(: Rule SD2206 - Missing RANDOM Trial Summary Parameter:
'Trial is Randomized' (RANDOM) record must be populated in Trial Summay (TS) domain. It is expected for SDTM IG v3.1.2 data and required for data in all more recent SDTM versions.
:)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
declare namespace xs="http://www.w3.org/2001/XMLSchema";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define)) 
(: get the TS dataset :)
let $tsdataset := $definedoc//odm:ItemGroupDef[@Name='TS']
let $tsdatasetname := (
	if($defineversion = '2.1') then $tsdataset/def21:leaf/@xlink:href
	else $tsdataset/def:leaf/@xlink:href
)
let $tsdatasetdoc := (
	if($tsdatasetname) then doc(concat($base,$tsdatasetname))
	else ()
)
(: get the OID of the TSPARMCD variable :)
let $tsparmcdoid := (
    for $a in $definedoc//odm:ItemDef[@Name='TSPARMCD']/@OID 
    where $a = $definedoc//odm:ItemGroupDef[@Name='TS']/odm:ItemRef/@ItemOID
    return $a
)
(: count the number of records for which TSPARMCD=RANDOM :)
let $count := count($tsdatasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$tsparmcdoid and @Value='RANDOM']])
(: The number of PLANSUB records may not be 0 :)
where $count = 0
return <error rule="SD2206" dataset="TS" rulelastupdate="2020-08-08">Missing RANDOM Trial Summary Parameter in dataset TS</error>	
]]></rulexquery>
</sdsrule>

<sdsrule id="SD2207" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>SEXPOP Trial Summary Parameter must be present - </ruledescription>
<ruledetaileddescription>'Sex of Participants' (SEXPOP) record must be populated in Trial Summay (TS) domain. It is expected for SDTM IG v3.1.2 data and required for data in all more recent SDTM versions</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>TS</domain>
<rulexquery><![CDATA[	
(: Rule SD2207 - Missing SEXPOP Trial Summary Parameter:
'Sex of Participants' (SEXPOP) record must be populated in Trial Summay (TS) domain. It is expected for SDTM IG v3.1.2 data and required for data in all more recent SDTM versions.
:)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
declare namespace xs="http://www.w3.org/2001/XMLSchema";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: get the TS dataset :)
let $tsdataset := $definedoc//odm:ItemGroupDef[@Name='TS']
let $tsdatasetname := (
	if($defineversion = '2.1') then $tsdataset/def21:leaf/@xlink:href
	else $tsdataset/def:leaf/@xlink:href
)
let $tsdatasetdoc := (
	if($tsdatasetname) then doc(concat($base,$tsdatasetname))
	else ()
)
(: get the OID of the TSPARMCD variable :)
let $tsparmcdoid := (
    for $a in $definedoc//odm:ItemDef[@Name='TSPARMCD']/@OID 
    where $a = $definedoc//odm:ItemGroupDef[@Name='TS']/odm:ItemRef/@ItemOID
    return $a
)
(: count the number of records for which TSPARMCD=SEXPOP :)
let $count := count($tsdatasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$tsparmcdoid and @Value='SEXPOP']])
(: The number of SEXPOP records may not be 0 :)
where $count = 0
return <error rule="SD2207" dataset="TS" rulelastupdate="2020-08-08">Missing SEXPOP Trial Summary Parameter in dataset TS</error>	
]]></rulexquery>
</sdsrule>

<sdsrule id="SD2209" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>TBLIND Trial Summary Parameter must be present</ruledescription>
<ruledetaileddescription>'Trial Blinding Schema' (TBLIND) record must be populated in Trial Summay (TS) domain. It is expected for SDTM IG v3.1.2 data and required for data in all more recent SDTM versions</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>TS</domain>
<rulexquery><![CDATA[	
(: Rule SD2209 - Missing TBLIND Trial Summary Parameter:
'Trial Blinding Schema' (TBLIND) record must be populated in Trial Summay (TS) domain. It is expected for SDTM IG v3.1.2 data and required for data in all more recent SDTM versions.
:)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
declare namespace xs="http://www.w3.org/2001/XMLSchema";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: get the TS dataset :)
let $tsdataset := $definedoc//odm:ItemGroupDef[@Name='TS']
let $tsdatasetname := (
	if($defineversion = '2.1') then $tsdataset/def21:leaf/@xlink:href
	else $tsdataset/def:leaf/@xlink:href
)
let $tsdatasetdoc := (
	if($tsdatasetname) then doc(concat($base,$tsdatasetname))
	else ()
)
(: get the OID of the TSPARMCD variable :)
let $tsparmcdoid := (
    for $a in $definedoc//odm:ItemDef[@Name='TSPARMCD']/@OID 
    where $a = $definedoc//odm:ItemGroupDef[@Name='TS']/odm:ItemRef/@ItemOID
    return $a
)
(: count the number of records for which TSPARMCD=TBLIND :)
let $count := count($tsdatasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$tsparmcdoid and @Value='TBLIND']])
(: The number of TBLIND records may not be 0 :)
where $count = 0
return <error rule="SD2209" rulelastupdate="2020-08-08">Missing TBLIND Trial Summary Parameter in dataset {data($tsdatasetname)}</error>
]]></rulexquery>
</sdsrule>

<sdsrule id="SD2210" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>TCNTRL Trial Summary Parameter must be present</ruledescription>
<ruledetaileddescription>'Control Type' (TCNTRL) record must be populated in Trial Summay (TS) domain. It is expected for SDTM IG v3.1.2 data and required for data in all more recent SDTM versions</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>TS</domain>
<rulexquery><![CDATA[	
(: Rule SD2210 - Missing TCNTRL Trial Summary Parameter:
'Control Type' (TCNTRL) record must be populated in Trial Summay (TS) domain. It is expected for SDTM IG v3.1.2 data and required for data in all more recent SDTM versions.
:)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
declare namespace xs="http://www.w3.org/2001/XMLSchema";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: get the TS dataset :)
let $tsdataset := $definedoc//odm:ItemGroupDef[@Name='TS']
let $tsdatasetname := (
	if($defineversion = '2.1') then $tsdataset/def21:leaf/@xlink:href
	else $tsdataset/def:leaf/@xlink:href
)
let $tsdatasetdoc := (
	if($tsdatasetname) then doc(concat($base,$tsdatasetname))
	else ()
)
(: get the OID of the TSPARMCD variable :)
let $tsparmcdoid := (
    for $a in $definedoc//odm:ItemDef[@Name='TSPARMCD']/@OID 
    where $a = $definedoc//odm:ItemGroupDef[@Name='TS']/odm:ItemRef/@ItemOID
    return $a
)
(: count the number of records for which TSPARMCD=TCNTRL :)
let $count := count($tsdatasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$tsparmcdoid and @Value='TCNTRL']])
(: The number of TCNTRL records may not be 0 :)
where $count = 0
return <error rule="SD2210" dataset="TS" rulelastupdate="2020-08-08">Missing TCNTRL Trial Summary Parameter in dataset TS</error>
]]></rulexquery>
</sdsrule>

<!-- Rule SD2211 is identical to rule SD1307! -->

<sdsrule id="SD2212" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>TINDTP record must be present in TS when STYPE='INTERVENTIONAL'</ruledescription>
<ruledetaileddescription>'Trial Indication Type' (TINDTP) record must be populated in Trial Summay (TS) domain, when study type is 'INTERVENTIONAL'. It is expected for SDTM IG v3.1.2 data and required for data in all more recent SDTM versions</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>TS</domain>
<rulexquery><![CDATA[	
(: Rule SD2212 - Missing TINDTP Trial Summary Parameter
'Trial Indication Type' (TINDTP) record must be populated in Trial Summay (TS) domain, when study type is 'INTERVENTIONAL'. It is expected for SDTM IG v3.1.2 data and required for data in all more recent SDTM versions.
:)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
declare namespace xs="http://www.w3.org/2001/XMLSchema";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: get the TS dataset :)
let $tsdataset := $definedoc//odm:ItemGroupDef[@Name='TS']
let $tsdatasetname := (
	if($defineversion = '2.1') then $tsdataset/def21:leaf/@xlink:href
	else $tsdataset/def:leaf/@xlink:href
)
let $tsdatasetdoc := (
	if($tsdatasetname) then doc(concat($base,$tsdatasetname))
	else ()
)
(: get the OID of the TSPARMCD variable :)
let $tsparmcdoid := (
    for $a in $definedoc//odm:ItemDef[@Name='TSPARMCD']/@OID 
    where $a = $definedoc//odm:ItemGroupDef[@Name='TS']/odm:ItemRef/@ItemOID
    return $a
)
(: count the number of records for which TSPARMCD=TINDTP :)
let $count := count($tsdatasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$tsparmcdoid and @Value='TINDTP']])
(: The number of TINDTP records may not be 0 :)
where $count = 0
return <error rule="SD2212" dataset="TS" rulelastupdate="2020-08-08">Missing TINDTP Trial Summary Parameter in dataset TS</error>	
]]></rulexquery>
</sdsrule>

<sdsrule id="SD2213" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>TITLE Trial Summary Parameter must be present</ruledescription>
<ruledetaileddescription>'Trial Title' (TITLE) record must be populated in Trial Summay (TS) domain. It is expected for SDTM IG v3.1.2 data and required for data in all more recent SDTM versions</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>TS</domain>
<rulexquery><![CDATA[	
(: Rule SD2213 - Missing TITLE Trial Summary Parameter:
'Trial Title' (TITLE) record must be populated in Trial Summay (TS) domain. It is expected for SDTM IG v3.1.2 data and required for data in all more recent SDTM versions.
:)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
declare namespace xs="http://www.w3.org/2001/XMLSchema";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: get the TS dataset :)
let $tsdataset := $definedoc//odm:ItemGroupDef[@Name='TS']
let $tsdatasetname := (
	if($defineversion = '2.1') then $tsdataset/def21:leaf/@xlink:href
	else $tsdataset/def:leaf/@xlink:href
)
let $tsdatasetdoc := (
	if($tsdatasetname) then doc(concat($base,$tsdatasetname))
	else ()
)
(: get the OID of the TSPARMCD variable :)
let $tsparmcdoid := (
    for $a in $definedoc//odm:ItemDef[@Name='TSPARMCD']/@OID 
    where $a = $definedoc//odm:ItemGroupDef[@Name='TS']/odm:ItemRef/@ItemOID
    return $a
)
(: count the number of records for which TSPARMCD=TITLE :)
let $count := count($tsdatasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$tsparmcdoid and @Value='TITLE']])
(: The number of TITLE records may not be 0 :)
where $count = 0
return <error rule="SD2213" dataset="TS" rulelastupdate="2020-08-08">Missing TITLE Trial Summary Parameter in dataset TS</error>
]]></rulexquery>
</sdsrule>

<sdsrule id="SD2214" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>TPHASE Trial Summary Parameter must be present</ruledescription>
<ruledetaileddescription>'Trial Phase Classification' (TPHASE) record must be populated in Trial Summay (TS) domain. It is expected for SDTM IG v3.1.2 data and required for data in all more recent SDTM versions</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>TS</domain>
<rulexquery><![CDATA[	
(: Rule SD2214 - Missing TPHASE Trial Summary Parameter:
'Trial Phase Classification' (TPHASE) record must be populated in Trial Summay (TS) domain. It is expected for SDTM IG v3.1.2 data and required for data in all more recent SDTM versions.
:)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
declare namespace xs="http://www.w3.org/2001/XMLSchema";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: get the TS dataset :)
let $tsdataset := $definedoc//odm:ItemGroupDef[@Name='TS']
let $tsdatasetname := (
	if($defineversion = '2.1') then $tsdataset/def21:leaf/@xlink:href
	else $tsdataset/def:leaf/@xlink:href
)
let $tsdatasetdoc := (
	if($tsdatasetname) then doc(concat($base,$tsdatasetname))
	else ()
)
(: get the OID of the TSPARMCD variable :)
let $tsparmcdoid := (
    for $a in $definedoc//odm:ItemDef[@Name='TSPARMCD']/@OID 
    where $a = $definedoc//odm:ItemGroupDef[@Name='TS']/odm:ItemRef/@ItemOID
    return $a
)
(: count the number of records for which TSPARMCD=TPHASE :)
let $count := count($tsdatasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$tsparmcdoid and @Value='TPHASE']])
(: The number of TPHASE records may not be 0 :)
where $count = 0
return <error rule="SD2214" dataset="TS" rulelastupdate="2020-08-08">Missing TPHASE Trial Summary Parameter in dataset TS</error>
]]></rulexquery>
</sdsrule>

<sdsrule id="SD2215" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>TTYPE Trial Summary Parameter must be present</ruledescription>
<ruledetaileddescription>'Trial Type (TTYPE) record must be populated in Trial Summay (TS) domain. It is expected for SDTM IG v3.1.2 data and required for data in all more recent SDTM versions</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>TS</domain>
<rulexquery><![CDATA[	
(: Rule SD2215 - Missing TTYPE Trial Summary Parameter:
'Trial Type (TTYPE) record must be populated in Trial Summay (TS) domain. It is expected for SDTM IG v3.1.2 data and required for data in all more recent SDTM versions.
:)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
declare namespace xs="http://www.w3.org/2001/XMLSchema";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define)) 
(: get the TS dataset :)
let $tsdataset := $definedoc//odm:ItemGroupDef[@Name='TS']
let $tsdatasetname := (
	if($defineversion = '2.1') then $tsdataset/def21:leaf/@xlink:href
	else $tsdataset/def:leaf/@xlink:href
)
let $tsdatasetdoc := (
	if($tsdatasetname) then doc(concat($base,$tsdatasetname))
	else ()
)
(: get the OID of the TSPARMCD variable :)
let $tsparmcdoid := (
    for $a in $definedoc//odm:ItemDef[@Name='TSPARMCD']/@OID 
    where $a = $definedoc//odm:ItemGroupDef[@Name='TS']/odm:ItemRef/@ItemOID
    return $a
)
(: count the number of records for which TSPARMCD=TTYPE :)
let $count := count($tsdatasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$tsparmcdoid and @Value='TTYPE']])
(: The number of TTYPE records may not be 0 :)
where $count = 0
return <error rule="SD2215" dataset="TS" rulelastupdate="2020-08-08">Missing TTYPE Trial Summary Parameter in dataset TS</error>
]]></rulexquery>
</sdsrule>

<sdsrule id="SD2216" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>CURTRT Trial Summary Parameter must be present - </ruledescription>
<ruledetaileddescription>'Current Therapy or Treatment' (CURTRT) record must be populated in Trial Summay (TS) domain, when ADDON is 'Y'. It is expected for SDTM IG v3.1.2 data and required for data in all more recent SDTM versions</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>TS</domain>
<rulexquery><![CDATA[	
(: Rule SD2216 - Missing CURTRT Trial Summary Parameter:
'Current Therapy or Treatment' (CURTRT) record must be populated in Trial Summay (TS) domain, when ADDON is 'Y'. It is expected for SDTM IG v3.1.2 data and required for data in all more recent SDTM versions.
:) 
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
declare namespace xs="http://www.w3.org/2001/XMLSchema";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: get the TS dataset :)
let $tsdataset := $definedoc//odm:ItemGroupDef[@Name='TS']
let $tsdatasetname := (
	if($defineversion = '2.1') then $tsdataset/def21:leaf/@xlink:href
	else $tsdataset/def:leaf/@xlink:href
)
let $tsdatasetdoc := (
	if($tsdatasetname) then doc(concat($base,$tsdatasetname))
	else ()
)
(: get the OID of the TSPARMCD variable :)
let $tsparmcdoid := (
    for $a in $definedoc//odm:ItemDef[@Name='TSPARMCD']/@OID 
    where $a = $definedoc//odm:ItemGroupDef[@Name='TS']/odm:ItemRef/@ItemOID
    return $a
)
(: count the number of records for which TSPARMCD=CURTRT :)
let $count := count($tsdatasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$tsparmcdoid and @Value='CURTRT']])
(: The number of CURTRT records may not be 0 :)
where $count = 0
return <error rule="SD2216" dataset="TS" rulelastupdate="2020-08-08">Missing CURTRT Trial Summary Parameter in dataset {data($tsdatasetname)}</error>
]]></rulexquery>
</sdsrule>

<sdsrule id="SD2217" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>OBJPRIM Trial Summary Parameter must be present</ruledescription>
<ruledetaileddescription>'Trial Primary Objective' (OBJPRIM) record must be populated in Trial Summay (TS) domain. It is expected for SDTM IG v3.1.2 data and required for data in all more recent SDTM versions</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>TS</domain>
<rulexquery><![CDATA[
(: Rule SD2217 - Missing OBJPRIM Trial Summary Parameter:
'Trial Primary Objective' (OBJPRIM) record must be populated in Trial Summay (TS) domain. It is expected for SDTM IG v3.1.2 data and required for data in all more recent SDTM versions.
:)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
declare namespace xs="http://www.w3.org/2001/XMLSchema";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external;
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: get the TS dataset :)
let $tsdataset := $definedoc//odm:ItemGroupDef[@Name='TS']
let $tsdatasetname := (
	if($defineversion = '2.1') then $tsdataset/def21:leaf/@xlink:href
	else $tsdataset/def:leaf/@xlink:href
)
let $tsdatasetdoc := (
	if($tsdatasetname) then doc(concat($base,$tsdatasetname))
	else ()
)
(: get the OID of the TSPARMCD variable :)
let $tsparmcdoid := (
    for $a in $definedoc//odm:ItemDef[@Name='TSPARMCD']/@OID 
    where $a = $definedoc//odm:ItemGroupDef[@Name='TS']/odm:ItemRef/@ItemOID
    return $a
)
(: count the number of records for which TSPARMCD=OBJPRIM :)
let $count := count($tsdatasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$tsparmcdoid and @Value='OBJPRIM']])
(: The number of OBJPRIM records may not be 0 :)
where $count = 0
return <error rule="SD2217" dataset="TS" rulelastupdate="2020-08-08">Missing OBJPRIM Trial Summary Parameter in dataset {data($tsdatasetname)}</error>
]]></rulexquery>
</sdsrule>


<sdsrule id="SD2218" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>SPONSOR Trial Summary Parameter must be present</ruledescription>
<ruledetaileddescription>'Clinical Study Sponsor' (SPONSOR) record must be populated in Trial Summay (TS) domain. It is expected for SDTM IG v3.1.2 data and required for data in all more recent SDTM versions</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>TS</domain>
<rulexquery><![CDATA[	
(: Rule SD2218 - Missing SPONSOR Trial Summary Parameter:
'Clinical Study Sponsor' (SPONSOR) record must be populated in Trial Summay (TS) domain. It is expected for SDTM IG v3.1.2 data and required for data in all more recent SDTM versions.
:)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
declare namespace xs="http://www.w3.org/2001/XMLSchema";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external;
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: get the TS dataset :)
let $tsdataset := $definedoc//odm:ItemGroupDef[@Name='TS']
let $tsdatasetname := (
	if($defineversion = '2.1') then $tsdataset/def21:leaf/@xlink:href
	else $tsdataset/def:leaf/@xlink:href
)
let $tsdatasetdoc := (
	if($tsdatasetname) then doc(concat($base,$tsdatasetname))
	else ()
)
(: get the OID of the TSPARMCD variable :)
let $tsparmcdoid := (
    for $a in $definedoc//odm:ItemDef[@Name='TSPARMCD']/@OID 
    where $a = $definedoc//odm:ItemGroupDef[@Name='TS']/odm:ItemRef/@ItemOID
    return $a
)
(: count the number of records for which TSPARMCD=SPONSOR :)
let $count := count($tsdatasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$tsparmcdoid and @Value='SPONSOR']])
(: The number of SPONSOR records may not be 0 :)
where $count = 0
return <error rule="SD2218" dataset="TS" rulelastupdate="2020-08-08">Missing SPONSOR Trial Summary Parameter in dataset {data($tsdatasetname)}</error>
]]></rulexquery>
</sdsrule>

<sdsrule id="SD2219" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>TRT Trial Summary Parameter must be present</ruledescription>
<ruledetaileddescription>Registry Identifier' (REGID) record may be populated in Trial Summary (TS) domain. It is expected for SDTM IG v3.1.2 data and all more recent SDTM versions</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>TS</domain>
<rulexquery><![CDATA[	
(: Rule SD2219 - Missing SPONSOR Trial Summary Parameter:
Registry Identifier' (REGID) record may be populated in Trial Summary (TS) domain. It is expected for SDTM IG v3.1.2 data and all more recent SDTM versions.
:)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
declare namespace xs="http://www.w3.org/2001/XMLSchema";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external;
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: get the TS dataset :)
let $tsdataset := $definedoc//odm:ItemGroupDef[@Name='TS']
let $tsdatasetname := (
	if($defineversion = '2.1') then $tsdataset/def21:leaf/@xlink:href
	else $tsdataset/def:leaf/@xlink:href
)
let $tsdatasetdoc := (
	if($tsdatasetname) then doc(concat($base,$tsdatasetname))
	else ()
)
(: get the OID of the TSPARMCD variable :)
let $tsparmcdoid := (
    for $a in $definedoc//odm:ItemDef[@Name='TSPARMCD']/@OID 
    where $a = $definedoc//odm:ItemGroupDef[@Name='TS']/odm:ItemRef/@ItemOID
    return $a
)
(: count the number of records for which TSPARMCD=TRT :)
let $count := count($tsdatasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$tsparmcdoid and @Value='TRT']])
(: The number of SPONSOR records may not be 0 :)
where $count = 0
return <error rule="SD2219" dataset="TS" rulelastupdate="2020-08-08">Missing TRT Trial Summary Parameter in dataset {data($tsdatasetname)}</error>	
]]></rulexquery>
</sdsrule>

<sdsrule id="SD2221" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>REGID Trial Summary Parameter must be present</ruledescription>
<ruledetaileddescription>'Registry Identifier' (REGID) record may be populated in Trial Summay (TS) domain. It is expected for SDTM IG v3.1.2 data and all more recent SDTM versions</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>TS</domain>
<rulexquery><![CDATA[	
(: Rule SD2221 - Missing REGID Trial Summary Parameter:
'Registry Identifier' (REGID) record may be populated in Trial Summay (TS) domain. It is expected for SDTM IG v3.1.2 data and all more recent SDTM versions.
:)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external;
declare variable $defineversion external;
(: let $base := '/db/xxx/Study09/' :)
(: let $define := 'define_DS-XML.xml' :)
let $definedoc := doc(concat($base,$define))
(: get the TS dataset :)
let $tsdataset := $definedoc//odm:ItemGroupDef[@Name='TS']
let $tsdatasetname := (
	if($defineversion = '2.1') then $tsdataset/def21:leaf/@xlink:href
	else $tsdataset/def:leaf/@xlink:href
)
let $tsdatasetdoc := (
	if($tsdatasetname) then doc(concat($base,$tsdatasetname))
	else ()
)
(: get the OID of the TSPARMCD and TSVAL and TSVALNF :)
let $tsparmcdoid := (
    for $a in $definedoc//odm:ItemDef[@Name='TSPARMCD']/@OID 
    where $a = $definedoc//odm:ItemGroupDef[@Name='TS']/odm:ItemRef/@ItemOID
    return $a
)
let $tsvaloid := (
    for $a in $definedoc//odm:ItemDef[@Name='TSVAL']/@OID 
    where $a = $definedoc//odm:ItemGroupDef[@Name='TS']/odm:ItemRef/@ItemOID
    return $a
)
let $tsvalnfoid := (
    for $a in $definedoc//odm:ItemDef[@Name='TSVALNF']/@OID 
    where $a = $definedoc//odm:ItemGroupDef[@Name='TS']/odm:ItemRef/@ItemOID
    return $a
) 
(: get the record with TSPARMCD=REGID where TSVALNF is not populated :)
let $regidrecord := $tsdatasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$tsparmcdoid and @Value='REGID']]
(: it is not sufficient that there is a record with TSPARMCD=REGID , TSVAL MUST also be populated :)
(: we also retrieve TSVALNF so that the rule can easily be changed for the case that either TSVAL or TSVALNF must be populated :)
let $tsregidvalue := $regidrecord/odm:ItemData[@ItemOID=$tsvaloid]/@Value
let $tsvalnfvalue := $regidrecord/odm:ItemData[@ItemOID=$tsvalnfoid]/@Value
where not($tsvalnfvalue) and (not($regidrecord) or not($tsregidvalue))
return <error rule="FDAC276" dataset="TS" rulelastupdate="2020-08-08">Missing REGID Trial Summary Parameter in dataset TS</error>	
]]></rulexquery>
</sdsrule>

<sdsrule id="SD2222" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>OUTMSPRI Trial Summary Parameter must be present</ruledescription>
<ruledetaileddescription>'Primary Outcome Measure' (OUTMSPRI) record must be populated in Trial Summay (TS) domain. It is expected for SDTM IG v3.1.2 data and required for data in all more recent SDTM versions</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>TS</domain>
<rulexquery><![CDATA[
(: Rule SD2222 - Missing OUTMSPRI Trial Summary Parameter:
'Primary Outcome Measure' (OUTMSPRI) record must be populated in Trial Summay (TS) domain. It is expected for SDTM IG v3.1.2 data and required for data in all more recent SDTM versions.
:)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: get the TS dataset :)
let $tsdataset := $definedoc//odm:ItemGroupDef[@Name='TS']
let $tsdatasetname := (
	if($defineversion = '2.1') then $tsdataset/def21:leaf/@xlink:href
	else $tsdataset/def:leaf/@xlink:href
)
let $tsdatasetdoc := (
	if($tsdatasetname) then doc(concat($base,$tsdatasetname))
	else ()
)
(: get the OID of the TSPARMCD and TSVAL :)
let $tsparmcdoid := (
    for $a in $definedoc//odm:ItemDef[@Name='TSPARMCD']/@OID 
    where $a = $definedoc//odm:ItemGroupDef[@Name='TS']/odm:ItemRef/@ItemOID
    return $a
)
let $tsvaloid := (
    for $a in $definedoc//odm:ItemDef[@Name='TSVAL']/@OID 
    where $a = $definedoc//odm:ItemGroupDef[@Name='TS']/odm:ItemRef/@ItemOID
    return $a
)
(: get the record with TSPARMCD=OUTMSPRI :)
let $outmsprirecord := $tsdatasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$tsparmcdoid and @Value='OUTMSPRI']]
(: it is not sufficient that there is a record with TSPARMCD=OUTMSPRI , TSVAL MUST also be populated :)
let $outmsprivalue := $outmsprirecord/odm:ItemData[@ItemOID=$tsvaloid]/@Value
where not($outmsprivalue) or not($outmsprivalue)
return <error rule="SD2222" dataset="TS" rulelastupdate="2020-08-08">Missing OUTMSPRI Trial Summary Parameter</error>
]]></rulexquery>
</sdsrule>

<!-- Rule SD2223 "Missing PCLAS Trial Summary Parameter" is a duplicate of rule SD1312 -->

<sdsrule id="SD2224" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>FCNTRY Trial Summary Parameter must be present</ruledescription>
<ruledetaileddescription>'Planned Country of Investigational Sites' (FCNTRY) record must be populated in Trial Summay (TS) domain. It is expected for SDTM IG v3.1.2 data and required for data in all more recent SDTM versions</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>TS</domain>
<rulexquery><![CDATA[
(: Rule SD2224 - Missing FCNTRY Trial Summary Parameter
'Planned Country of Investigational Sites' (FCNTRY) record must be populated in Trial Summay (TS) domain. It is expected for SDTM IG v3.1.2 data and required for data in all more recent SDTM versions.
:)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
declare namespace xs="http://www.w3.org/2001/XMLSchema";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: get the TS dataset :)
let $tsdataset := $definedoc//odm:ItemGroupDef[@Name='TS']
let $tsdatasetname := (
	if($defineversion = '2.1') then $tsdataset/def21:leaf/@xlink:href
	else $tsdataset/def:leaf/@xlink:href
)
let $tsdatasetdoc := (
	if($tsdatasetname) then doc(concat($base,$tsdatasetname))
	else ()
)
(: get the OID of the TSPARMCD variable :)
let $tsparmcdoid := (
    for $a in $definedoc//odm:ItemDef[@Name='TSPARMCD']/@OID 
    where $a = $definedoc//odm:ItemGroupDef[@Name='TS']/odm:ItemRef/@ItemOID
    return $a
)
(: count the number of records for which TSPARMCD=FCNTRY :)
let $count := count($tsdatasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$tsparmcdoid and @Value='FCNTRY']])
(: The number of FCNTRY records may not be 0 :)
where $count = 0
return <error rule="SD2224" dataset="TS" rulelastupdate="2020-08-08">Missing FCNTRY Trial Summary Parameter in dataset {data($tsdatasetname)}</error>
]]></rulexquery>
</sdsrule>

<sdsrule id="SD2225" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>ADAPT Trial Summary Parameter must be present</ruledescription>
<ruledetaileddescription>'Adaptive Design' (ADAPT) record must be populated in Trial Summay (TS) domain. It is expected for SDTM IG v3.1.2 data and required for data in all more recent SDTM versions</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>TS</domain>
<rulexquery><![CDATA[
(: Rule SD2225 - Missing ADAPT Trial Summary Parameter:
'Adaptive Design' (ADAPT) record must be populated in Trial Summay (TS) domain. It is expected for SDTM IG v3.1.2 data and required for data in all more recent SDTM versions.
:)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
declare namespace xs="http://www.w3.org/2001/XMLSchema";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: get the TS dataset :)
let $tsdataset := $definedoc//odm:ItemGroupDef[@Name='TS']
let $tsdatasetname := (
	if($defineversion = '2.1') then $tsdataset/def21:leaf/@xlink:href
	else $tsdataset/def:leaf/@xlink:href
)
let $tsdatasetdoc := (
	if($tsdatasetname) then doc(concat($base,$tsdatasetname))
	else ()
)
(: get the OID of the TSPARMCD variable :)
let $tsparmcdoid := (
    for $a in $definedoc//odm:ItemDef[@Name='TSPARMCD']/@OID 
    where $a = $definedoc//odm:ItemGroupDef[@Name='TS']/odm:ItemRef/@ItemOID
    return $a
)
(: count the number of records for which TSPARMCD=ADAPT :)
let $count := count($tsdatasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$tsparmcdoid and @Value='ADAPT']])
(: The number of ADAPT records may not be 0 :)
where $count = 0
return <error rule="SD2225" dataset="TS" rulelastupdate="2020-08-08">Missing ADAPT Trial Summary Parameter in dataset {data($tsdatasetname)}</error>
]]></rulexquery>
</sdsrule>

<sdsrule id="SD2226" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>DCUTDTC Trial Summary Parameter must be present</ruledescription>
<ruledetaileddescription>'Data Cutoff Date' (DCUTDTC) record must be populated in Trial Summay (TS) domain. It is expected for SDTM IG v3.1.2 data and required for data in all more recent SDTM versions</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>TS</domain>
<rulexquery><![CDATA[	
(: Rule SD2226 - Missing DCUTDTC Trial Summary Parameter:
'Data Cutoff Date' (DCUTDTC) record must be populated in Trial Summay (TS) domain. It is expected for SDTM IG v3.1.2 data and required for data in all more recent SDTM versions.
:)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
declare namespace xs="http://www.w3.org/2001/XMLSchema";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: get the TS dataset :)
let $tsdataset := $definedoc//odm:ItemGroupDef[@Name='TS']
let $tsdatasetname := (
	if($defineversion = '2.1') then $tsdataset/def21:leaf/@xlink:href
	else $tsdataset/def:leaf/@xlink:href
)
let $tsdatasetdoc := (
	if($tsdatasetname) then doc(concat($base,$tsdatasetname))
	else ()
)
(: get the OID of the TSPARMCD variable :)
let $tsparmcdoid := (
    for $a in $definedoc//odm:ItemDef[@Name='TSPARMCD']/@OID 
    where $a = $definedoc//odm:ItemGroupDef[@Name='TS']/odm:ItemRef/@ItemOID
    return $a
)
(: count the number of records for which TSPARMCD=DCUTDTC :)
let $count := count($tsdatasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$tsparmcdoid and @Value='DCUTDTC']])
(: The number of DCUTDTC records may not be 0 :)
where $count = 0
return <error rule="SD2226" dataset="TS" rulelastupdate="2020-08-08">Missing DCUTDTC Trial Summary Parameter in dataset {data($tsdatasetname)}</error>
]]></rulexquery>
</sdsrule>

<sdsrule id="SD2227" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>DCUTDESC Trial Summary Parameter must be present</ruledescription>
<ruledetaileddescription>'Data Cutoff Description' (DCUTDESC) record must be populated in Trial Summay (TS) domain. It is expected for SDTM IG v3.1.2 data and required for data in all more recent SDTM versions</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>TS</domain>
<rulexquery><![CDATA[
(: Rule SD2227 - Missing DCUTDESC Trial Summary Parameter:
'Data Cutoff Description' (DCUTDESC) record must be populated in Trial Summay (TS) domain. It is expected for SDTM IG v3.1.2 data and required for data in all more recent SDTM versions.
:)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define)) 
(: get the TS dataset :)
let $tsdataset := $definedoc//odm:ItemGroupDef[@Name='TS']
let $tsdatasetname := (
	if($defineversion = '2.1') then $tsdataset/def21:leaf/@xlink:href
	else $tsdataset/def:leaf/@xlink:href
)
let $tsdatasetdoc := (
	if($tsdatasetname) then doc(concat($base,$tsdatasetname))
	else ()
)
(: get the OID of the TSPARMCD and TSVAL :)
let $tsparmcdoid := (
    for $a in $definedoc//odm:ItemDef[@Name='TSPARMCD']/@OID 
    where $a = $definedoc//odm:ItemGroupDef[@Name='TS']/odm:ItemRef/@ItemOID
    return $a
)
let $tsvaloid := (
    for $a in $definedoc//odm:ItemDef[@Name='TSVAL']/@OID 
    where $a = $definedoc//odm:ItemGroupDef[@Name='TS']/odm:ItemRef/@ItemOID
    return $a
)
(: count the number of the records with TSPARMCD=DCUTDESC :)
let $count := count($tsdatasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$tsparmcdoid and @Value='DCUTDESC']])
(: the number of DCUTDESC records may not be 0 :)
where $count=0
return <error rule="SD2227" dataset="TS" rulelastupdate="2020-08-08">Missing DCUTDESC Trial Summary Parameter in dataset {data($tsdatasetname)}</error>
]]></rulexquery>
</sdsrule>

<!-- Rule SD2228 "Missing INTMODEL Trial Summary Parameter" is a duplicate of rule SD1310 -->

<sdsrule id="SD2229" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>NARMS Trial Summary Parameter must be present</ruledescription>
<ruledetaileddescription>'Planned Number of Arms' (NARMS) record must be populated in Trial Summay (TS) domain. It is expected for SDTM IG v3.1.2 data and required for data in all more recent SDTM versions</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>TS</domain>
<rulexquery><![CDATA[
(: Rule SD2229 - Missing NARMS Trial Summary Parameter:
'Planned Number of Arms' (NARMS) record must be populated in Trial Summay (TS) domain. It is expected for SDTM IG v3.1.2 data and required for data in all more recent SDTM versions.
:)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: get the TS dataset :)
let $tsdataset := $definedoc//odm:ItemGroupDef[@Name='TS']
let $tsdatasetname := (
	if($defineversion = '2.1') then $tsdataset/def21:leaf/@xlink:href
	else $tsdataset/def:leaf/@xlink:href
)
let $tsdatasetdoc := (
	if($tsdatasetname) then doc(concat($base,$tsdatasetname))
	else ()
)
(: get the OID of the TSPARMCD and TSVAL :)
let $tsparmcdoid := (
    for $a in $definedoc//odm:ItemDef[@Name='TSPARMCD']/@OID 
    where $a = $definedoc//odm:ItemGroupDef[@Name='TS']/odm:ItemRef/@ItemOID
    return $a
)
let $tsvaloid := (
    for $a in $definedoc//odm:ItemDef[@Name='TSVAL']/@OID 
    where $a = $definedoc//odm:ItemGroupDef[@Name='TS']/odm:ItemRef/@ItemOID
    return $a
)
(: count the number of the records with TSPARMCD=NARMS :)
let $count := count($tsdatasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$tsparmcdoid and @Value='NARMS']])
(: the number of NARMS records may not be 0 :)
where $count=0
return <error rule="SD2229" dataset="TS" rulelastupdate="2020-08-08">Missing NARMS Trial Summary Parameter in dataset {data($tsdatasetname)}</error>
]]></rulexquery>
</sdsrule>

<sdsrule id="SD2230" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>STYPE Trial Summary Parameter must be present</ruledescription>
<ruledetaileddescription>'Study Type' (STYPE) record must be populated in Trial Summay (TS) domain. It is expected for SDTM IG v3.1.2 data and required for data in all more recent SDTM versions</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>TS</domain>
<rulexquery><![CDATA[
(: Rule SD2230 - Missing STYPE Trial Summary Parameter:
'Study Type' (STYPE) record must be populated in Trial Summay (TS) domain. It is expected for SDTM IG v3.1.2 data and required for data in all more recent SDTM versions.
:)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: get the TS dataset :)
let $tsdataset := $definedoc//odm:ItemGroupDef[@Name='TS']
let $tsdatasetname := (
	if($defineversion = '2.1') then $tsdataset/def21:leaf/@xlink:href
	else $tsdataset/def:leaf/@xlink:href
)
let $tsdatasetdoc := (
	if($tsdatasetname) then doc(concat($base,$tsdatasetname))
	else ()
)
(: get the OID of the TSPARMCD and TSVAL :)
let $tsparmcdoid := (
    for $a in $definedoc//odm:ItemDef[@Name='TSPARMCD']/@OID 
    where $a = $definedoc//odm:ItemGroupDef[@Name='TS']/odm:ItemRef/@ItemOID
    return $a
)
let $tsvaloid := (
    for $a in $definedoc//odm:ItemDef[@Name='TSVAL']/@OID 
    where $a = $definedoc//odm:ItemGroupDef[@Name='TS']/odm:ItemRef/@ItemOID
    return $a
)
(: count the number of the records with TSPARMCD=STYPE :)
let $count := count($tsdatasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$tsparmcdoid and @Value='STYPE']])
(: the number of STYPE records may not be 0 :)
where $count=0
return <error rule="SD2230" dataset="TS" rulelastupdate="2020-08-08">Missing STYPE Trial Summary Parameter in dataset {data($tsdatasetname)}</error>
]]></rulexquery>
</sdsrule>

<!-- Rule 2231 is a duplicate of rule SD1311 -->

<sdsrule id="SD2232" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>SSTDTC Trial Summary Parameter must be present</ruledescription>
<ruledetaileddescription>'Study Start Date' (SSTDTC) record must be populated in Trial Summay (TS) domain. It is expected for SDTM IG v3.1.2 data and required for data in all more recent SDTM versions</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>TS</domain>
<rulexquery><![CDATA[
(: Rule SD2232 - Missing SSTDTC Trial Summary Parameter:
'Study Start Date' (SSTDTC) record must be populated in Trial Summay (TS) domain. It is expected for SDTM IG v3.1.2 data and required for data in all more recent SDTM versions.
:)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: get the TS dataset :)
let $tsdataset := $definedoc//odm:ItemGroupDef[@Name='TS']
let $tsdatasetname := (
	if($defineversion = '2.1') then $tsdataset/def21:leaf/@xlink:href
	else $tsdataset/def:leaf/@xlink:href
)
let $tsdatasetdoc := (
	if($tsdatasetname) then doc(concat($base,$tsdatasetname))
	else ()
)
(: get the OID of the TSPARMCD and TSVAL :)
let $tsparmcdoid := (
    for $a in $definedoc//odm:ItemDef[@Name='TSPARMCD']/@OID 
    where $a = $definedoc//odm:ItemGroupDef[@Name='TS']/odm:ItemRef/@ItemOID
    return $a
)
let $tsvaloid := (
    for $a in $definedoc//odm:ItemDef[@Name='TSVAL']/@OID 
    where $a = $definedoc//odm:ItemGroupDef[@Name='TS']/odm:ItemRef/@ItemOID
    return $a
)
(: count the number of the records with TSPARMCD=SSTDTC :)
let $count := count($tsdatasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$tsparmcdoid and @Value='SSTDTC']])
(: the number of SSTDTC records may not be 0 :)
where $count=0
return <error rule="SD2232" dataset="TS" rulelastupdate="2020-08-08">Missing SSTDTC Trial Summary Parameter in dataset {data($tsdatasetname)}</error>
]]></rulexquery>
</sdsrule>


<sdsrule id="SD2233" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>SENDTC Trial Summary Parameter must be present</ruledescription>
<ruledetaileddescription>'Study End Date' (SENDTC) record must be populated in Trial Summay (TS) domain. It is expected for SDTM IG v3.1.2 data and required for data in all more recent SDTM versions</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>TS</domain>
<rulexquery><![CDATA[
(: Rule SD2233 - Missing SENDTC Trial Summary Parameter:
'Study End Date' (SENDTC) record must be populated in Trial Summay (TS) domain. It is expected for SDTM IG v3.1.2 data and required for data in all more recent SDTM versions.
:)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: get the TS dataset :)
let $tsdataset := $definedoc//odm:ItemGroupDef[@Name='TS']
let $tsdatasetname := (
	if($defineversion = '2.1') then $tsdataset/def21:leaf/@xlink:href
	else $tsdataset/def:leaf/@xlink:href
)
let $tsdatasetdoc := (
	if($tsdatasetname) then doc(concat($base,$tsdatasetname))
	else ()
)
(: get the OID of the TSPARMCD and TSVAL :)
let $tsparmcdoid := (
    for $a in $definedoc//odm:ItemDef[@Name='TSPARMCD']/@OID 
    where $a = $definedoc//odm:ItemGroupDef[@Name='TS']/odm:ItemRef/@ItemOID
    return $a
)
let $tsvaloid := (
    for $a in $definedoc//odm:ItemDef[@Name='TSVAL']/@OID 
    where $a = $definedoc//odm:ItemGroupDef[@Name='TS']/odm:ItemRef/@ItemOID
    return $a
)
(: count the number of the records with TSPARMCD=SENDTC :)
let $count := count($tsdatasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$tsparmcdoid and @Value='SENDTC']])
(: the number of SSTDTC records may not be 0 :)
where $count=0
return <error rule="SD2233" dataset="TS" rulelastupdate="2020-08-08">Missing SENDTC Trial Summary Parameter in dataset TS</error>
]]></rulexquery>
</sdsrule>


<sdsrule id="SD2234" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>ACTSUB Trial Summary Parameter must be present - </ruledescription>
<ruledetaileddescription>'Actual Number of Subjects' (ACTSUB) record must be populated in Trial Summay (TS) domain. It is expected for SDTM IG v3.1.2 data and required for data in all more recent SDTM versions</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>TS</domain>
<rulexquery><![CDATA[
(: Rule SD2234 - Missing ACTSUB Trial Summary Parameter:
'Actual Number of Subjects' (ACTSUB) record must be populated in Trial Summay (TS) domain. It is expected for SDTM IG v3.1.2 data and required for data in all more recent SDTM versions.
:)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: get the TS dataset :)
let $tsdataset := $definedoc//odm:ItemGroupDef[@Name='TS']
let $tsdatasetname := (
	if($defineversion = '2.1') then $tsdataset/def21:leaf/@xlink:href
	else $tsdataset/def:leaf/@xlink:href
)
let $tsdatasetdoc := (
	if($tsdatasetname) then doc(concat($base,$tsdatasetname))
	else ()
)
(: get the OID of the TSPARMCD and TSVAL :)
let $tsparmcdoid := (
    for $a in $definedoc//odm:ItemDef[@Name='TSPARMCD']/@OID 
    where $a = $definedoc//odm:ItemGroupDef[@Name='TS']/odm:ItemRef/@ItemOID
    return $a
)
let $tsvaloid := (
    for $a in $definedoc//odm:ItemDef[@Name='TSVAL']/@OID 
    where $a = $definedoc//odm:ItemGroupDef[@Name='TS']/odm:ItemRef/@ItemOID
    return $a
)
(: count the number of the records with TSPARMCD=ACTSUB :)
let $count := count($tsdatasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$tsparmcdoid and @Value='ACTSUB']])
(: the number of ACTSUB records may not be 0 :)
where $count=0
return <error rule="FDAC304" dataset="TS" rulelastupdate="2020-08-08">Missing ACTSUB Trial Summary Parameter in dataset TS</error>
]]></rulexquery>
</sdsrule>

<sdsrule id="SD2235" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>HLTSUBJI Trial Summary Parameter must be present</ruledescription>
<ruledetaileddescription>'Healthy Subject Indicator' (HLTSUBJI) record must be populated in Trial Summay (TS) domain. It is expected for SDTM IG v3.1.2 data and required for data in all more recent SDTM versions</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>TS</domain>
<rulexquery><![CDATA[
(: Rule SD2235 - Missing HLTSUBJI Trial Summary Parameter:
'Healthy Subject Indicator' (HLTSUBJI) record must be populated in Trial Summay (TS) domain. It is expected for SDTM IG v3.1.2 data and required for data in all more recent SDTM versions.
:)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: get the TS dataset :)
let $tsdataset := $definedoc//odm:ItemGroupDef[@Name='TS']
let $tsdatasetname := (
	if($defineversion = '2.1') then $tsdataset/def21:leaf/@xlink:href
	else $tsdataset/def:leaf/@xlink:href
)
let $tsdatasetdoc := (
	if($tsdatasetname) then doc(concat($base,$tsdatasetname))
	else ()
)
(: get the OID of the TSPARMCD and TSVAL :)
let $tsparmcdoid := (
    for $a in $definedoc//odm:ItemDef[@Name='TSPARMCD']/@OID 
    where $a = $definedoc//odm:ItemGroupDef[@Name='TS']/odm:ItemRef/@ItemOID
    return $a
)
let $tsvaloid := (
    for $a in $definedoc//odm:ItemDef[@Name='TSVAL']/@OID 
    where $a = $definedoc//odm:ItemGroupDef[@Name='TS']/odm:ItemRef/@ItemOID
    return $a
)
(: count the number of the records with TSPARMCD=HLTSUBJI :)
let $count := count($tsdatasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$tsparmcdoid and @Value='HLTSUBJI']])
(: the number of HLTSUBJI records may not be 0 :)
where $count=0
return <error rule="SD2235" dataset="TS" rulelastupdate="2020-08-08">Missing HLTSUBJI Trial Summary Parameter in dataset TS</error>
]]></rulexquery>
</sdsrule>

<sdsrule originator="FDA" id="SD1297" standard="SDTM" last-update="2020-08-08">
	<ruledescription>TSVAL may not be populated with a value from the ISO 21090 null flavor codelist</ruledescription>
	<ruledetaileddescription>TSVAL should not be populated with a value from the ISO 21090 null flavor codelist, or synonyms of the values. The TSVALNF variable should be used to capture these values, not TSVAL</ruledetaileddescription>
	<igversion>3.1.2</igversion>
	<igversion>3.1.3</igversion>
	<igversion>3.2</igversion>
	<domain>TS</domain>
	<synonym context="CDISC">CG0291</synonym>
	<rulexquery><![CDATA[
(: Rule SD1297 TSVAL may not populated with values or synonyms of values in the ISO 21090 null flavor codelist (or other terms that can be represented as null flavors)  :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
declare namespace xs="http://www.w3.org/2001/XMLSchema";
declare namespace functx = "http://www.functx.com";
(: function to find out whether a value is in a sequence (array) :)
declare function functx:is-value-in-sequence
  ( $value as xs:anyAtomicType? ,
    $seq as xs:anyAtomicType* )  as xs:boolean {
   $value = $seq
} ;
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external;
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: the list of allowed null flavor values - ISO21090 :)
let $nfvalues := ('NI','OTH','NINF','PINF','UNK','ASKU','NAV','NASK','TRC','MSK','NA','QS')
(: iterate over all TS datasets (there should be only one) :)
for $itemgroup in $definedoc//odm:ItemGroupDef[@Name='TS']
    (: Get the OID for the TSVAL and TSVALNF variables :)
    let $tsvaloid := (
        for $a in $definedoc//odm:ItemDef[@Name='TSVAL']/@OID
        where $a = $definedoc//odm:ItemGroupDef[@Name='TS']/odm:ItemRef/@ItemOID
        return $a
    )
    let $tsvalnfoid := (
        for $a in $definedoc//odm:ItemDef[@Name='TSVALNF']/@OID
        where $a = $definedoc//odm:ItemGroupDef[@Name='TS']/odm:ItemRef/@ItemOID
        return $a
    )
    (: get the dataset :)
    let $datasetlocation := (
		if($defineversion = '2.1') then $itemgroup/def21:leaf/@xlink:href
		else $itemgroup/def:leaf/@xlink:href
	)
    let $datasetdoc := (
		if($datasetlocation) then doc(concat($base,$datasetlocation))
		else ()
	)
    (: iterate over all records in the dataset for which TSVALNF is null :)
	for $record in $datasetdoc//odm:ItemGroupData
        let $recnum := $record/@data:ItemGroupDataSeq
        (: get the TSVAL value :)
        let $tsval := $record/odm:ItemData[@ItemOID=$tsvaloid]/@Value
        (: TSVAL is not allowed to come out of the list of ISO-21090 null flavors :)
        where functx:is-value-in-sequence($tsval,$nfvalues)
        return <error rule="SD1297" dataset="TS" variable="TSVAL" recordnumber="{data($recnum)}" rulelastupdate="2020-08-08">As TSVALNF=null, TSVAL={data($tsval)} is not allowed to come out of the list of ISO-21090 null flavors ({data($nfvalues)}</error>			
		]]>
	</rulexquery>
</sdsrule>

<!-- Rules SD0063 and SD0063A are duplicates of rule SD1324,
which was not implemented, as there are no labels in Dataset-XML -->

<sdsrule id="SD1069" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>Value for --PARM must be consistent within --PARMCD</ruledescription>
<ruledetaileddescription>All values of a Parameter (--PARM) variables should be the same for a given value of a Parameter Short Name (--PARMCD) variables - TS domain</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>TS</domain>
<rulexquery><![CDATA[
(: Rule SD1069 - Inconsistent value for --PARM within --PARMCD:
All values of a Parameter (--PARM) variables should be the same for a given value of a Parameter Short Name (--PARMCD) variables - TS domain
:)

xquery version "3.0";

declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: Remark that rules FDAC087 and FDAC088 could easily be combined into a single rule :)
(: iterate over all TS datasets in the define.xml - 
 usually there will be only one :)
for $itemgroup in $definedoc//odm:ItemGroupDef[@Name='TS']
    (: get the dataset name and location :)
	let $datasetname := (
		if($defineversion = '2.1') then $itemgroup/def21:leaf/@xlink:href
		else $itemgroup/def:leaf/@xlink:href
	)
    let $datasetdoc := (
		if($datasetname) then doc(concat($base,$datasetname))
		else ()
	)
    (: we need the OID of the TSPARMCD and TSPARM variables :)
    let $tsparmcdoid := (
        for $a in $definedoc//odm:ItemDef[@Name='TSPARMCD']/@OID 
        where $a = $itemgroup/odm:ItemRef/@ItemOID 
        return $a 
    )
    let $tsparmoid := (
        for $a in $definedoc//odm:ItemDef[@Name='TSPARM']/@OID 
        where $a = $itemgroup/odm:ItemRef/@ItemOID 
        return $a 
    )
    (: iterate over all the records in the TS dataset 
    where both TSPARMCD AND TSPARM are present - 
    this should essentially be the case for all records :)
    for $record in $datasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$tsparmcdoid] and odm:ItemData[@ItemOID=$tsparmoid]]
        let $recnum := $record/@data:ItemGroupDataSeq
        let $tsparmcdvalue1 := $record/odm:ItemData[@ItemOID=$tsparmcdoid]/@Value
        let $tsparmvalue1 := $record/odm:ItemData[@ItemOID=$tsparmoid]/@Value
        (: iterate over all following records having the same value for TSPARMCD:)
        for $record2 in $datasetdoc//odm:ItemGroupData[@data:ItemGroupDataSeq>$recnum][odm:ItemData[@ItemOID=$tsparmcdoid and @Value=$tsparmcdvalue1]]
            let $recnum2 := $record2/@data:ItemGroupDataSeq
            (: let $tsparmcdvalue2 := $record/odm:ItemData[@ItemOID=$tsparmcdoid]/@Value :)
            let $tsparmvalue2 := $record2/odm:ItemData[@ItemOID=$tsparmoid]/@Value 
            (: and compare the values - both must be equal :)
            where not($tsparmvalue1=$tsparmvalue2)
            return <warning rule="SD1069" dataset="TS" variable="TSPARM" rulelastupdate="2020-08-08" recordnumber="{data($recnum2)}">Inconsistent value for --PARM within --PARMCD: both records {data($recnum)} and {data($recnum2)} have --PARMCD='{data($tsparmcdvalue1)}' but the values for --PARM '{data($tsparmvalue1)}' and '{data($tsparmvalue2)}' are different</warning>
]]></rulexquery>
</sdsrule>

<sdsrule id="SD1070" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>Value for --PARMCD must be consistent within --PARM</ruledescription>
<ruledetaileddescription>All values of a Parameter Short Name (--PARMCD) variables should be the same for a given value of a Parameter (--PARM) variables</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>TS</domain>
<rulexquery><![CDATA[
(: Rule SD1070 - Inconsistent value for --PARMCD within --PARM:
All values of a Parameter Short Name (--PARMCD) variables should be the same for a given value of a Parameter (--PARM) variables - TS domain
:)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
 
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: Remark that rules SD1069 and SD1070 could easily be combined into a single rule :)

(: iterate over all TS datasets in the define.xml - 
 usually there will be only one :)
for $itemgroup in $definedoc//odm:ItemGroupDef[@Name='TS']
    (: get the dataset name and location :)
	let $datasetname := (
		if($defineversion = '2.1') then $itemgroup/def21:leaf/@xlink:href
		else $itemgroup/def:leaf/@xlink:href
	)
    let $datasetdoc := (
		if($datasetname) then doc(concat($base,$datasetname))
		else ()
	)
    (: we need the OID of the TSPARMCD and TSPARM variables :)
    let $tsparmcdoid := (
        for $a in $definedoc//odm:ItemDef[@Name='TSPARMCD']/@OID 
        where $a = $itemgroup/odm:ItemRef/@ItemOID 
        return $a 
    )
    let $tsparmoid := (
        for $a in $definedoc//odm:ItemDef[@Name='TSPARM']/@OID 
        where $a = $itemgroup/odm:ItemRef/@ItemOID 
        return $a 
    )
    (: iterate over all the records in the TS dataset 
    where both TSPARMCD AND TSPARM are present - 
    this should essentially be the case for all records :)
    for $record in $datasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$tsparmcdoid] and odm:ItemData[@ItemOID=$tsparmoid]]
        let $recnum := $record/@data:ItemGroupDataSeq
        let $tsparmcdvalue1 := $record/odm:ItemData[@ItemOID=$tsparmcdoid]/@Value
        let $tsparmvalue1 := $record/odm:ItemData[@ItemOID=$tsparmoid]/@Value
        (: iterate over all following records having the same value for TSPARM:)
        for $record2 in $datasetdoc//odm:ItemGroupData[@data:ItemGroupDataSeq>$recnum][odm:ItemData[@ItemOID=$tsparmoid and @Value=$tsparmvalue1]]
            let $recnum2 := $record2/@data:ItemGroupDataSeq
            let $tsparmcdvalue2 := $record2/odm:ItemData[@ItemOID=$tsparmcdoid]/@Value 
            (: and compare the values - both must be equal :)
            where not($tsparmcdvalue1=$tsparmcdvalue2)
            return <warning rule="SD1070" dataset="TS" variable="TSPARMCD" rulelastupdate="2020-08-08" recordnumber="{data($recnum2)}">Inconsistent value for --PARMCD within --PARM: both records {data($recnum)} and {data($recnum2)} have --PARM='{data($tsparmvalue1)}' but the values for --PARMCD '{data($tsparmcdvalue1)}' and '{data($tsparmcdvalue2)}' are different</warning>	
]]></rulexquery>
</sdsrule>

<sdsrule id="SD0072" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>RDOMAIN must correspond to a domain defined in the submission</ruledescription>
<ruledetaileddescription>Related Domain Abbreviation (RDOMAIN) must have a valid value of Domains included in the study data</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>CO</domain>
<domain>RELREC</domain>
<domain>SUPPQUAL</domain>
<rulexquery><![CDATA[
(: Rule SD0072 - Invalid RDOMAIN:
Related Domain Abbreviation (RDOMAIN) must have a valid value of Domains included in the study data (CO,RELREC,SUPxx)
:)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external;
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: Get the RELREC, CO and SUPPxx datasets :)
let $datasets := $definedoc//odm:ItemGroupDef[@Name='RELREC' or @Name='CO' or starts-with(@Name,'SUPP')]
(: iterate over these datasets :)
for $dataset in $datasets
    let $name := $dataset/@Name
    (: Get the dataset location :)
	let $datasetname := (
		if($defineversion = '2.1') then $dataset/def21:leaf/@xlink:href
		else $dataset/def:leaf/@xlink:href
	)
    let $datasetdoc := (
		if($datasetname) then doc(concat($base,$datasetname))
		else ()
	)
    (: Get the OID of the RDOMAIN variable :)
    let $rdomainoid := (
        for $a in $definedoc//odm:ItemDef[@Name='RDOMAIN']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
    )
    (: now iterate over all the record in these dataset :)
    for $record in $datasetdoc//odm:ItemGroupData
        let $recnum := $record/@data:ItemGroupDataSeq
        (: and get the value of the RDOMAIN variable :)
        let $rdomainvalue := $record/odm:ItemData[@ItemOID=$rdomainoid]/@Value
        (: and check whether defined in the define.xml - we take into account that sometimes the "Domain" attribute is missing :)
        where not($definedoc//odm:ItemGroupDef[@Domain=$rdomainvalue or @Name=$rdomainvalue])
        return <error rule="SD0072" dataset="{data($name)}" variable="RDOMAIN" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">Invalid value for RDOMAIN='{data($rdomainvalue)}' in dataset {data($datasetname)} - domain {data($rdomainvalue)} was not found in the define.xml file {data($define)}</error>
]]></rulexquery>
</sdsrule>

<sdsrule originator="FDA" id="SD1300" standard="SDTM" last-update="2020-08-08" timeintensive="Yes">
	<ruledescription>DOMAIN value length must be 2, except for AP, RELREC</ruledescription>
	<ruledetaileddescription>The value of DOMAIN should be no more than 2 characters in length, excluding Associated Persons domains</ruledetaileddescription>
	<igversion>3.1.2</igversion>
	<igversion>3.1.3</igversion>
	<igversion>3.2</igversion>
	<domain>ALL</domain>
	<synonym context="CDISC">CG0308</synonym>
	<rulexquery><![CDATA[
(: Rule SD1300 - DOMAIN value length = 2, except for AP,RELREC
The rule is not 100% clear: does it apply to @Domain in the define.xml, or do DOMAIN/RDOMAIN values in the datasets themselves?
We implement it here for DOMAIN (but not for RDOMAIN) in non-AP, non-RELREC datasets.
Be aware that this assumes that RELREC and SUPPAP never reference AP records. 
Does this however also assume that there are no APxx records in CO? :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdiscpilot01/' :)
(: let $define := 'define_2_0.xml' :)
let $definedoc := doc(concat($base,$define)) 
(: iterate over all datasets definitions, except for AP, RELREC and SUPPAP :)
for $itemgroupdef in $definedoc//odm:ItemGroupDef[not(@Name='AP') and not(@Name='RELREC') and not(@Name='SUPPAP')]
    let $name := $itemgroupdef/@Name
    (: In each of the datasets, get the OID of the DOMAIN (when there is one) variable :)
    let $domainoid := (
        for $a in $definedoc//odm:ItemDef[@Name='DOMAIN']/@OID 
        where $a = $itemgroupdef/odm:ItemRef/@ItemOID
        return $a
    )
    (: get the location of the dataset  :)
    let $datasetlocation := (
		if($defineversion = '2.1') then $itemgroupdef/def21:leaf/@xlink:href
		else $itemgroupdef/def:leaf/@xlink:href
	)
    let $datasetdoc := (
		if($datasetlocation) then doc(concat($base,$datasetlocation))
		else ()
	)
    (: return over all the records that have a value for DOMAIN (this should essentially be all of them?) :)
    for $record in $datasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$domainoid]]
        let $recnum := $record/@data:ItemGroupDataSeq
        (: get the value of DOMAIN (if any) :)
        let $domainvalue := $record/odm:ItemData[@ItemOID=$domainoid]/@Value
        (: the length of the value must be exactly 2 :)
        where string-length($domainvalue)!=2
        return <error rule="SD1300" dataset="{data($name)}" variable="DOMAIN" recordnumber="{data($recnum)}" rulelastupdate="2020-08-08">The value for DOMAIN in the dataset does not consist of exactly 2 characters: DOMAIN='{data($domainvalue)}' was found</error>							
		]]>
	</rulexquery>
</sdsrule>
	

<sdsrule originator="FDA" id="SD1300-SD" standard="SDTM" last-update="2020-08-08" requiresDomainOrDataset="Yes">
	<ruledescription>DOMAIN value length must be 2, except for AP, RELREC</ruledescription>
	<ruledetaileddescription>The value of DOMAIN should be no more than 2 characters in length, excluding Associated Persons domains</ruledetaileddescription>
	<domain>ALL</domain>
	<igversion>3.1.2</igversion>
	<igversion>3.1.3</igversion>
	<igversion>3.2</igversion>
	<synonym context="CDISC">CG0308</synonym>
	<rulexquery><![CDATA[
(: Rule SD1300 - DOMAIN value length = 2, except for AP,RELREC
The rule is not 100% clear: does it apply to @Domain in the define.xml, or do DOMAIN/RDOMAIN values in the datasets themselves?
We implement it here for DOMAIN (but not for RDOMAIN) in non-AP, non-RELREC datasets.
Be aware that this assumes that RELREC and SUPPAP never reference AP records. 
Does this however also assume that there are no APxx records in CO? :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
declare variable $datasetname external;
(: let $base := '/db/fda_submissions/cdiscpilot01/' :)
(: let $define := 'define_2_0.xml' :)
let $definedoc := doc(concat($base,$define)) 
(: iterate over all datasets definitions, except for AP, RELREC and SUPPAP :)
for $itemgroupdef in $definedoc//odm:ItemGroupDef[@Name=$datasetname][not(@Name='AP') and not(@Name='RELREC') and not(@Name='SUPPAP')]
    let $name := $itemgroupdef/@Name
    (: In each of the datasets, get the OID of the DOMAIN (when there is one) variable :)
    let $domainoid := (
        for $a in $definedoc//odm:ItemDef[@Name='DOMAIN']/@OID 
        where $a = $itemgroupdef/odm:ItemRef/@ItemOID
        return $a
    )
    (: get the location of the dataset  :)
	let $datasetlocation := (
		if($defineversion = '2.1') then $itemgroupdef/def21:leaf/@xlink:href
		else $itemgroupdef/def:leaf/@xlink:href
	)
    let $datasetdoc := (
		if($datasetlocation) then doc(concat($base,$datasetlocation))
		else ()
	)
    (: return over all the records that have a value for DOMAIN (this should essentially be all of them?) :)
    for $record in $datasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$domainoid]]
        let $recnum := $record/@data:ItemGroupDataSeq
        (: get the value of DOMAIN (if any) :)
        let $domainvalue := $record/odm:ItemData[@ItemOID=$domainoid]/@Value
        (: the length of the value must be exactly 2 :)
        where string-length($domainvalue)!=2
        return <error rule="SD1300" dataset="{data($name)}" variable="DOMAIN" recordnumber="{data($recnum)}" rulelastupdate="2020-08-08">The value for DOMAIN in the dataset does not consist of exactly 2 characters: DOMAIN='{data($domainvalue)}' was found</error>							
		]]>
	</rulexquery>
</sdsrule>
	
<sdsrule id="SD2024" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>Either one of USUBJID or POOLID must be populated</ruledescription>
<ruledetaileddescription>Either Unique Subject Identifier (Missing or redundant values for USUBJID and POOLIDUSUBJID) or Pool Identifier (POOLID) variables values and only one must be populated</ruledetaileddescription>
<!-- does not apply to IG 3.1.2 -->
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>INTERVENTIONS</domain>
<domain>EVENTS</domain>
<domain>FINDINGS</domain>
<rulexquery><![CDATA[
(: Rule SD2024 - Missing or redundant values for USUBJID and POOLID:
 Either Unique Subject Identifier (Missing or redundant values for USUBJID and POOLIDUSUBJID) or Pool Identifier (POOLID) variables values and only one must be populated :)
xquery version "3.0";
(: POOLID is e.g. used in RELREC :)
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/'  
let $define := 'define2-0-0-example-sdtm.xml'  :)
let $definedoc := doc(concat($base,$define))
(: now iterate over all dataset definitions in the define.xm and get the USUBJID :)
for $itemgroupdef in $definedoc//odm:ItemGroupDef[upper-case(@def:Class)='INTERVENTIONS' or upper-case(@def:Class)='EVENTS' or upper-case(@def:Class)='FINDINGS'
		or upper-case(./def21:Class/@Name)='INTERVENTIONS' or upper-case(./def21:Class/@Name)='EVENTS' or upper-case(./def21:Class/@Name)='FINDINGS']
    let $name := $itemgroupdef/@Name
    let $datasetlink := (
		if($defineversion = '2.1') then $itemgroupdef/def21:leaf/@xlink:href
		else $itemgroupdef/def:leaf/@xlink:href
	)
    let $datasetdoc := (
		if($datasetlink) then doc(concat($base,$datasetlink))
		else ()
	)
	(: get the OID of the POOLID and the USUBJID variables :)
    let $poolidoid := (
        for $a in $definedoc//odm:ItemDef[@Name='POOLID']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
    ) 
    let $usubjidoid := (
        for $a in $definedoc//odm:ItemDef[@Name='USUBJID']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
    ) 
    (: iterate over all the records, but only when USUBJID and POOLID have been defined :)
    for $record in $datasetdoc[$usubjidoid and $poolidoid]//odm:ItemGroupData
        let $recnum := $record/@data:ItemGroupDataSeq
        (: get the values of POOLID and USUBJID (if any) :)
        let $poolid := $record/odm:ItemData[@ItemOID=$poolidoid]/@Value
        let $usubjid := $record/odm:ItemData[@ItemOID=$usubjidoid]/@Value
		
		let $message := (
		    (: case that both USUBJID AND POOLID are populated :)
            if(string-length($poolid) > 0 and string-length($usubjid) > 0) then
                <error rule="SD2024"  dataset="{$name}"  rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">Both USUBJID and POOLID in dataset {data($name)} are populated</error>
            (: case that neither USUBJID nor POOLID are populated :)
            else if (not($usubjid) and not($poolid)) then
                <error rule="SD2024"  dataset="{$name}"  rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">Both USUBJID and POOLID in dataset {data($name)} are populated</error>
            else ()
		)
		(: return the dynamically generated message (which can be null)  :)
        return $message  
]]>
</rulexquery>
</sdsrule>


<sdsrule id="SD2024-SD" last-update="2020-08-08" originator="FDA" standard="SDTM" requiresDomainOrDataset="Yes">
<ruledescription>Either one of USUBJID or POOLID must be populated</ruledescription>
<ruledetaileddescription>Either Unique Subject Identifier (Missing or redundant values for USUBJID and POOLIDUSUBJID) or Pool Identifier (POOLID) variables values and only one must be populated</ruledetaileddescription>
<!-- does not apply to IG 3.1.2 -->
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>INTERVENTIONS</domain>
<domain>EVENTS</domain>
<domain>FINDINGS</domain>
<rulexquery><![CDATA[
(: Rule SD2024 - Missing or redundant values for USUBJID and POOLID:
 Either Unique Subject Identifier (Missing or redundant values for USUBJID and POOLIDUSUBJID) or Pool Identifier (POOLID) variables values and only one must be populated :)
xquery version "3.0";
(: POOLID is e.g. used in RELREC :)
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external;
declare variable $defineversion external;
declare variable $datasetname external; 
(: let $base := '/db/fda_submissions/cdisc01/'  
let $define := 'define2-0-0-example-sdtm.xml'  :)
let $definedoc := doc(concat($base,$define))
(: now iterate over all dataset provided definitions in the define.xm and get the USUBJID :)
for $itemgroupdef in $definedoc//odm:ItemGroupDef[@Name=$datasetname][upper-case(@def:Class)='INTERVENTIONS' or upper-case(@def:Class)='EVENTS' or upper-case(@def:Class)='FINDINGS' 
		or upper-case(./def21:Class/@Name)='INTERVENTIONS' or upper-case(./def21:Class/@Name)='EVENTS' or upper-case(./def21:Class/@Name)='FINDINGS']
    let $name := $itemgroupdef/@Name
    let $datasetlink := (
		if($defineversion = '2.1') then $itemgroupdef/def21:leaf/@xlink:href
		else $itemgroupdef/def:leaf/@xlink:href
	)
    let $datasetdoc := (
		if($datasetlink) then doc(concat($base,$datasetlink))
		else ()
	)
    (: get the OID of the POOLID and the USUBJID variables :)
    let $poolidoid := (
        for $a in $definedoc//odm:ItemDef[@Name='POOLID']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
    ) 
    let $usubjidoid := (
        for $a in $definedoc//odm:ItemDef[@Name='USUBJID']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
    ) 
    (: iterate over all the records, but only when USUBJID and POOLID have been defined :)
    for $record in $datasetdoc[$usubjidoid and $poolidoid]//odm:ItemGroupData
        let $recnum := $record/@data:ItemGroupDataSeq
        (: get the values of POOLID and USUBJID (if any) :)
        let $poolid := $record/odm:ItemData[@ItemOID=$poolidoid]/@Value
        let $usubjid := $record/odm:ItemData[@ItemOID=$usubjidoid]/@Value
		
		let $message := (
		    (: case that both USUBJID AND POOLID are populated :)
            if(string-length($poolid) > 0 and string-length($usubjid) > 0) then
                <error rule="SD2024"  dataset="{$name}"  rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">Both USUBJID and POOLID in dataset {data($name)} are populated</error>
            (: case that neither USUBJID nor POOLID are populated :)
            else if (not($usubjid) and not($poolid)) then
                <error rule="SD2024"  dataset="{$name}"  rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">Both USUBJID and POOLID in dataset {data($name)} are populated</error>
            else ()
		)
		(: return the dynamically generated message (which can be null)  :)
        return $message  
]]>
</rulexquery>
</sdsrule>

<!-- IMPORTANT: use doc-available function! -->
<sdsrule originator="FDA" id="SD1270" standard="SDTM" last-update="2020-08-08">
	<ruledescription>PC dataset must be included when PP dataset is present</ruledescription>
	<ruledetaileddescription>Pharmacokinetic Concentrations (PC) dataset should be included, when a Pharmacokinetic Parameters(PP) dataset is present</ruledetaileddescription>
	<igversion>3.1.2</igversion>
	<igversion>3.1.3</igversion>
	<igversion>3.2</igversion>
	<domain>PP</domain>
	<domain>PC</domain>
	<synonym context="CDISC">CG0318</synonym>
	<rulexquery><![CDATA[
(: Rule SD1270 - When PP dataset present in study then PC dataset present in study :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define)) 
(: get the definition of the PP dataset :)
let $ppitemgroupdef := $definedoc//odm:ItemGroupDef[@Name='PP' or @Domain='PP']
(: and the location of the PP dataset (if any) :)
let $ppdatasetlocation := (
	if($defineversion = '2.1') then $ppitemgroupdef/def21:leaf/@xlink:href
	else $ppitemgroupdef/def:leaf/@xlink:href
)
let $ppdatasetdoc := (
	if($ppdatasetlocation) then doc(concat($base,$ppdatasetlocation))
	else ()
)
(: get the definition of the PC dataset and its location :)
let $pcitemgroupdef := $definedoc//odm:ItemGroupDef[@Name='PC' or @Domain='PC']
let $pcdatasetlocation := (
	if($defineversion = '2.1') then $pcitemgroupdef/def21:leaf/@xlink:href
	else $pcitemgroupdef/def:leaf/@xlink:href
)
let $pcdatasetdoc := (
	if($pcdatasetlocation) then doc(concat($base,$pcdatasetlocation))
	else ()
)
(: when the PP dataset exists, then also the PC dataset must exist :)
(: where $ppdatasetdoc and not($pcdatasetdoc) :)
where doc-available($ppdatasetdoc) and not(doc-available($pcdatasetdoc))
return <error rule="SD1270" dataset="PC" rulelastupdate="2020-08-08">The PP dataset is present in the study but the PC dataset is not present in study</error>									
]]>
	</rulexquery>
</sdsrule>

<!-- Rule SD9999 "" has not been implemented, as there is currently no way to recoginize a "custom" dataset from the define.xml.
This will be possible with define.xml 2.1 -->

<sdsrule originator="FDA" id="SD1271" standard="SDTM" last-update="2020-08-08">
	<ruledescription>The combination of ELEMENT, TESTRL TEENRL, and TEDUR must be unique for each ETCD</ruledescription>
	<ruledetaileddescription>The combination of Description of Element (ELEMENT), Rule for Start of Element (TESTRL), Rule for End of Element (TEENRL), and Planned Duration of Element (TEDUR) should be unique for each Element Code (ETCD)</ruledetaileddescription>
	<igversion>3.1.2</igversion>
	<igversion>3.1.3</igversion>
	<igversion>3.2</igversion>
	<domain>TE</domain>
	<rulexquery><![CDATA[
(: Rule SD1271 - The combination of ELEMENT, TESTRL TEENRL, and TEDUR must be unique for each ETCD :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' 
let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define)) 
(: get the TE dataset definition and location :)
let $teitemgroupdef := $definedoc//odm:ItemGroupDef[@Name='TE']
let $tedatasetlocation := (
	if($defineversion = '2.1') then $teitemgroupdef/def21:leaf/@xlink:href
	else $teitemgroupdef/def:leaf/@xlink:href
)
let $tedatasetdoc := (
	if($tedatasetlocation) then doc(concat($base,$tedatasetlocation))
	else ()
)
(: we need the OIDs of ELEMENT, TESTRL, TEENRL, TEDUR and ETCD :)
let $elementoid := (
    for $a in $definedoc//odm:ItemDef[@Name='ELEMENT']/@OID
    where $a = $teitemgroupdef/odm:ItemRef/@ItemOID
    return $a
)
let $testrloid := (
    for $a in $definedoc//odm:ItemDef[@Name='TESTRL']/@OID
    where $a = $teitemgroupdef/odm:ItemRef/@ItemOID
    return $a
)
let $teenrloid := (
    for $a in $definedoc//odm:ItemDef[@Name='TEENRL']/@OID
    where $a = $teitemgroupdef/odm:ItemRef/@ItemOID
    return $a
)
let $teduroid := (
    for $a in $definedoc//odm:ItemDef[@Name='TEDUR']/@OID
    where $a = $teitemgroupdef/odm:ItemRef/@ItemOID
    return $a
)
let $etcdoid := (
    for $a in $definedoc//odm:ItemDef[@Name='ETCD']/@OID
    where $a = $teitemgroupdef/odm:ItemRef/@ItemOID
    return $a
)

(: group the records by ETCD :)
let $orderedrecords := (
    for $record in $tedatasetdoc//odm:ItemGroupData
            group by 
            $b:=$record/odm:ItemData[@ItemOID=$etcdoid]/@Value
            return element group {  
                $record
            }
)
(: all records within a group have the same value of ETCD, 
 : so they should also have the same values of ELEMENT, TESTRL, TEENRL, and TEDUR :)
(: iterate over the groups :)
for $group in $orderedrecords
    (: take the FIRST record and get the values of ELEMENT, TESTRL, TEENRL, and TEDUR  :)
    let $element := $group/odm:ItemGroupData[1]/odm:ItemData[@ItemOID=$elementoid]/@Value
    let $testrl := $group/odm:ItemGroupData[1]/odm:ItemData[@ItemOID=$testrloid]/@Value
    let $teenrl := $group/odm:ItemGroupData[1]/odm:ItemData[@ItemOID=$teenrloid]/@Value
    let $tedur := $group/odm:ItemGroupData[1]/odm:ItemData[@ItemOID=$teduroid]/@Value
    let $recnum := $group/odm:ItemGroupData[1]/@data:ItemGroupDataSeq
    let $etcd := $group/odm:ItemGroupData[1]/odm:ItemData[@ItemOID=$etcdoid]/@Value
    (: now iterate over all OTHER records and check that they do have the same values for ELEMENT, TESTRL, TEENRL, and TEDUR 
    as the first record :)
    for $record in $group/odm:ItemGroupData[position()>1]
        (: and get the value :)
        let $element2 := $record/odm:ItemData[@ItemOID=$elementoid]/@Value
        let $testrl2 := $record/odm:ItemData[@ItemOID=$testrloid]/@Value
        let $teenrl2 := $record/odm:ItemData[@ItemOID=$teenrloid]/@Value
        let $tedur2 := $record/odm:ItemData[@ItemOID=$teduroid]/@Value
        let $recnum2 := $record/@data:ItemGroupDataSeq
        (: the values for ELEMENT, TESTRL, TEENRL, and TEDUR must be the same as in the first record :)
        where (not($element=$element2) or not($testrl=$testrl2) or not($teenrl=$teenrl2) or not($tedur=$tedur2))
        return <warning rule="SD1271" dataset="TE" variable="ETCD" rulelastupdate="2020-08-08" recordnumber="{data($recnum2)}">Records {data($recnum)} and {data($recnum2)} with ETCD='{data($etcd)}' do not have the same combination of ELEMENT, TESTRL, TEENRL, and TEDUR</warning>				
	]]>
	</rulexquery>
</sdsrule>

<sdsrule id="SD0089" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>At least one of TEENRL or TEDUR must be populated</ruledescription>
<ruledetaileddescription>At least one of Rule for End of Element (TEENRL) or Planned Duration of Element (TEDUR) should be populated in TE</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>TE</domain>
<rulexquery><![CDATA[	
(: Rule SD0089 - Missing values for TEENRL and TEDUR
At least one of Rule for End of Element (TEENRL) or Planned Duration of Element (TEDUR) should be populated in TE
:)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink"; 
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/'  :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: Get the location of the TE dataset :)
let $tedataset := $definedoc//odm:ItemGroupDef[@Name='TE']
let $tedatasetname := (
	if($defineversion = '2.1') then $tedataset/def21:leaf/@xlink:href
	else $tedataset/def:leaf/@xlink:href
)
let $tedatasetdoc := (
	if($tedatasetname) then doc(concat($base,$tedatasetname))
	else ()
)
(: and get the OIDs of TEENRL and TEDUR in TE :)
let $teenrloid := (
    for $a in $definedoc//odm:ItemDef[@Name='TEENRL']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name='TE']/odm:ItemRef/@ItemOID
        return $a
)
let $teduroid := (
    for $a in $definedoc//odm:ItemDef[@Name='TEDUR']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name='TE']/odm:ItemRef/@ItemOID
        return $a
)
(: iterate over all records in the TE dataset :)
for $record in $tedatasetdoc//odm:ItemGroupData
    let $recnum := $record/@data:ItemGroupDataSeq
    (: and get the values for TEENRL and TEDUR (if any) :)
    let $teenrlvalue := $record/odm:ItemData[@ItemOID=$teenrloid]/@Value
    let $tedurvalue := $record/odm:ItemData[@ItemOID=$teduroid]/@Value
    (: at least one must be populated :)
    where not($teenrlvalue) and not($tedurvalue)
    return <warning rule="SD0089" dataset="TE" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">Missing values for TEENRL and TEDUR</warning>
]]></rulexquery>
</sdsrule>

<!-- TODO: the following is only valid for SDTM-IG 3.2 / SDTM 1.4.
Needs to be extended, probably using a RESTful web service for other SDTM versions, 
i.e. the CDISC Library -->
<sdsrule id="SD1079" last-update="2010-08-30" originator="FDA" standard="SDTM">
<ruledescription>Variable must be ordered in the domain as by CDISC Standards</ruledescription>
<ruledetaileddescription>Order of variables should be as specified by CDISC standard</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>ALL</domain>
<rulexquery><![CDATA[
(: Rule SD1079 - Variables are ordered with Identifiers first, followed by the Topic, Qualifier, and Timing variables. 
Within each role, variables are ordered as shown in SDTM: Tables 2.2.1, 2.2.2, 2.2.3, 2.2.3.1, 2.2.4, and 2.2.5
We must look into the SDTM specification itself, make a list (how do we deal with prefixes?) and then compare the list with the order in the define.xml.
Is it also possible (in a second step?) to compare the order of the variables in each record with that in the define.xml?
:)	
(: Rule SD1079: Variable is in wrong order within domain: Order of variables should be as specified by CDISC standard :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
declare namespace xs="http://www.w3.org/2001/XMLSchema";
declare namespace functx = "http://www.functx.com";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external;
declare variable $defineversion external;
(: function to find out whether a value is in a sequence (array) :)
declare function functx:is-value-in-sequence
  ( $value as xs:anyAtomicType? ,
    $seq as xs:anyAtomicType* )  as xs:boolean {
   $value = $seq
} ;
(: value-intersect function: returns the intersection of two sequences :)
declare function functx:value-intersect
  ( $arg1 as xs:anyAtomicType* ,
    $arg2 as xs:anyAtomicType* )  as xs:anyAtomicType* {

  distinct-values($arg1[.=$arg2])
 } ;
(: combines two sequences :)
declare function functx:value-union
  ( $arg1 as xs:anyAtomicType* ,
    $arg2 as xs:anyAtomicType* )  as xs:anyAtomicType* {
  distinct-values(($arg1, $arg2))
 } ;
 
 (: This function compares two sequences in which the second is a subset of the first.
 If the order in both is the same, the function returns 'true', if the order is different, it returns 'false' :)
 declare function local:compare($sequence1, $sequence2) {
    let $new-list :=
      for $id in $sequence1
      where $id = $sequence2
      return $id
    return string-join($sequence2,'') eq string-join($new-list, '')
} ;
(: function replacing two subsequent dashes with the domain code :)
declare function local:replacedash($sequence,$domain) {
    for $v in $sequence
            return replace($v,'\-\-',$domain)
} ;

(: Identifiers for all classes :)
let $identifiers := ('STUDYID','DOMAIN','USUBJID','POOLID','SPDEVID','--SEQ','--GRPID',
    '--REFID','--SPID','--LNKID','--LNKGRP')
(: Timing variables for all classes :)
let $timingvars := ('VISITNUM','VISIT','VISITDY','TAETORD','EPOCH','--DTC','--STDTC','--ENDTC', '--DY','--STDY','--ENDY','--DUR','--TPT','--TPTNUM','--ELTM','--TPTREF','--RFTDTC','--STRF','--ENRF','--EVLINT','--EVINTX','--STRTPT','--STTPT','--ENRTPT','--ENTPT','--STINT','--ENINT','--DETECT')
let $dmvars := ('STUDYID','DOMAIN','USUBJID','SUBJID','RFSTDTC','RFENDTC','RFXSTDTC','RFXENDTC','RFICDTC','RFPENDTC','DTHDTC','DTHFL','SITEID','INVID','INVNAM','BRTHDTC','AGE','AGETXT','AGEU','SEX','RACE','ETHNIC','SPECIES','STRAIN','SBSTRAIN','ARMCD','ARM','ACTARMCD','ACTARM','SETCD','COUNTRY','DMDTC','DMDY')
(: variables for Interventions classes :) 
let $interventionvars := ('--TRT','--MODIFY','--DECOD','--MOOD','--CAT','--SCAT','--PRESP','--OCCUR','--STAT','--REASND','--INDC','--CLAS','--CLASCD','--DOSE','--DOSTXT','--DOSU','--DOSFRM','--DOSFRQ','--DOSTOT','--DOSRGM','--ROUTE','--LOT','--LOC','--LAT','--DIR','--PORTOT','--FAST','--PSTRG','--PSTRGU','--TRTV','--VAMT','--VAMTU','--ADJ')
(: variables for all Event classes :)
let $eventvars := ('--TERM','--MODIFY','--LLT','--LLTCD','--DECOD','--PTCD','--HLT','--HLTCD','--HLGT','--HLGTCD','--CAT','--SCAT','--PRESP','--OCCUR','--STAT','--REASND','--BODSYS','--BDSYCD','--SOC','--SOCCD','--LOC','--LAT','--DIR','--PORTOT','--PARTY','--PRTYID','--SEV','--SER','--ACN','--ACNOTH','--ACNDEV','--REL','--RELNST','--PATT','--OUT','--SCAN','--SCONG','--SDISAB','--SDTH','--SHOSP','--SLIFE','--SOD','--SMIE','--CONTRT','--TOX','--TOXGR')
let $findingvars := ('--TESTCD','--TEST','--MODIFY','--TSTDTL','--CAT','--SCAT','--POS','--BODSYS','--ORRES','--ORRESU','--ORNRLO','--ORNRHI','--STRESC','--STRESN','--STRESU','--STNRLO','--STNRHI','--STNRC','--NRIND','--RESCAT','--STAT','--REASND','--XFN','--NAM','--LOINC','--SPEC','--ANTREG','--SPCCND','--SPCUFL','--LOC','--LAT','--DIR','--PORTOT','--METHOD','--RUNID','--ANMETH','--LEAD','--CSTATE','--BLFL','--FAST','--DRVFL','--EVAL','--EVALID','--ACPTFL','--TOX','--TOXGR','--SEV','--DTHREL','--LLOQ','--ULOQ','--EXCLFL','--REASEX')
(: FA-domains: --OBJ - comes after --TEST :)
let $findingsaboutvars := ('--TESTCD','--TEST','--OBJ','--MODIFY','--TSTDTL','--CAT','--SCAT','--POS','--BODSYS','--ORRES','--ORRESU','--ORNRLO','--ORNRHI','--STRESC','--STRESN','--STRESU','--STNRLO','--STNRHI','--STNRC','--NRIND','--RESCAT','--STAT','--REASND','--XFN','--NAM','--LOINC','--SPEC','--ANTREG','--SPCCND','--SPCUFL','--LOC','--LAT','--DIR','--PORTOT','--METHOD','--RUNID','--ANMETH','--LEAD','--CSTATE','--BLFL','--FAST','--DRVFL','--EVAL','--EVALID','--ACPTFL','--TOX','--TOXGR','--SEV','--DTHREL','--LLOQ','--ULOQ','--EXCLFL','--REASEX') 
(: Special purpose: Comments (CO) domain - 
 Remark that we do not allow for COVAL1, COVAL2, ... as we are using Dataset-XML 
 which doesn't have the 200 character limitation :)
let $commentvars := ('STUDYID','DOMAIN','RDOMAIN','USUBJID','POOLID','COSEQ','IDVAR','IDVARVAL','COREF','COVAL','COEVAL','CODTC')
(: Subject Elements (SE) :)
let $subjectelementsvar := ('STUDYID','DOMAIN','USUBJID','SESEQ','ETCD','ELEMENT','SESTDTC','SEENDTC','TAETORD','EPOCH','SEUPDES')
(: Subject Visits (SV) :)
let $subjectvisitsvar := ('STUDYID','DOMAIN','USUBJID','VISITNUM','VISIT','VISITDY','SVSTDTC','SVENDTC','SVSTDY','SVENDY','SVUPDES')
(: Trial Design datasets :)
(: Trial Elements (TE) :)
let $trialelementvars := ('STUDYID','DOMAIN','ETCD','ELEMENT','TESTRL','TEENRL','TEDUR')
(: Trial Arms :)
let $trialarmvars := ('STUDYID','DOMAIN','ARMCD','ARM','TAETORD','ETCD','ELEMENT','TABRANCH','TATRANS','EPOCH')
(: Trial Visits (TV) :)
let $trialvisitvars := ('STUDYID','DOMAIN','VISITNUM','VISIT','VISITDY','ARMCD','ARM','TVSTRL','TVENRL')
(: Trial Sets (TX) :)
let $trialsetvars := ('STUDYID','DOMAIN','SETCD','SET','TXSEQ','TXPARMCD','TXPARM','TXVAL')
(: Trial Inclusion/Exclusion Criteria (TI) :)
let $trialievars := ('STUDYID','DOMAIN','IETESTCD','IETEST','IECAT','IESCAT','TIRL','TIVERS')
(: Trial Summary (TS) :)
let $trialsummaryvars := ('STUDYID','DOMAIN','TSSEQ','TSGRPID','TSPARMCD','TSPARM','TSVAL','TSVALNF','TSVALCD','TSVCDREF','TSVCDVER')
(: Trial Disease Assessments (TD) :)
let $trialdiseasevars := ('STUDYID','DOMAIN','TDORDER','TDANCVAR','TDSTOFF','TDTGTPAI','TDMINPAI','TDMAXPAI','TDNUMRPT')
(: Related Records (RELREC) :)
let $relrecvars := ('STUDYID','RDOMAIN','USUBJID','APID','POOLID','IDVAR','IDVARVAL','RELTYPE','RELID')
(: supplemental qualifiers (SUPPxx)  :)
let $suppqualvars := ('STUDYID','RDOMAIN','USUBJID','APID','POOLID','IDVAR','IDVARVAL','QNAM','QLABEL','QVAL','QORIG','QEVAL')
(: Get the define.xml file :)
(: let $base:= '/db/fda_submissions/cdiscpilot01/' :)
(: let $define := 'define_2_0.xml' :)
let $definedoc := doc(concat($base,$define))
(: iterate over all ItemGroupDef elements in define.xml :)
for $dataset in $definedoc//odm:ItemGroupDef
    (: get the 2-letter domain (except for SUPPQUAL, RELREC, ...)
        as we need it to replace the "--" by the domain code
    :)
    (: Get the domain name and class :)
    (: let $domaincode := $dataset/@Name :)
    (: take care of the case that the Domain attribute is absent :)
    let $domaincode := if ($dataset/@Domain) then $dataset/@Domain
    	else $dataset/@Name
    let $domainclass := (
		if($defineversion = '2.1') then $dataset/./def21:Class/@Name
		else $dataset/@def:Class
	)
    let $datasetname := $dataset/@Name
    (: iterate over the ItemRef elements and get the OID - then transform it to the Name
    make an array out of it :)
    let $vararray := (
    for $itemref in $dataset/odm:ItemRef
        (: Attention - we need to exclude non-standard variables (NSVs) :)
        let $itemrefoid := $itemref[not(starts-with(upper-case(@Role),'SUPPLEMENTAL'))]/@ItemOID
        (: Get the name of the variable from the corresponding ItemDef :)
        let $varname := $definedoc//odm:ItemDef[@OID=$itemrefoid]/@Name
        return $varname
    )
    (: now create the array to compare with - we need to replace any '--' by the domain code :)
    (: replace any dashes in the variable names by the domain code :)
    let $identifiers := local:replacedash($identifiers,$domaincode)
    let $timingvars := local:replacedash($timingvars,$domaincode)
    let $interventionvars := local:replacedash($interventionvars,$domaincode)
    let $eventvars := local:replacedash($eventvars,$domaincode)
    let $findingvars := local:replacedash($findingvars,$domaincode)
    (: combine all variables: identifiers, class-specific variables and timing variables 
    into a single sequence :)
    let $standardvars := (
        if ($domaincode = 'DM') then $dmvars
        else if($domaincode = 'CO') then $commentvars
        else if($domaincode = 'SE') then $subjectelementsvar
        else if($domaincode = 'SV') then $subjectvisitsvar
        else if($domaincode = 'TV') then $trialvisitvars
        (: Trial Design datasets :)
        else if($domaincode = 'TE') then $trialelementvars
        else if($domaincode = 'TA') then $trialarmvars
        else if($domaincode = 'TX') then $trialsetvars
        else if($domaincode = 'TI') then $trialievars
        else if($domaincode = 'TS') then $trialsummaryvars
        else if($domaincode = 'TD') then $trialdiseasevars
        else if(starts-with($domaincode,'SUPP')) then $suppqualvars
        (: Related Records dataset :)
        else if($domaincode = 'RELREC') then $relrecvars
        else if (upper-case($domainclass) = 'INTERVENTIONS' ) then
            functx:value-union(functx:value-union($identifiers,$interventionvars),$timingvars)
        else if (upper-case($domainclass) = 'EVENTS') then
            functx:value-union(functx:value-union($identifiers,$eventvars),$timingvars)
        else if (upper-case($domainclass) = 'FINDINGS') then
            functx:value-union(functx:value-union($identifiers,$findingvars),$timingvars)    
        else ()
    )
    (: if 'false' is returned by local:compare then the order is incorrect :)
    where local:compare($standardvars,$vararray) = false()
    return <warning rule="SD1079" dataset="{data($datasetname)}" rulelastupdate="2020-08-08">{data($datasetname)} - domain {data($domaincode)}: Invalid order of variables in define.xml - found: {data($vararray)}
    - expected: {data($standardvars)}</warning>
]]></rulexquery>
</sdsrule>


<!-- SD1040 applies to INTERVENTIONS, EVENTS, FINDINGS, TI -->
<sdsrule id="SD1040" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>SCAT may not have the same value as DOMAIN, -TERM, -TESTCD, -TRT or BODSYS</ruledescription>
<ruledetaileddescription>Value of Subcategory (--SCAT) should not be  identical to that in Domain Abbreviation (DOMAIN); Reported Term (--TERM); Name of Measurement, Test, or Examination (--TESTCD); Name of Treatment (--TRT); Body System or Organ Class (--BODSYS)</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>INTERVENTIONS</domain>
<domain>EVENTS</domain>
<domain>FINDINGS</domain>
<domain>TI</domain>
<rulexquery><![CDATA[	
(: Rule SD1040 - Redundancy in paired variables values:
Value of Subcategory (--SCAT) should not be  identical to that in Domain Abbreviation (DOMAIN); Reported Term (--TERM); Name of Measurement, Test, or Examination (--TESTCD); Name of Treatment (--TRT); Body System or Organ Class (--BODSYS)
:)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: iterate over all datasets that have a --SCAT variable in the interventions, events findings domains and the TI dataset :)
for $dataset in $definedoc//odm:ItemGroupDef[upper-case(@def:Class)='INTERVENTIONS' or upper-case(@def:Class)='EVENTS' or upper-case(@def:Class)='FINDINGS' 
		or upper-case(./def21:Class/@Name)='INTERVENTIONS' or upper-case(./def21:Class/@Name)='EVENTS' or upper-case(./def21:Class/@Name)='FINDINGS' 
		or @Name='TI']
    let $name := $dataset/@Name
    let $datasetname := (
		if($defineversion = '2.1') then $dataset/def21:leaf/@xlink:href
		else $dataset/def:leaf/@xlink:href
	)
    let $datasetdoc := (
		if($datasetname) then doc(concat($base,$datasetname))
		else ()
	)
    (: Get the OID of the --SCAT variable (if any) :)
    let $scatoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'SCAT') and string-length(@Name)=6]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a 
    )
    let $scatname := concat($name,'SCAT')
    (: similarly, get the OIDs of DOMAIN, TERM, TESTCD, TRT, BODSYS :)
    let $domainoid := (
        for $a in $definedoc//odm:ItemDef[@Name='DOMAIN']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
    )
    let $termoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'TERM') and string-length(@Name)=6]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a    
    )
    let $testcdoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'TESTCD') and string-length(@Name)=8]/@OID 
            where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
            return $a    
    )
    let $trtoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'TRT') and string-length(@Name)=5]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a    
    )
    let $bodsysoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'BODSYS') and string-length(@Name)=7]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a    
    )
    (: iterate over all the records that do have a value for --CAT :)
    for $record in $datasetdoc[$scatoid]//odm:ItemGroupData[odm:ItemData[@ItemOID=$scatoid]]
        let $recnum := $record/@data:ItemGroupDataSeq
        let $scatvalue := $record/odm:ItemData[@ItemOID=$scatoid]/@Value
        (: and get the values of the DOMAIN, TERM, TESTCD, TRT, BODSYS variables (when present) :)
        let $domainvalue := $record/odm:ItemData[@ItemOID=$domainoid]/@Value
        let $termvalue := $record/odm:ItemData[@ItemOID=$termoid]/@Value
        let $testcdvalue := $record/odm:ItemData[@ItemOID=$testcdoid]/@Value
        let $trtvalue := $record/odm:ItemData[@ItemOID=$trtoid]/@Value
        let $bodsysvalue := $record/odm:ItemData[@ItemOID=$bodsysoid]/@Value
        (: we do already have selected on the presence of a value for the CAT variable,
        now compare it with the values of the DOMAIN, TERM, TESTCD, TRT, BODSYS variables
        (when present) :)
        where $scatvalue=$domainvalue or $scatvalue=$termvalue or $scatvalue=$testcdvalue or $scatvalue=$trtvalue or $scatvalue=$bodsysvalue
        return <warning rule="SD1040" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">Redundancy in paired variables values for {data($scatname)} with value={data($scatvalue)}.</warning>
]]></rulexquery>
</sdsrule>

<!-- SD1040 applies to INTERVENTIONS, EVENTS, FINDINGS, TI -->
<sdsrule id="SD1040-SD" last-update="2020-08-08" originator="FDA" standard="SDTM" requiresDomainOrDataset="Yes">
<ruledescription>SCAT may not have the same value as DOMAIN, -TERM, -TESTCD, -TRT or BODSYS</ruledescription>
<ruledetaileddescription>Value of Subcategory (--SCAT) should not be  identical to that in Domain Abbreviation (DOMAIN); Reported Term (--TERM); Name of Measurement, Test, or Examination (--TESTCD); Name of Treatment (--TRT); Body System or Organ Class (--BODSYS)</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>INTERVENTIONS</domain>
<domain>EVENTS</domain>
<domain>FINDINGS</domain>
<domain>TI</domain>
<rulexquery><![CDATA[	
(: Rule SD1040 - Redundancy in paired variables values:
Value of Subcategory (--SCAT) should not be  identical to that in Domain Abbreviation (DOMAIN); Reported Term (--TERM); Name of Measurement, Test, or Examination (--TESTCD); Name of Treatment (--TRT); Body System or Organ Class (--BODSYS)
:)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external;
declare variable $defineversion external;
declare variable $datasetname external; 
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: iterate over all datasets that have a --SCAT variable in the interventions, events findings domains and the TI dataset :)
for $dataset in $definedoc//odm:ItemGroupDef[@Name=$datasetname][upper-case(@def:Class)='INTERVENTIONS' or upper-case(@def:Class)='EVENTS' or upper-case(@def:Class)='FINDINGS' 
		or upper-case(./def21:Class/@Name)='INTERVENTIONS' or upper-case(./def21:Class/@Name)='EVENTS' or upper-case(./def21:Class/@Name)='FINDINGS'
		or @Name='TI']
    let $name := $dataset/@Name
	let $dsname := (
		if($defineversion = '2.1') then $dataset/def21:leaf/@xlink:href
		else $dataset/def:leaf/@xlink:href
	)
    let $datasetdoc := (
		if($dsname) then doc(concat($base,$dsname))
		else ()
	)
    (: Get the OID of the --SCAT variable (if any) :)
    let $scatoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'SCAT') and string-length(@Name)=6]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a 
    )
    let $scatname := concat($name,'SCAT')
    (: similarly, get the OIDs of DOMAIN, TERM, TESTCD, TRT, BODSYS :)
    let $domainoid := (
        for $a in $definedoc//odm:ItemDef[@Name='DOMAIN']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
    )
    let $termoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'TERM') and string-length(@Name)=6]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a    
    )
    let $testcdoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'TESTCD') and string-length(@Name)=8]/@OID 
            where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
            return $a    
    )
    let $trtoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'TRT') and string-length(@Name)=5]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a    
    )
    let $bodsysoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'BODSYS') and string-length(@Name)=7]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a    
    )
    (: iterate over all the records that do have a value for --CAT :)
    for $record in $datasetdoc[$scatoid]//odm:ItemGroupData[odm:ItemData[@ItemOID=$scatoid]]
        let $recnum := $record/@data:ItemGroupDataSeq
        let $scatvalue := $record/odm:ItemData[@ItemOID=$scatoid]/@Value
        (: and get the values of the DOMAIN, TERM, TESTCD, TRT, BODSYS variables (when present) :)
        let $domainvalue := $record/odm:ItemData[@ItemOID=$domainoid]/@Value
        let $termvalue := $record/odm:ItemData[@ItemOID=$termoid]/@Value
        let $testcdvalue := $record/odm:ItemData[@ItemOID=$testcdoid]/@Value
        let $trtvalue := $record/odm:ItemData[@ItemOID=$trtoid]/@Value
        let $bodsysvalue := $record/odm:ItemData[@ItemOID=$bodsysoid]/@Value
        (: we do already have selected on the presence of a value for the CAT variable,
        now compare it with the values of the DOMAIN, TERM, TESTCD, TRT, BODSYS variables
        (when present) :)
        where $scatvalue=$domainvalue or $scatvalue=$termvalue or $scatvalue=$testcdvalue or $scatvalue=$trtvalue or $scatvalue=$bodsysvalue
        return <warning rule="SD1040" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">Redundancy in paired variables values for {data($scatname)} with value={data($scatvalue)}.</warning>
]]></rulexquery>
</sdsrule>


<!-- TODO? use "group by" for faster execution  -->
<sdsrule originator="FDA" id="SD1272" standard="SDTM" last-update="2020-08-08">
	<ruledescription>The value of --TESTCD must be different from 'OTHER'</ruledescription>
	<ruledetaileddescription>Test Code variables (--TESTCD) should not be populated with values of 'OTHER'</ruledetaileddescription>
	<igversion>3.1.2</igversion>
	<igversion>3.1.3</igversion>
	<igversion>3.2</igversion>
	<domain>FINDINGS</domain>
	<synonym context="CDISC">CG0341</synonym>
	<rulexquery><![CDATA[
(: Rule SD1272 - --TESTCD != 'OTHER' :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
declare namespace xs="http://www.w3.org/2001/XMLSchema";
declare namespace functx = "http://www.functx.com";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external;
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define)) 
(: iterate over all dataset definitions :)
for $itemgroupdef in $definedoc//odm:ItemGroupDef[upper-case(@def:Class)='FINDINGS' or upper-case(./def21:Class/@Name)='FINDINGS']
    let $name := $itemgroupdef/@Name
    (: we need the OID of --TESTCD (if any) and the complete name :)
    let $testcdoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'TESTCD')]/@OID 
        where $a = $itemgroupdef/odm:ItemRef/@ItemOID
        return $a
    )
    (: and its full name :)
    let $testcdname := $definedoc//odm:ItemDef[@OID=$testcdoid]/@Name
    (: get the location of the dataset and the document itself :)
    let $datasetlocation := (
		if($defineversion = '2.1') then $itemgroupdef/def21:leaf/@xlink:href
		else $itemgroupdef/def:leaf/@xlink:href
	)
    let $datasetdoc := (
		if($datasetlocation) then doc(concat($base,$datasetlocation))
		else ()
	)
    (: Iterate over all records in the dataset for which there is a --TESTCD variable :)
    for $record in $datasetdoc//odm:ItemGroupData[odm:ItemData/@ItemOID=$testcdoid]
        let $recnum := $record/@data:ItemGroupDataSeq
        let $testcd := $record/odm:ItemData[@ItemOID=$testcdoid]/@Value
        (: The value of --TESTCD is not allowed to be 'OTHER' (case insensitive?)  :)
        where upper-case($testcd) = 'OTHER'
        return <error rule="SD1272" dataset="{data($name)}" variable="{data($testcdname)}" recordnumber="{data($recnum)}" rulelastupdate="2020-08-08">Value of {data($testcdname)}='{data($testcd)}' is not allowed to be equal to 'OTHER'</error>						
		]]>
	</rulexquery>
</sdsrule>

<!-- TODO? use "group by" for faster execution  -->
<sdsrule originator="FDA" id="SD1272-SD" standard="SDTM" last-update="2020-08-08" requiresDomainOrDataset="Yes">
	<ruledescription>The value of --TESTCD must be different from 'OTHER'</ruledescription>
	<ruledetaileddescription>Test Code variables (--TESTCD) should not be populated with values of 'OTHER'</ruledetaileddescription>
	<igversion>3.1.2</igversion>
	<igversion>3.1.3</igversion>
	<igversion>3.2</igversion>
	<domain>FINDINGS</domain>
	<synonym context="CDISC">CG0341</synonym>
	<rulexquery><![CDATA[
(: Rule SD1272 - --TESTCD != 'OTHER' :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
declare namespace xs="http://www.w3.org/2001/XMLSchema";
declare namespace functx = "http://www.functx.com";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external;
declare variable $defineversion external;
declare variable $datasetname external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define)) 
(: iterate over all provided dataset definitions :)
for $itemgroupdef in $definedoc//odm:ItemGroupDef[@Name=$datasetname][upper-case(@def:Class)='FINDINGS' or upper-case(./def21:Class/@Name)='FINDINGS']
    let $name := $itemgroupdef/@Name
    (: we need the OID of --TESTCD (if any) and the complete name :)
    let $testcdoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'TESTCD')]/@OID 
        where $a = $itemgroupdef/odm:ItemRef/@ItemOID
        return $a
    )
    (: and its full name :)
    let $testcdname := $definedoc//odm:ItemDef[@OID=$testcdoid]/@Name
    (: get the location of the dataset and the document itself :)
    let $datasetlocation := (
		if($defineversion = '2.1') then $itemgroupdef/def21:leaf/@xlink:href
		else $itemgroupdef/def:leaf/@xlink:href
	)
    let $datasetdoc := (
		if($datasetlocation) then doc(concat($base,$datasetlocation))
		else ()
	)
    (: Iterate over all records in the dataset for which there is a --TESTCD variable :)
    for $record in $datasetdoc//odm:ItemGroupData[odm:ItemData/@ItemOID=$testcdoid]
        let $recnum := $record/@data:ItemGroupDataSeq
        let $testcd := $record/odm:ItemData[@ItemOID=$testcdoid]/@Value
        (: The value of --TESTCD is not allowed to be 'OTHER' (case insensitive?)  :)
        where upper-case($testcd) = 'OTHER'
        return <error rule="SD1272" dataset="{data($name)}" variable="{data($testcdname)}" recordnumber="{data($recnum)}" rulelastupdate="2020-08-08">Value of {data($testcdname)}='{data($testcd)}' is not allowed to be equal to 'OTHER'</error>						
		]]>
	</rulexquery>
</sdsrule>

<sdsrule originator="FDA" id="SD1273" standard="SDTM" last-update="2020-08-08">
	<ruledescription>--TRT is not allowed to have the value 'OTHER'</ruledescription>
	<ruledetaileddescription>Test Code variables (--TESTCD) should not be populated with values of 'OTHER'</ruledetaileddescription>
	<igversion>3.1.2</igversion>
	<igversion>3.1.3</igversion>
	<igversion>3.2</igversion>
	<domain>INTERVENTIONS</domain>
	<synonym context="CDISC">CG0342</synonym>
	<rulexquery><![CDATA[	
(: Rule SD1272 - --TRT != 'OTHER' :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
declare namespace xs="http://www.w3.org/2001/XMLSchema";
declare namespace functx = "http://www.functx.com";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external;
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define)) 
(: iterate over all dataset definitions :)
for $itemgroupdef in $definedoc//odm:ItemGroupDef[upper-case(@def:Class)='INTERVENTIONS' or upper-case(./def21:Class/@Name)='INTERVENTIONS']
    let $name := $itemgroupdef/@Name
    (: we need the OID of --TRT (if any) and the complete name :)
    let $trtoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'TRT') and string-length(@Name)=5]/@OID 
        where $a = $itemgroupdef/odm:ItemRef/@ItemOID
        return $a
    )
    (: and its full name :)
    let $trtname := $definedoc//odm:ItemDef[@OID=$trtoid]/@Name
    (: get the location of the dataset and the document itself :)
    let $datasetlocation := (
		if($defineversion = '2.1') then $itemgroupdef/def21:leaf/@xlink:href
		else $itemgroupdef/def:leaf/@xlink:href
	)
    let $datasetdoc := (
		if($datasetlocation) then doc(concat($base,$datasetlocation))
		else ()
	)
    (: Iterate over all records in the dataset for which there is a --TRT variable :)
    for $record in $datasetdoc//odm:ItemGroupData[odm:ItemData/@ItemOID=$trtoid]
        let $recnum := $record/@data:ItemGroupDataSeq
        let $trt := $record/odm:ItemData[@ItemOID=$trtoid]/@Value
        (: The value of --TRT is not allowed to be 'OTHER' (case insensitive?)  :)
        where upper-case($trt) = 'OTHER'
        return <error rule="SD1273" dataset="{data($name)}" variable="{data($trtname)}" recordnumber="{data($recnum)}" rulelastupdate="2020-08-08">Value of {data($trtname)}='{data($trt)}' is not allowed to be equal to 'OTHER'</error>						
		]]>
	</rulexquery>
</sdsrule>

<sdsrule originator="FDA" id="SD1273-SD" standard="SDTM" last-update="2020-08-08" requiresDomainOrDataset="Yes">
	<ruledescription>--TRT is not allowed to have the value 'OTHER'</ruledescription>
	<ruledetaileddescription>Test Code variables (--TESTCD) should not be populated with values of 'OTHER'</ruledetaileddescription>
	<igversion>3.1.2</igversion>
	<igversion>3.1.3</igversion>
	<igversion>3.2</igversion>
	<domain>INTERVENTIONS</domain>
	<synonym context="CDISC">CG0342</synonym>
	<rulexquery><![CDATA[	
(: Rule SD1272 - --TRT != 'OTHER' :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
declare namespace xs="http://www.w3.org/2001/XMLSchema";
declare namespace functx = "http://www.functx.com";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external;
declare variable $defineversion external;
declare variable $datasetname external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define)) 
(: iterate over all provided dataset definitions :)
for $itemgroupdef in $definedoc//odm:ItemGroupDef[@Name=$datasetname][upper-case(@def:Class)='INTERVENTIONS' or upper-case(./def21:Class/@Name)='INTERVENTIONS']
    let $name := $itemgroupdef/@Name
    (: we need the OID of --TRT (if any) and the complete name :)
    let $trtoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'TRT') and string-length(@Name)=5]/@OID 
        where $a = $itemgroupdef/odm:ItemRef/@ItemOID
        return $a
    )
    (: and its full name :)
    let $trtname := $definedoc//odm:ItemDef[@OID=$trtoid]/@Name
    (: get the location of the dataset and the document itself :)
    let $datasetlocation := (
		if($defineversion = '2.1') then $itemgroupdef/def21:leaf/@xlink:href
		else $itemgroupdef/def:leaf/@xlink:href
	)
    let $datasetdoc := (
		if($datasetlocation) then doc(concat($base,$datasetlocation))
		else ()
	)
    (: Iterate over all records in the dataset for which there is a --TRT variable :)
    for $record in $datasetdoc//odm:ItemGroupData[odm:ItemData/@ItemOID=$trtoid]
        let $recnum := $record/@data:ItemGroupDataSeq
        let $trt := $record/odm:ItemData[@ItemOID=$trtoid]/@Value
        (: The value of --TRT is not allowed to be 'OTHER' (case insensitive?)  :)
        where upper-case($trt) = 'OTHER'
        return <error rule="SD1273" dataset="{data($name)}" variable="{data($trtname)}" recordnumber="{data($recnum)}" rulelastupdate="2020-08-08">Value of {data($trtname)}='{data($trt)}' is not allowed to be equal to 'OTHER'</error>						
		]]>
	</rulexquery>
</sdsrule>

<sdsrule originator="FDA" id="SD1274" standard="SDTM" last-update="2020-08-08">
	<ruledescription>The value of --TERM must be different from 'OTHER'</ruledescription>
	<ruledetaileddescription>Reported Term variables (--TERM) should not be populated with values of 'OTHER'</ruledetaileddescription>
	<igversion>3.1.2</igversion>
	<igversion>3.1.3</igversion>
	<igversion>3.2</igversion>
	<domain>EVENTS</domain>
	<synonym context="CDISC">CG0343</synonym>
	<rulexquery><![CDATA[	
(: Rule SD1274 - --TERM != 'OTHER' :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
declare namespace xs="http://www.w3.org/2001/XMLSchema";
declare namespace functx = "http://www.functx.com";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external;
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define)) 
(: iterate over all dataset definitions :)
for $itemgroupdef in $definedoc//odm:ItemGroupDef[upper-case(@def:Class)='EVENTS' or upper-case(./def21:Class/@Name)='EVENTS']
    let $name := $itemgroupdef/@Name
    (: we need the OID of --TERM (if any) and the complete name :)
    let $termoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'TERM') and string-length(@Name)=6]/@OID 
        where $a = $itemgroupdef/odm:ItemRef/@ItemOID
        return $a
    )
    (: and its full name :)
    let $termname := $definedoc//odm:ItemDef[@OID=$termoid]/@Name
    (: get the location of the dataset and the document itself :)
    let $datasetlocation := (
		if($defineversion = '2.1') then $itemgroupdef/def21:leaf/@xlink:href
		else $itemgroupdef/def:leaf/@xlink:href
	)
     let $datasetdoc := (
		if($datasetlocation) then doc(concat($base,$datasetlocation))
		else ()
	)
    (: Iterate over all records in the dataset for which there is a --TERM variable :)
    for $record in $datasetdoc//odm:ItemGroupData[odm:ItemData/@ItemOID=$termoid]
        let $recnum := $record/@data:ItemGroupDataSeq
        let $term := $record/odm:ItemData[@ItemOID=$termoid]/@Value
        (: The value of --TERM is not allowed to be 'OTHER' (case insensitive?)  :)
        where upper-case($term) = 'OTHER'
        return <error rule="SD1274" dataset="{data($name)}" variable="{data($termname)}" recordnumber="{data($recnum)}" rulelastupdate="2020-08-08">Value of {data($termname)}='{data($term)}' is not allowed to be equal to 'OTHER'</error>						
		]]>
	</rulexquery>
</sdsrule>

<sdsrule originator="FDA" id="SD1274-SD" standard="SDTM" last-update="2020-08-08" requiresDomainOrDataset="Yes">
	<ruledescription>The value of --TERM must be different from 'OTHER'</ruledescription>
	<ruledetaileddescription>Reported Term variables (--TERM) should not be populated with values of 'OTHER'</ruledetaileddescription>
	<igversion>3.1.2</igversion>
	<igversion>3.1.3</igversion>
	<igversion>3.2</igversion>
	<domain>EVENTS</domain>
	<synonym context="CDISC">CG0343</synonym>
	<rulexquery><![CDATA[	
(: Rule SD1274 - --TERM != 'OTHER' :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
declare namespace xs="http://www.w3.org/2001/XMLSchema";
declare namespace functx = "http://www.functx.com";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external;
declare variable $defineversion external;
declare variable $datasetname external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define)) 
(: iterate over all provided dataset definitions :)
for $itemgroupdef in $definedoc//odm:ItemGroupDef[@Name=$datasetname][upper-case(@def:Class)='EVENTS' or upper-case(./def21:Class/@Name)='EVENTS']
    let $name := $itemgroupdef/@Name
    (: we need the OID of --TERM (if any) and the complete name :)
    let $termoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'TERM') and string-length(@Name)=6]/@OID 
        where $a = $itemgroupdef/odm:ItemRef/@ItemOID
        return $a
    )
    (: and its full name :)
    let $termname := $definedoc//odm:ItemDef[@OID=$termoid]/@Name
    (: get the location of the dataset and the document itself :)
    let $datasetlocation := (
		if($defineversion = '2.1') then $itemgroupdef/def21:leaf/@xlink:href
		else $itemgroupdef/def:leaf/@xlink:href
	)
     let $datasetdoc := (
		if($datasetlocation) then doc(concat($base,$datasetlocation))
		else ()
	)
    (: Iterate over all records in the dataset for which there is a --TERM variable :)
    for $record in $datasetdoc//odm:ItemGroupData[odm:ItemData/@ItemOID=$termoid]
        let $recnum := $record/@data:ItemGroupDataSeq
        let $term := $record/odm:ItemData[@ItemOID=$termoid]/@Value
        (: The value of --TERM is not allowed to be 'OTHER' (case insensitive?)  :)
        where upper-case($term) = 'OTHER'
        return <error rule="SD1274" dataset="{data($name)}" variable="{data($termname)}" recordnumber="{data($recnum)}" rulelastupdate="2020-08-08">Value of {data($termname)}='{data($term)}' is not allowed to be equal to 'OTHER'</error>						
		]]>
	</rulexquery>
</sdsrule>

<sdsrule originator="FDA" id="SD1275" standard="SDTM" last-update="2020-08-08" timeintensive="Yes">
	<ruledescription>The value of --TESTCD must be different from 'MULTIPLE'</ruledescription>
	<ruledetaileddescription>Test Code variables (--TESTCD) should not be populated with values of 'MULTIPLE'</ruledetaileddescription>
	<igversion>3.1.2</igversion>
	<igversion>3.1.3</igversion>
	<igversion>3.2</igversion>
	<domain>FINDINGS</domain>
	<synonym context="CDISC">CG0344</synonym>
	<rulexquery><![CDATA[
(: Rule SD1275 - --TESTCD != 'MULTIPLE' :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
declare namespace xs="http://www.w3.org/2001/XMLSchema";
declare namespace functx = "http://www.functx.com";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external;
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define)) 
(: iterate over all dataset definitions within FINDINGS :)
for $itemgroupdef in $definedoc//odm:ItemGroupDef[upper-case(@def:Class)='FINDINGS' or upper-case(./def21:Class/@Name)='FINDINGS']
    let $name := $itemgroupdef/@Name
    (: we need the OID of --TESTCD (if any) and the complete name :)
    let $testcdoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'TESTCD')]/@OID 
        where $a = $itemgroupdef/odm:ItemRef/@ItemOID
        return $a
    )
    (: and its full name :)
    let $testcdname := $definedoc//odm:ItemDef[@OID=$testcdoid]/@Name
    (: get the location of the dataset and the document itself :)
    let $datasetlocation := (
		if($defineversion = '2.1') then $itemgroupdef/def21:leaf/@xlink:href
		else $itemgroupdef/def:leaf/@xlink:href
	)
    let $datasetdoc := (
		if($datasetlocation) then doc(concat($base,$datasetlocation))
		else ()
	)
    (: Iterate over all records in the dataset for which there is a --TESTCD variable :)
    for $record in $datasetdoc//odm:ItemGroupData[odm:ItemData/@ItemOID=$testcdoid]
        let $recnum := $record/@data:ItemGroupDataSeq
        let $testcd := $record/odm:ItemData[@ItemOID=$testcdoid]/@Value
        (: The value of --TESTCD is not allowed to be 'MULTIPLE' (case insensitive?)  :)
        where upper-case($testcd) = 'MULTIPLE'
        return <error rule="SD1275" dataset="{data($name)}" variable="{data($testcdname)}" recordnumber="{data($recnum)}" rulelastupdate="2020-08-08">Value of {data($testcdname)}='{data($testcd)}' is not allowed to be equal to 'MULTIPLE'</error>									
		]]>
	</rulexquery>
</sdsrule>

<sdsrule originator="FDA" id="SD1275-SD" standard="SDTM" last-update="2020-08-08" requiresDomainOrDataset="Yes">
	<ruledescription>The value of --TESTCD must be different from 'MULTIPLE'</ruledescription>
	<ruledetaileddescription>Test Code variables (--TESTCD) should not be populated with values of 'MULTIPLE'</ruledetaileddescription>
	<igversion>3.1.2</igversion>
	<igversion>3.1.3</igversion>
	<igversion>3.2</igversion>
	<domain>FINDINGS</domain>
	<synonym context="CDISC">CG0344</synonym>
	<rulexquery><![CDATA[
(: Rule SD1275 - --TESTCD != 'MULTIPLE' :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
declare namespace xs="http://www.w3.org/2001/XMLSchema";
declare namespace functx = "http://www.functx.com";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external;
declare variable $defineversion external;
declare variable $datasetname external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define)) 
(: iterate over all dataset definitions within FINDINGS :)
for $itemgroupdef in $definedoc//odm:ItemGroupDef[@Name=$datasetname][upper-case(@def:Class)='FINDINGS' or upper-case(./def21:Class/@Name)='FINDINGS']
    let $name := $itemgroupdef/@Name
    (: we need the OID of --TESTCD (if any) and the complete name :)
    let $testcdoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'TESTCD')]/@OID 
        where $a = $itemgroupdef/odm:ItemRef/@ItemOID
        return $a
    )
    (: and its full name :)
    let $testcdname := $definedoc//odm:ItemDef[@OID=$testcdoid]/@Name
    (: get the location of the dataset and the document itself :)
    let $datasetlocation := (
		if($defineversion = '2.1') then $itemgroupdef/def21:leaf/@xlink:href
		else $itemgroupdef/def:leaf/@xlink:href
	)
    let $datasetdoc := (
		if($datasetlocation) then doc(concat($base,$datasetlocation))
		else ()
	)
    (: Iterate over all records in the dataset for which there is a --TESTCD variable :)
    for $record in $datasetdoc//odm:ItemGroupData[odm:ItemData/@ItemOID=$testcdoid]
        let $recnum := $record/@data:ItemGroupDataSeq
        let $testcd := $record/odm:ItemData[@ItemOID=$testcdoid]/@Value
        (: The value of --TESTCD is not allowed to be 'MULTIPLE' (case insensitive?)  :)
        where upper-case($testcd) = 'MULTIPLE'
        return <error rule="SD1275" dataset="{data($name)}" variable="{data($testcdname)}" recordnumber="{data($recnum)}" rulelastupdate="2020-08-08">Value of {data($testcdname)}='{data($testcd)}' is not allowed to be equal to 'MULTIPLE'</error>									
		]]>
	</rulexquery>
</sdsrule>
	
<sdsrule originator="FDA" id="SD1276" standard="SDTM" last-update="2020-08-08">
	<ruledescription>The value of --TRT must be different from 'MULTIPLE'</ruledescription>
	<ruledetaileddescription>Name of Treatment variables (--TRT) should not be populated with values of 'MULTIPLE'</ruledetaileddescription>
	<igversion>3.1.2</igversion>
	<igversion>3.1.3</igversion>
	<igversion>3.2</igversion>
	<domain>INTERVENTIONS</domain>
	<synonym context="CDISC">CG0345</synonym>
	<rulexquery><![CDATA[
(: Rule SD1276 - --TRT != 'MULTIPLE' :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
declare namespace xs="http://www.w3.org/2001/XMLSchema";
declare namespace functx = "http://www.functx.com";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external;
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define)) 
(: iterate over all dataset definitions :)
for $itemgroupdef in $definedoc//odm:ItemGroupDef[upper-case(@def:Class)='INTERVENTIONS' or upper-case(./def21:Class/@Name)='INTERVENTIONS']
    let $name := $itemgroupdef/@Name
    (: we need the OID of --TRT (if any) and the complete name :)
    let $trtoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'TRT') and string-length(@Name)=5]/@OID 
        where $a = $itemgroupdef/odm:ItemRef/@ItemOID
        return $a
    )
    (: and its full name :)
    let $trtname := $definedoc//odm:ItemDef[@OID=$trtoid]/@Name
    (: get the location of the dataset and the document itself :)
    let $datasetlocation := (
		if($defineversion = '2.1') then $itemgroupdef/def21:leaf/@xlink:href
		else $itemgroupdef/def:leaf/@xlink:href
	)
    let $datasetdoc := (
		if($datasetlocation) then doc(concat($base,$datasetlocation))
		else ()
	)
    (: Iterate over all records in the dataset for which there is a --TRT variable :)
    for $record in $datasetdoc//odm:ItemGroupData[odm:ItemData/@ItemOID=$trtoid]
        let $recnum := $record/@data:ItemGroupDataSeq
        let $trt := $record/odm:ItemData[@ItemOID=$trtoid]/@Value
        (: The value of --TRT is not allowed to be 'MULTIPLE' (case insensitive?)  :)
        where upper-case($trt) = 'MULTIPLE'
        return <error rule="SD1276" dataset="{data($name)}" variable="{data($trtname)}" recordnumber="{data($recnum)}" rulelastupdate="2020-08-08">Value of {data($trtname)}='{data($trt)}' is not allowed to be equal to 'MULTIPLE'</error>									
		]]>
	</rulexquery>
</sdsrule>

<sdsrule originator="FDA" id="SD1276-SD" standard="SDTM" last-update="2020-08-08" requiresDomainOrDataset="Yes">
	<ruledescription>The value of --TRT must be different from 'MULTIPLE'</ruledescription>
	<ruledetaileddescription>Name of Treatment variables (--TRT) should not be populated with values of 'MULTIPLE'</ruledetaileddescription>
	<igversion>3.1.2</igversion>
	<igversion>3.1.3</igversion>
	<igversion>3.2</igversion>
	<domain>INTERVENTIONS</domain>
	<synonym context="CDISC">CG0345</synonym>
	<rulexquery><![CDATA[
(: Rule SD1276 - --TRT != 'MULTIPLE' :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
declare namespace xs="http://www.w3.org/2001/XMLSchema";
declare namespace functx = "http://www.functx.com";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external;
declare variable $defineversion external;
declare variable $datasetname external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define)) 
(: iterate over all dataset definitions :)
for $itemgroupdef in $definedoc//odm:ItemGroupDef[@Name=$datasetname][upper-case(@def:Class)='INTERVENTIONS' or upper-case(./def21:Class/@Name)='INTERVENTIONS']
    let $name := $itemgroupdef/@Name
    (: we need the OID of --TRT (if any) and the complete name :)
    let $trtoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'TRT') and string-length(@Name)=5]/@OID 
        where $a = $itemgroupdef/odm:ItemRef/@ItemOID
        return $a
    )
    (: and its full name :)
    let $trtname := $definedoc//odm:ItemDef[@OID=$trtoid]/@Name
    (: get the location of the dataset and the document itself :)
    let $datasetlocation := (
		if($defineversion = '2.1') then $itemgroupdef/def21:leaf/@xlink:href
		else $itemgroupdef/def:leaf/@xlink:href
	)
    let $datasetdoc := (
		if($datasetlocation) then doc(concat($base,$datasetlocation))
		else ()
	)
    (: Iterate over all records in the dataset for which there is a --TRT variable :)
    for $record in $datasetdoc//odm:ItemGroupData[odm:ItemData/@ItemOID=$trtoid]
        let $recnum := $record/@data:ItemGroupDataSeq
        let $trt := $record/odm:ItemData[@ItemOID=$trtoid]/@Value
        (: The value of --TRT is not allowed to be 'MULTIPLE' (case insensitive?)  :)
        where upper-case($trt) = 'MULTIPLE'
        return <error rule="SD1276" dataset="{data($name)}" variable="{data($trtname)}" recordnumber="{data($recnum)}" rulelastupdate="2020-08-08">Value of {data($trtname)}='{data($trt)}' is not allowed to be equal to 'MULTIPLE'</error>									
		]]>
	</rulexquery>
</sdsrule>

<sdsrule originator="FDA" id="SD1277" standard="SDTM" last-update="2020-08-08">
	<ruledescription>The value of --TERM must be different from 'MULTIPLE'</ruledescription>
	<ruledetaileddescription>Reported Term variables (--TERM) should not be populated with values of 'MULTIPLE'</ruledetaileddescription>
	<igversion>3.1.2</igversion>
	<igversion>3.1.3</igversion>
	<igversion>3.2</igversion>
	<domain>EVENTS</domain>
	<synonym context="CDISC">CG0346</synonym>
	<rulexquery><![CDATA[	
(: Rule SD1277 - --TERM != 'MULTIPLE' :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
declare namespace xs="http://www.w3.org/2001/XMLSchema";
declare namespace functx = "http://www.functx.com";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external;
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define)) 
(: iterate over all dataset definitions :)
for $itemgroupdef in $definedoc//odm:ItemGroupDef[upper-case(@def:Class)='EVENTS' or upper-case(./def21:Class/@Name)='EVENTS']
    let $name := $itemgroupdef/@Name
    (: we need the OID of --TERM (if any) and the complete name :)
    let $termoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'TERM') and string-length(@Name)=6]/@OID 
        where $a = $itemgroupdef/odm:ItemRef/@ItemOID
        return $a
    )
    (: and its full name :)
    let $termname := $definedoc//odm:ItemDef[@OID=$termoid]/@Name
    (: get the location of the dataset and the document itself :)
    let $datasetlocation := (
		if($defineversion = '2.1') then $itemgroupdef/def21:leaf/@xlink:href
		else $itemgroupdef/def:leaf/@xlink:href
	)
    let $datasetdoc := (
		if($datasetlocation) then doc(concat($base,$datasetlocation))
		else ()
	)
    (: Iterate over all records in the dataset for which there is a --TERM variable :)
    for $record in $datasetdoc//odm:ItemGroupData[odm:ItemData/@ItemOID=$termoid]
        let $recnum := $record/@data:ItemGroupDataSeq
        let $term := $record/odm:ItemData[@ItemOID=$termoid]/@Value
        (: The value of --TERM is not allowed to be 'MULTIPLE' (case insensitive?)  :)
        where upper-case($term) = 'MULTIPLE'
        return <error rule="SD1277" dataset="{data($name)}" variable="{data($termname)}" recordnumber="{data($recnum)}" rulelastupdate="2020-08-08">Value of {data($termname)}='{data($term)}' is not allowed to be equal to 'MULTIPLE'</error>						
		]]>
	</rulexquery>
</sdsrule>

<sdsrule originator="FDA" id="SD1277-SD" standard="SDTM" last-update="2020-08-08" requiresDomainOrDataset="Yes">
	<ruledescription>The value of --TERM must be different from 'MULTIPLE'</ruledescription>
	<ruledetaileddescription>Reported Term variables (--TERM) should not be populated with values of 'MULTIPLE'</ruledetaileddescription>
	<igversion>3.1.2</igversion>
	<igversion>3.1.3</igversion>
	<igversion>3.2</igversion>
	<domain>EVENTS</domain>
	<synonym context="CDISC">CG0346</synonym>
	<rulexquery><![CDATA[	
(: Rule SD1277 - --TERM != 'MULTIPLE' :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
declare namespace xs="http://www.w3.org/2001/XMLSchema";
declare namespace functx = "http://www.functx.com";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external;
declare variable $defineversion external;
declare variable $datasetname external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define)) 
(: iterate over all provided dataset definitions :)
for $itemgroupdef in $definedoc//odm:ItemGroupDef[@Name=$datasetname][upper-case(@def:Class)='EVENTS' or upper-case(./def21:Class/@Name)='EVENTS']
    let $name := $itemgroupdef/@Name
    (: we need the OID of --TERM (if any) and the complete name :)
    let $termoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'TERM') and string-length(@Name)=6]/@OID 
        where $a = $itemgroupdef/odm:ItemRef/@ItemOID
        return $a
    )
    (: and its full name :)
    let $termname := $definedoc//odm:ItemDef[@OID=$termoid]/@Name
    (: get the location of the dataset and the document itself :)
    let $datasetlocation := (
		if($defineversion = '2.1') then $itemgroupdef/def21:leaf/@xlink:href
		else $itemgroupdef/def:leaf/@xlink:href
	)
    let $datasetdoc := (
		if($datasetlocation) then doc(concat($base,$datasetlocation))
		else ()
	)
    (: Iterate over all records in the dataset for which there is a --TERM variable :)
    for $record in $datasetdoc//odm:ItemGroupData[odm:ItemData/@ItemOID=$termoid]
        let $recnum := $record/@data:ItemGroupDataSeq
        let $term := $record/odm:ItemData[@ItemOID=$termoid]/@Value
        (: The value of --TERM is not allowed to be 'MULTIPLE' (case insensitive?)  :)
        where upper-case($term) = 'MULTIPLE'
        return <error rule="SD1277" dataset="{data($name)}" variable="{data($termname)}" recordnumber="{data($recnum)}" rulelastupdate="2020-08-08">Value of {data($termname)}='{data($term)}' is not allowed to be equal to 'MULTIPLE'</error>						
		]]>
	</rulexquery>
</sdsrule>

<sdsrule id="SD0027" last-update="2020-08-08" originator="FDA" standard="SDTM" timeintensive="Yes">
<ruledescription>--ORRES must be provided, when --ORRESU is provided</ruledescription>
<ruledetaileddescription>Result or Finding in Original Units (--ORRES) should not be NULL, when Original Units (--ORRESU) is provided</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>FINDINGS</domain>
<rulexquery><![CDATA[	
(: Rule SD0027 - Missing value for --ORRES, when --ORRESU is provided:
Result or Finding in Original Units (--ORRES) should not be NULL, when Original Units (--ORRESU) is provided
All FINDINGS datasets
:)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: Iterate over all FINDINGS domains :)
for $dataset in $definedoc//odm:ItemGroupDef[upper-case(@def:Class)='FINDINGS' or upper-case(./def21:Class/@Name)='FINDINGS']
    let $name := $dataset/@Name
    let $datasetname := (
		if($defineversion = '2.1') then $dataset/def21:leaf/@xlink:href
		else $dataset/def:leaf/@xlink:href
	)
    let $datasetdoc := (
		if($datasetname) then doc(concat($base,$datasetname))
		else ()
	)
    (: and get the OIDs of the ORRES and ORRESU variables :)
    let $orresoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'ORRES')]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a 
    )
    let $orresname := $definedoc//odm:ItemDef[@OID=$orresoid]/@Name
    let $orresuoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'ORRESU')]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a     
    )
    let $orresuname := $definedoc//odm:ItemDef[@OID=$orresuoid]/@Name
    (: now iterate over all records in the dataset that have an ORRESU :)
    for $record in $datasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$orresuoid]]
        let $recnum := $record/@data:ItemGroupDataSeq
        (: and get the values for ORRESU and ORRES :)
        let $orresvalue := $record/odm:ItemData[@ItemOID=$orresoid]/@Value
        let $orresuvalue := $record/odm:ItemData[@ItemOID=$orresuoid]/@Value
        (: we already selected on the presence of ORRESU, check whether there is an ORRES :)
         where not($orresvalue) 
        return <warning rule="SD0027" variable="{data($orresname)}" dataset="{data($name)}" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">Missing value for {data($orresname)}, when {data($orresuname)}, value={data($orresuvalue)} is provided in dataset {data($datasetname)}</warning>	
]]></rulexquery>
</sdsrule>

<sdsrule id="SD0027-SD" last-update="2020-08-08" originator="FDA" standard="SDTM" requiresDomainOrDataset="Yes">
<ruledescription>--ORRES must be provided, when --ORRESU is provided</ruledescription>
<ruledetaileddescription>Result or Finding in Original Units (--ORRES) should not be NULL, when Original Units (--ORRESU) is provided</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>FINDINGS</domain>
<rulexquery><![CDATA[	
(: Rule SD0027 - Missing value for --ORRES, when --ORRESU is provided:
Result or Finding in Original Units (--ORRES) should not be NULL, when Original Units (--ORRESU) is provided
All FINDINGS datasets
:)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
declare variable $datasetname external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: Iterate over all provided datasets in the FINDINGS domains :)
for $dataset in $definedoc//odm:ItemGroupDef[@Name=$datasetname][upper-case(@def:Class)='FINDINGS' or upper-case(./def21:Class/@Name)='FINDINGS']
    let $name := $dataset/@Name
	let $dsname := (
		if($defineversion = '2.1') then $dataset/def21:leaf/@xlink:href
		else $dataset/def:leaf/@xlink:href
	)
    let $datasetdoc := (
		if($dsname) then doc(concat($base,$dsname))
		else ()
	)
    (: and get the OIDs of the ORRES and ORRESU variables :)
    let $orresoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'ORRES')]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a 
    )
    let $orresname := $definedoc//odm:ItemDef[@OID=$orresoid]/@Name
    let $orresuoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'ORRESU')]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a     
    )
    let $orresuname := $definedoc//odm:ItemDef[@OID=$orresuoid]/@Name
    (: now iterate over all records in the dataset that have an ORRESU :)
    for $record in $datasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$orresuoid]]
        let $recnum := $record/@data:ItemGroupDataSeq
        (: and get the values for ORRESU and ORRES :)
        let $orresvalue := $record/odm:ItemData[@ItemOID=$orresoid]/@Value
        let $orresuvalue := $record/odm:ItemData[@ItemOID=$orresuoid]/@Value
        (: we already selected on the presence of ORRESU, check whether there is an ORRES :)
         where not($orresvalue) 
        return <warning rule="SD0027" variable="{data($orresname)}" dataset="{data($name)}" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">Missing value for {data($orresname)}, when {data($orresuname)}, value={data($orresuvalue)} is provided in dataset {data($datasetname)}</warning>
]]></rulexquery>
</sdsrule>

<sdsrule id="SD0047" last-update="2020-08-08" originator="FDA" standard="SDTM" timeintensive="Yes">
<ruledescription>--ORRES must be populated, when --STAT or --DRVFL is not populated</ruledescription>
<ruledetaileddescription>Status (--STAT) should be set to 'NOT DONE' or Derived Flag (--DRVFL) should have a value of 'Y', when Result or Finding in Original Units (--ORRES) is NULL</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>FINDINGS</domain>
<rulexquery><![CDATA[	
(: TODO: "Message" and "Description" in FDA worksheet have a mismatch :)
(: Rule SD0047 - Missing value for --ORRES, when --STAT or --DRVFL is not populated:
Status (--STAT) should be set to 'NOT DONE' or Derived Flag (--DRVFL) should have a value of 'Y', when Result or Finding in Original Units (--ORRES) is NULL
FINDINGS DATASETS
:)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/'  :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: iterate over all FINDINGS datasets :)
for $dataset in $definedoc//odm:ItemGroupDef[upper-case(@def:Class)='FINDINGS' or upper-case(./def21:Class/@Name)='FINDINGS']
    let $name := $dataset/@Name
	let $datasetname := (
		if($defineversion = '2.1') then $dataset/def21:leaf/@xlink:href
		else $dataset/def:leaf/@xlink:href
	)
    let $datasetdoc := (
		if($datasetname) then doc(concat($base,$datasetname))
		else ()
	)
    (: get the OIDs of the --STAT, --DRVFL and --ORRES variable :)
    let $statoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'STAT')]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
    )
    let $statname := $definedoc//odm:ItemDef[@OID=$statoid]/@Name
    let $drvfloid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'DRVFL')]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
    )
    let $drvflname := $definedoc//odm:ItemDef[@OID=$drvfloid]/@Name
    let $orresoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'ORRES')]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
    )
    let $orresname := $definedoc//odm:ItemDef[@OID=$orresoid]/@Name
    (: iterate over all records that do NOT have a value for ORRES :)
    for $record in $datasetdoc//odm:ItemGroupData[not(odm:ItemData[@ItemOID=$orresoid])]
        let $recnum := $record/@data:ItemGroupDataSeq
        (: get the values for STAT and DRVFL :)
        let $statvalue := $record/odm:ItemData[@ItemOID=$statoid]/@Value
        let $drvflvalue := $record/odm:ItemData[@ItemOID=$drvfloid]/@Value
        (: Either STAT='NOT DONE', OR, DRVL='Y' :)
        where not($statvalue='NOT DONE') and not($drvflvalue='Y')
        return <warning rule="SD0047" dataset="{data($name)}" variable="{data($orresname)}" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">Status {data($statname)} should be set to 'NOT DONE' or Derived Flag {data($drvflname)} should have a value of 'Y', when Result or Finding in Original Units {data($orresname)} is NULL</warning>
]]></rulexquery>
</sdsrule>

<sdsrule id="SD0047-SD" last-update="2020-08-08" originator="FDA" standard="SDTM" requiresDomainOrDataset="Yes">
<ruledescription>--ORRES must be populated, when --STAT or --DRVFL is not populated</ruledescription>
<ruledetaileddescription>Status (--STAT) should be set to 'NOT DONE' or Derived Flag (--DRVFL) should have a value of 'Y', when Result or Finding in Original Units (--ORRES) is NULL</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>FINDINGS</domain>
<rulexquery><![CDATA[	
(: TODO: "Message" and "Description" in FDA worksheet have a mismatch :)
(: Rule SD0047 - Missing value for --ORRES, when --STAT or --DRVFL is not populated:
Status (--STAT) should be set to 'NOT DONE' or Derived Flag (--DRVFL) should have a value of 'Y', when Result or Finding in Original Units (--ORRES) is NULL
FINDINGS DATASETS
:)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
declare variable $datasetname external;
(: let $base := '/db/fda_submissions/cdisc01/'  :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: iterate over all provided FINDINGS datasets :)
for $dataset in $definedoc//odm:ItemGroupDef[@Name=$datasetname][upper-case(@def:Class)='FINDINGS' or upper-case(./def21:Class/@Name)='FINDINGS']
    let $name := $dataset/@Name
	let $dsname := (
		if($defineversion = '2.1') then $dataset/def21:leaf/@xlink:href
		else $dataset/def:leaf/@xlink:href
	)
    let $datasetdoc := (
		if($dsname) then doc(concat($base,$dsname))
		else ()
	)
    (: get the OIDs of the --STAT, --DRVFL and --ORRES variable :)
    let $statoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'STAT')]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
    )
    let $statname := $definedoc//odm:ItemDef[@OID=$statoid]/@Name
    let $drvfloid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'DRVFL')]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
    )
    let $drvflname := $definedoc//odm:ItemDef[@OID=$drvfloid]/@Name
    let $orresoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'ORRES')]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
    )
    let $orresname := $definedoc//odm:ItemDef[@OID=$orresoid]/@Name
    (: iterate over all records that do NOT have a value for ORRES :)
    for $record in $datasetdoc//odm:ItemGroupData[not(odm:ItemData[@ItemOID=$orresoid])]
        let $recnum := $record/@data:ItemGroupDataSeq
        (: get the values for STAT and DRVFL :)
        let $statvalue := $record/odm:ItemData[@ItemOID=$statoid]/@Value
        let $drvflvalue := $record/odm:ItemData[@ItemOID=$drvfloid]/@Value
        (: Either STAT='NOT DONE', OR, DRVL='Y' :)
        where not($statvalue='NOT DONE') and not($drvflvalue='Y')
        return <warning rule="SD0047" dataset="{data($name)}" variable="{data($orresname)}" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">Status {data($statname)} should be set to 'NOT DONE' or Derived Flag {data($drvflname)} should have a value of 'Y', when Result or Finding in Original Units {data($orresname)} is NULL</warning>
]]></rulexquery>
</sdsrule>

<sdsrule originator="FDA" id="SD1137" standard="SDTM" last-update="2020-08-08" timeintensive="Yes">
	<ruledescription>When --DRVFL = 'Y' then --ORRES must be null</ruledescription>
	<ruledetaileddescription>Character Result/Finding in Original Units (--ORRES) value should be NULL, when Derived Flag (--DRVFL) value is 'Y'</ruledetaileddescription>
	<igversion>3.1.2</igversion>
	<igversion>3.1.3</igversion>
	<igversion>3.2</igversion>
	<domain>FINDINGS</domain>
	<rulexquery><![CDATA[
(: Rule SD1137 - When --DRVFL != 'Y' then --ORRES != null :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
declare namespace xs="http://www.w3.org/2001/XMLSchema";
declare namespace functx = "http://www.functx.com";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' 
let $define := 'define2-0-0-example-sdtm.xml'  :)
let $definedoc := doc(concat($base,$define)) 
(: iterate over all dataset definitions :)
for $itemgroupdef in $definedoc//odm:ItemGroupDef[upper-case(@def:Class)='FINDINGS' or upper-case(./def21:Class/@Name)='FINDINGS']
    let $name := $itemgroupdef/@Name
    let $domainname := (
        if($itemgroupdef/@Domain) then $itemgroupdef/@Domain
        else substring($name,1,2)
    )
    (: we need the OID of --ORRES (if any) and the complete name :)
    let $orresoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'ORRES')]/@OID 
        where $a = $itemgroupdef/odm:ItemRef/@ItemOID
        return $a
    )
    (: and its full name :)
    let $orresname := $definedoc//odm:ItemDef[@OID=$orresoid]/@Name
    let $drvfloid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'DRVFL')]/@OID 
        where $a = $itemgroupdef/odm:ItemRef/@ItemOID
        return $a
    )
    let $drvflname := $definedoc//odm:ItemDef[@OID=$drvfloid]/@Name
    (: get the location of the dataset and the document itself :)
    let $datasetlocation := (
		if($defineversion = '2.1') then $itemgroupdef/def21:leaf/@xlink:href
		else $itemgroupdef/def:leaf/@xlink:href
	)
    let $datasetdoc := (
		if($datasetlocation) then doc(concat($base,$datasetlocation))
		else ()
	)
    (: iterate over all records in the dataset for which --DRVFL != 'Y' and --ORRES is defined :)
    for $record in $datasetdoc[$orresoid and $drvfloid]//odm:ItemGroupData[odm:ItemData[@ItemOID=$drvfloid]/@Value='Y']
        let $recnum := $record/@data:ItemGroupDataSeq
        (: and the value of --ORRES (if any) :)
        let $orres := $record/odm:ItemData[@ItemOID=$orresoid]/@Value
        (: --ORRES must be absent :)
        where string-length($orres) > 0 
        return <error rule="SD1137" dataset="{data($name)}" variable="{data($orresname)}" recordnumber="{data($recnum)}" rulelastupdate="2020-08-08">Value of {data($orresname)} must be null when {data($drvflname)}='Y'. The value found for {data($orresname)}='{data($orres)}'</error>												
	]]>
	</rulexquery>
</sdsrule>

<sdsrule originator="FDA" id="SD1137-SD" standard="SDTM" last-update="2020-08-08" requiresDomainOrDataset="Yes">
	<ruledescription>When --DRVFL = 'Y' then --ORRES must be null</ruledescription>
	<ruledetaileddescription>Character Result/Finding in Original Units (--ORRES) value should be NULL, when Derived Flag (--DRVFL) value is 'Y'</ruledetaileddescription>
	<igversion>3.1.2</igversion>
	<igversion>3.1.3</igversion>
	<igversion>3.2</igversion>
	<domain>FINDINGS</domain>
	<rulexquery><![CDATA[
(: Rule SD1137 - When --DRVFL != 'Y' then --ORRES != null :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
declare namespace xs="http://www.w3.org/2001/XMLSchema";
declare namespace functx = "http://www.functx.com";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
declare variable $datasetname external;
(: let $base := '/db/fda_submissions/cdisc01/' 
let $define := 'define2-0-0-example-sdtm.xml'  :)
let $definedoc := doc(concat($base,$define)) 
(: iterate over all provided dataset definitions :)
for $itemgroupdef in $definedoc//odm:ItemGroupDef[@Name=$datasetname][upper-case(@def:Class)='FINDINGS' or upper-case(./def21:Class/@Name)='FINDINGS']
    let $name := $itemgroupdef/@Name
    let $domainname := (
        if($itemgroupdef/@Domain) then $itemgroupdef/@Domain
        else substring($name,1,2)
    )
    (: we need the OID of --ORRES (if any) and the complete name :)
    let $orresoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'ORRES')]/@OID 
        where $a = $itemgroupdef/odm:ItemRef/@ItemOID
        return $a
    )
    (: and its full name :)
    let $orresname := $definedoc//odm:ItemDef[@OID=$orresoid]/@Name
    let $drvfloid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'DRVFL')]/@OID 
        where $a = $itemgroupdef/odm:ItemRef/@ItemOID
        return $a
    )
    let $drvflname := $definedoc//odm:ItemDef[@OID=$drvfloid]/@Name
    (: get the location of the dataset and the document itself :)
    let $datasetlocation := (
		if($defineversion = '2.1') then $itemgroupdef/def21:leaf/@xlink:href
		else $itemgroupdef/def:leaf/@xlink:href
	)
    let $datasetdoc := (
		if($datasetlocation) then doc(concat($base,$datasetlocation))
		else ()
	)
    (: iterate over all records in the dataset for which --DRVFL != 'Y' and --ORRES is defined :)
    for $record in $datasetdoc[$orresoid and $drvfloid]//odm:ItemGroupData[odm:ItemData[@ItemOID=$drvfloid]/@Value='Y']
        let $recnum := $record/@data:ItemGroupDataSeq
        (: and the value of --ORRES (if any) :)
        let $orres := $record/odm:ItemData[@ItemOID=$orresoid]/@Value
        (: --ORRES must be absent :)
        where string-length($orres) > 0 
        return <error rule="SD1137" dataset="{data($name)}" variable="{data($orresname)}" recordnumber="{data($recnum)}" rulelastupdate="2020-08-08">Value of {data($orresname)} must be null when {data($drvflname)}='Y'. The value found for {data($orresname)}='{data($orres)}'</error>												
	]]>
	</rulexquery>
</sdsrule>

<sdsrule originator="FDA" id="SD0016" standard="SDTM" last-update="2020-08-08" timeintensive="Yes">
	<ruledescription>When --DRVFL = 'Y' then --STRESC must be populated</ruledescription>
	<ruledetaileddescription>Character Result/Finding in Std Format (--STRESC) value should not be NULL, when Derived Flag (--DRVFL) value is 'Y'</ruledetaileddescription>
	<igversion>3.1.2</igversion>
	<igversion>3.1.3</igversion>
	<igversion>3.2</igversion>
	<domain>FINDINGS</domain>
	<rulexquery><![CDATA[
(: Rule SD0016 - When --DRVFL = 'Y' then --STRESC != null :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
declare namespace xs="http://www.w3.org/2001/XMLSchema";
declare namespace functx = "http://www.functx.com";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external;
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' 
let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define)) 
(: iterate over all dataset definitions :)
for $itemgroupdef in $definedoc//odm:ItemGroupDef[upper-case(@def:Class)='FINDINGS' or upper-case(./def21:Class/@Name)='FINDINGS']
    let $name := $itemgroupdef/@Name
    let $domainname := (
        if($itemgroupdef/@Domain) then $itemgroupdef/@Domain
        else substring($name,1,2)
    )
    (: we need the OID of --STRESC (if any) and the complete name :)
    let $strescoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'STRESC')]/@OID 
        where $a = $itemgroupdef/odm:ItemRef/@ItemOID
        return $a
    )
    (: and its full name :)
    let $strescname := $definedoc//odm:ItemDef[@OID=$strescoid]/@Name
    let $drvfloid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'DRVFL')]/@OID 
        where $a = $itemgroupdef/odm:ItemRef/@ItemOID
        return $a
    )
    let $drvflname := $definedoc//odm:ItemDef[@OID=$drvfloid]/@Name
    (: get the location of the dataset and the document itself :)
    let $datasetlocation := (
		if($defineversion = '2.1') then $itemgroupdef/def21:leaf/@xlink:href
		else $itemgroupdef/def:leaf/@xlink:href
	)
    let $datasetdoc := (
		if($datasetlocation) then doc(concat($base,$datasetlocation))
		else ()
	)
    (: iterate over all records in the dataset for which --DRVFL != 'Y' and --STRESC is defined :)
    for $record in $datasetdoc[$strescoid and $drvfloid]//odm:ItemGroupData[odm:ItemData[@ItemOID=$drvfloid]/@Value='Y']
        let $recnum := $record/@data:ItemGroupDataSeq
        (: and the value of --STRESC (if any) :)
        let $stresc := $record/odm:ItemData[@ItemOID=$strescoid]/@Value
        (: --STRESC must be populated :)
        where not($stresc) or string-length($stresc) = 0  
        return <error rule="SD0016" dataset="{data($name)}" variable="{data($strescname)}" recordnumber="{data($recnum)}" rulelastupdate="2020-08-08">Value of {data($strescname)} may not be null when {data($drvflname)}='Y'</error>												
	]]>
	</rulexquery>
</sdsrule>

<sdsrule originator="FDA" id="SD0016-SD" standard="SDTM" last-update="2020-08-08" requiresDomainOrDataset="Yes">
	<ruledescription>When --DRVFL = 'Y' then --STRESC must be populated</ruledescription>
	<ruledetaileddescription>Character Result/Finding in Std Format (--STRESC) value should not be NULL, when Derived Flag (--DRVFL) value is 'Y'</ruledetaileddescription>
	<igversion>3.1.2</igversion>
	<igversion>3.1.3</igversion>
	<igversion>3.2</igversion>
	<domain>FINDINGS</domain>
	<rulexquery><![CDATA[
(: Rule SD0016 - When --DRVFL = 'Y' then --STRESC != null :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
declare namespace xs="http://www.w3.org/2001/XMLSchema";
declare namespace functx = "http://www.functx.com";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external;
declare variable $defineversion external;
declare variable $datasetname external;
(: let $base := '/db/fda_submissions/cdisc01/' 
let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define)) 
(: iterate over all provided dataset definitions :)
for $itemgroupdef in $definedoc//odm:ItemGroupDef[@Name=$datasetname][upper-case(@def:Class)='FINDINGS' or upper-case(./def21:Class/@Name)='FINDINGS']
    let $name := $itemgroupdef/@Name
    let $domainname := (
        if($itemgroupdef/@Domain) then $itemgroupdef/@Domain
        else substring($name,1,2)
    )
    (: we need the OID of --STRESC (if any) and the complete name :)
    let $strescoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'STRESC')]/@OID 
        where $a = $itemgroupdef/odm:ItemRef/@ItemOID
        return $a
    )
    (: and its full name :)
    let $strescname := $definedoc//odm:ItemDef[@OID=$strescoid]/@Name
    let $drvfloid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'DRVFL')]/@OID 
        where $a = $itemgroupdef/odm:ItemRef/@ItemOID
        return $a
    )
    let $drvflname := $definedoc//odm:ItemDef[@OID=$drvfloid]/@Name
    (: get the location of the dataset and the document itself :)
    let $datasetlocation := (
		if($defineversion = '2.1') then $itemgroupdef/def21:leaf/@xlink:href
		else $itemgroupdef/def:leaf/@xlink:href
	)
    let $datasetdoc := (
		if($datasetlocation) then doc(concat($base,$datasetlocation))
		else ()
	)
    (: iterate over all records in the dataset for which --DRVFL != 'Y' and --STRESC is defined :)
    for $record in $datasetdoc[$strescoid and $drvfloid]//odm:ItemGroupData[odm:ItemData[@ItemOID=$drvfloid]/@Value='Y']
        let $recnum := $record/@data:ItemGroupDataSeq
        (: and the value of --STRESC (if any) :)
        let $stresc := $record/odm:ItemData[@ItemOID=$strescoid]/@Value
        (: --STRESC must be populated :)
        where not($stresc) or string-length($stresc) = 0  
        return <error rule="SD0016" dataset="{data($name)}" variable="{data($strescname)}" recordnumber="{data($recnum)}" rulelastupdate="2020-08-08">Value of {data($strescname)} may not be null when {data($drvflname)}='Y'</error>												
	]]>
	</rulexquery>
</sdsrule>

<!-- Rule SD1074: "Variable which can be used only in SEND" is not a real rule. It needs further specification -->

	
<sdsrule id="SD0075" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>IDVAR must be a valid variable name of the referenced domain</ruledescription>
<ruledetaileddescription>Identifying Variable (IDVAR) must have a valid value of variables from a referenced Domain</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>RELREC</domain>
<domain>SUPPQUAL</domain>
<domain>CO</domain>
<rulexquery><![CDATA[
(: Rule SD0075 - Invalid IDVAR:
Identifying Variable (IDVAR) must have a valid value of variables from a referenced Domain :)
(: Rule SD0075 - When IDVAR != null then IDVAR is variable in domain = RDOMAIN :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external;
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: iterate over all SUPP--, CO and RELREC dataset definitions :)
for $itemgroupdef in $definedoc//odm:ItemGroupDef[@Name='CO' or @Name='RELREC' or starts-with(@Name,'SUPP')]
    let $name := $itemgroupdef/@Name
    (: get the OID of RDOMAIN and of IDVAR:)
    let $rdomainoid := (
        for $a in $definedoc//odm:ItemDef[@Name='RDOMAIN']/@OID 
        where $a = $itemgroupdef/odm:ItemRef/@ItemOID
        return $a
    )
    let $idvaroid := (
        for $a in $definedoc//odm:ItemDef[@Name='IDVAR']/@OID 
        where $a = $itemgroupdef/odm:ItemRef/@ItemOID
        return $a
    )
    (: get the dataset location and the document :)
    let $datasetlocation := (
		if($defineversion = '2.1') then $itemgroupdef/def21:leaf/@xlink:href
		else $itemgroupdef/def:leaf/@xlink:href
	)
    let $datasetdoc := (
		if($datasetlocation) then doc(concat($base,$datasetlocation))
		else ()
	)
    (: iterate over all the records in the dataset for which there is a value of RDOMAIN and of IDVAR :)
    for $record in $datasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$rdomainoid] and odm:ItemData[@ItemOID=$idvaroid]]
        let $recnum := $record/@data:ItemGroupDataSeq
        (: get the value of RDOMAIN and of IDVAR :)
        let $rdomain := $record/odm:ItemData[@ItemOID=$rdomainoid]/@Value
        let $idvarvalue := $record/odm:ItemData[@ItemOID=$idvaroid]/@Value
        (: look up whether there is such a variable in the parent domain RDOMAIN :)
        where $idvarvalue and (not($definedoc//odm:ItemDef[@Name=$idvarvalue]) or not($definedoc//odm:ItemGroupDef[@Name=$rdomain or starts-with(@Name,$rdomain)]/odm:ItemRef[@ItemOID=$definedoc//odm:ItemDef[@Name=$idvarvalue]/@OID]))
        return <error rule="SD0075" dataset="{data($name)}" variable="IDVAR" recordnumber="{data($recnum)}" rulelastupdate="2020-08-08">Variable '{data($idvarvalue)}' was not found in the parent dataset of the referenced domain RDOMAIN='{data($rdomain)}' </error>
]]></rulexquery>
</sdsrule>

<!-- OLD version, before 2020-08-17
<sdsrule id="SD0077" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>Referenced record must be an existing record</ruledescription>
<ruledetaileddescription>Reference record defined by Related Domain Abbreviation (RDOMAIN), Unique Subject Identifier (USUBJID), Identifying Variable (IDVAR) and Identifying Variable Value (IDVARVAL) must exist in the target Domain</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>CO</domain>
<domain>RELREC</domain>
<domain>SUPPQUAL</domain>
<rulexquery><![CDATA[
(: Rule SD0077 - Invalid referenced record:
Reference record defined by Related Domain Abbreviation (RDOMAIN), Unique Subject Identifier (USUBJID), Identifying Variable (IDVAR) and Identifying Variable Value (IDVARVAL) must exist in the target Domain
:)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external;
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: Get the CO, RELREC and SUPPxx datasets :)
let $datasets := $definedoc//odm:ItemGroupDef[@Name='RELREC' or starts-with(@Name,'CO') or starts-with(@Name,'SUPP')]
(: iterate over these datasets :)
for $dataset in $datasets
    (: Get the name and location :)
    let $name := $dataset/@Name
	let $datasetname := (
		if($defineversion = '2.1') then $dataset/def21:leaf/@xlink:href
		else $dataset/def:leaf/@xlink:href
	)
    let $datasetdoc := (
		if($datasetname) then doc(concat($base,$datasetname))
		else ()
	)
    (: and take the OIDs of USUBJID, RDOMAIN, IDVAR and IDVARVAL :)
    let $usubjidoid := (
        for $a in $definedoc//odm:ItemDef[@Name='USUBJID']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
    )
    let $rdomainoid := (
        for $a in $definedoc//odm:ItemDef[@Name='RDOMAIN']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
    )
    let $idvaroid := (
        for $a in $definedoc//odm:ItemDef[@Name='IDVAR']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
    )
    let $idvarvaloid := (
        for $a in $definedoc//odm:ItemDef[@Name='IDVARVAL']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
    )
    (: iterate over all the records in the dataset :)
    for $record in $datasetdoc//odm:ItemGroupData
        let $recnum := $record/@data:ItemGroupDataSeq
        (: and retrieve the values of RDOMAIN, USUBJID, IDVAR and IDVARVAL :)
        let $rdomainval := $record/odm:ItemData[@ItemOID=$rdomainoid]/@Value
        let $usubjidvalue := $record/odm:ItemData[@ItemOID=$usubjidoid]/@Value
        let $idvarvalue := $record/odm:ItemData[@ItemOID=$idvaroid]/@Value
        let $idvarvalvalue := $record/odm:ItemData[@ItemOID=$idvarvaloid]/@Value
        (: find the corresponding record in the parent domain, which can have splitted datasets :)
        (: IMPORTANT REMARK: @Domain is only required in the context of a regulatory submission - so we must look at the first characters of the name too :)
        let $parentdatasets := $definedoc//odm:ItemGroupDef[@Domain=$rdomainval or substring(@Name,1,2)=$rdomainval]
        (: in case of splitted datasets, $parentrecordcount gives e.g. 0 0 21 :)
        let $parentrecordcount := (
            for $parentdataset in $parentdatasets
                let $parentname := $parentdataset/@Name
                let $parentdatasetname := (
					if($defineversion = '2.1') then $parentdataset/def21:leaf/@xlink:href
					else $parentdataset/def:leaf/@xlink:href
				)
                let $parentdatasetlocation := concat($base,$parentdatasetname)
                (: get the OID of USUBJID in the parent dataset  :)
                let $parentusubjidoid := (
                    for $a in $definedoc//odm:ItemDef[@Name='USUBJID']/@OID 
                    where $a = $definedoc//odm:ItemGroupDef[@Name=$parentname]/odm:ItemRef/@ItemOID
                    return $a
                )
                (: get the OID of the identifying variable in the parent dataset :)
                let $parentidvaroid := (
                    for $a in $definedoc//odm:ItemDef[@Name=$idvarvalue]/@OID 
                    where $a = $definedoc//odm:ItemGroupDef[@Name=$parentname]/odm:ItemRef/@ItemOID
                    return $a
                )
                (: try to find the record in this dataset :)
                return 
                    (: case DM - no IDVAR or IDVARVAL :)
                    if($rdomainval = 'DM') then 
                        count(doc($parentdatasetlocation)//odm:ItemGroupData[odm:ItemData[@ItemOID=$parentusubjidoid][@Value=$usubjidvalue]])
                    (: RDOMAIN is NOT DM - check on IDVAR/IDVARVAL :)
                    else (  
                        count(doc($parentdatasetlocation)//odm:ItemGroupData[odm:ItemData[@ItemOID=$parentusubjidoid][@Value=$usubjidvalue] and odm:ItemData[@ItemOID=$parentidvaroid][@Value=$idvarvalvalue]] )  
                    )
        )
        let $parentrecordcountsummed := sum($parentrecordcount) (: sum the counts over the (splitted) datasets :)
        where $parentrecordcountsummed = 0  (: parent no records found at all :)
        return <error rule="SD0077" dataset="{data($name)}" variable="RDOMAIN" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">Invalid referenced record - Dataset={data($name)}: no parent records found in RDOMAIN={data($rdomainval)} for USUBJID={data($usubjidvalue)}</error>
]]></rulexquery>
</sdsrule> -->

<sdsrule id="SD0077" last-update="2020-08-17" originator="FDA" standard="SDTM">
<ruledescription>Referenced record must be an existing record</ruledescription>
<ruledetaileddescription>Reference record defined by Related Domain Abbreviation (RDOMAIN), Unique Subject Identifier (USUBJID), Identifying Variable (IDVAR) and Identifying Variable Value (IDVARVAL) must exist in the target Domain</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>CO</domain>
<domain>RELREC</domain>
<domain>SUPPQUAL</domain>
<rulexquery><![CDATA[
(: Rule SD0077 - Invalid referenced record:
Reference record defined by Related Domain Abbreviation (RDOMAIN), Unique Subject Identifier (USUBJID), Identifying Variable (IDVAR) and Identifying Variable Value (IDVARVAL) must exist in the target Domain
:)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external;
declare variable $defineversion external;
(: let $defineversion := '2.1' :)
(: let $base := 'MSG_draft_define_2-1/' :)
(: let $define := 'define_2_1.xml' :)
let $definedoc := doc(concat($base,$define))
(: Get the CO, RELREC and SUPP-- datasets :)
let $datasets := $definedoc//odm:ItemGroupDef[@Name='RELREC' or starts-with(@Name,'CO') or starts-with(@Name,'SUPP')]
(: iterate over these datasets :)
for $dataset in $datasets
    (: Get the name and location :)
    let $name := $dataset/@Name
	let $datasetname := (
		if($defineversion = '2.1') then $dataset/def21:leaf/@xlink:href
		else $dataset/def:leaf/@xlink:href
	)
    let $datasetdoc := (
		if($datasetname) then doc(concat($base,$datasetname))
		else ()
	)
    (: and take the OIDs of USUBJID, RDOMAIN, IDVAR and IDVARVAL :)
    let $usubjidoid := (
        for $a in $definedoc//odm:ItemDef[@Name='USUBJID']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
    )
    let $rdomainoid := (
        for $a in $definedoc//odm:ItemDef[@Name='RDOMAIN']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
    )
    let $idvaroid := (
        for $a in $definedoc//odm:ItemDef[@Name='IDVAR']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
    )
    let $idvarvaloid := (
        for $a in $definedoc//odm:ItemDef[@Name='IDVARVAL']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
    )
    (: we also need the value for RELTYPE, 
        if it is "ONE" or "MANY" this is a general relationship, #
        and there is very little to check :)
    let $reltypeoid := (
    	for $a in $definedoc//odm:ItemDef[@Name='RELTYPE']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
    )
    (: iterate over all the records in the dataset :)
    for $record in $datasetdoc//odm:ItemGroupData
        let $recnum := $record/@data:ItemGroupDataSeq
        (: and retrieve the values of RDOMAIN, USUBJID, IDVAR and IDVARVAL :)
        let $rdomainval := $record/odm:ItemData[@ItemOID=$rdomainoid]/@Value
        let $usubjidvalue := $record/odm:ItemData[@ItemOID=$usubjidoid]/@Value
        let $idvarvalue := $record/odm:ItemData[@ItemOID=$idvaroid]/@Value
        let $idvarvalvalue := $record/odm:ItemData[@ItemOID=$idvarvaloid]/@Value
        (: we also need the value for RELTYPE, 
        if it is "ONE" or "MANY" this is a general relationship, #
        and there is very little to check :)
        let $reltypevalue := $record/odm:ItemData[@ItemOID=$reltypeoid]/@Value
        (: find the corresponding record in the parent domain, which can have splitted datasets :)
        (: IMPORTANT REMARK: @Domain is only required in the context of a regulatory submission - so we must look at the first characters of the name too :)
        let $parentdatasets := $definedoc//odm:ItemGroupDef[@Domain=$rdomainval or substring(@Name,1,2)=$rdomainval]
        (: in case of splitted datasets, $parentrecordcount gives e.g. 0 0 21 :)
        let $parentrecordcount := (
            for $parentdataset in $parentdatasets
                let $parentname := $parentdataset/@Name
                let $parentdatasetname := (
					if($defineversion = '2.1') then $parentdataset/def21:leaf/@xlink:href
					else $parentdataset/def:leaf/@xlink:href
				)
                let $parentdatasetdoc := (
                	if($parentdatasetname) then doc(concat($base,$parentdatasetname))
                    else ()
                )
                (: get the OID of USUBJID in the parent dataset  :)
                let $parentusubjidoid := (
                    for $a in $definedoc//odm:ItemDef[@Name='USUBJID']/@OID 
                    where $a = $definedoc//odm:ItemGroupDef[@Name=$parentname]/odm:ItemRef/@ItemOID
                    return $a
                )
                (: get the OID of the identifying variable in the parent dataset :)
                let $parentidvaroid := (
                    for $a in $definedoc//odm:ItemDef[@Name=$idvarvalue]/@OID 
                    where $a = $definedoc//odm:ItemGroupDef[@Name=$parentname]/odm:ItemRef/@ItemOID
                    return $a
                )
                (: try to find the record in this dataset :)
                return 
                	(: for ONE-MANY relatioships, we set the value of related records
                    to -1 artificially, as these are general relationships, not on the record level :)
                    if($reltypevalue='ONE' or $reltypevalue='MANY') then -1
                    (: case DM - no IDVAR or IDVARVAL :)
                    else if($rdomainval = 'DM' and $usubjidvalue) then 
                        count($parentdatasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$parentusubjidoid][@Value=$usubjidvalue]])
                    (: RDOMAIN is NOT DM - check on IDVAR/IDVARVAL :)
                    else if ($usubjidvalue) then (  
                        count($parentdatasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$parentusubjidoid][@Value=$usubjidvalue] and odm:ItemData[@ItemOID=$parentidvaroid][@Value=$idvarvalvalue]] )  
                    )
                    else -1
        )
        let $parentrecordcountsummed := sum($parentrecordcount) (: sum the counts over the (splitted) datasets :)
        (: return <test>dataset={data($name)} - RDOMAIN={data($rdomainval)} - IDVAR={data($idvarvalue)}  - IDVARVAL={data($idvarvalvalue)} - count={data($parentrecordcountsummed)}</test> :)
        where $parentrecordcountsummed = 0  (: parent no records found at all :)
        return <error rule="SD0077" dataset="{data($name)}" variable="RDOMAIN" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">Invalid referenced record - Dataset={data($name)}: no parent records found in RDOMAIN={data($rdomainval)} for USUBJID={data($usubjidvalue)}</error>
]]></rulexquery>
</sdsrule>

<sdsrule id="SD0018" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>Value for --TESTCD variable may not be longer than 8 characters</ruledescription>
<ruledetaileddescription>The value of a Short Name of Measurement, Test or Examination (--TESTCD) variable should be limited to 8 characters, cannot start with a number, and cannot contain characters other than letters in upper case, numbers, or underscores</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>FINDINGS</domain>
<rulexquery><![CDATA[
(: Rule SD0018 - Invalid value for --TESTCD variable:
The value of a Short Name of Measurement, Test or Examination (--TESTCD) variable should be limited to 8 characters, cannot start with a number, and cannot contain characters other than letters in upper case, numbers, or underscores :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
declare namespace functx = "http://www.functx.com";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external;
declare variable $defineversion external;
declare function functx:is-value-in-sequence
  ( $value as xs:anyAtomicType? ,
    $seq as xs:anyAtomicType* )  as xs:boolean {

   $value = $seq
 } ;
(: let $base := '/db/fda_submissions/cdiscpilot01/' :)
(: let $define := 'define_2_0.xml' :)
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
let $numbers := ("0","1","2","3","4","5","6","7","8","9")

(: iterate over all datasets in the define.xml :)
for $itemgroup in $definedoc//odm:ItemGroupDef[upper-case(@def:Class)='FINDINGS' or upper-case(./def21:Class/@Name)='FINDINGS']
	let $itemgroupdefname := $itemgroup/@Name
    (: get the dataset name and location :)
	let $datasetname := (
		if($defineversion = '2.1') then $itemgroup/def21:leaf/@xlink:href
		else $itemgroup/def:leaf/@xlink:href
	)
    let $datasetdoc := (
		if($datasetname) then doc(concat($base,$datasetname))
		else ()
	)
    (: get the OID of the "--TEST" variable :)
    let $testoid := (
    for $a in $definedoc//odm:ItemDef[ends-with(@Name,'TESTCD')]/@OID 
    where $a = $itemgroup/odm:ItemRef/@ItemOID 
    return $a 
    )
    let $testname := $definedoc//odm:ItemDef[@OID=$testoid]/@Name
    for $record in $datasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$testoid]]
        let $recnum := $record/@data:ItemGroupDataSeq
        let $value := $record/odm:ItemData[@ItemOID=$testoid]/@Value
        let $firstchar := substring($value,1,1)
        (: TODO: pattern matching does not seem to work well, though the pattern seems to be correct!  :)
        where string-length($value) > 8 or functx:is-value-in-sequence($firstchar,$numbers) or not(matches($value,'[A-Z_][A-Z0-9_]*'))
        return <error rule="SD0018" recordnumber="{data($recnum)}" dataset="{data($itemgroupdefname)}" variable="{$testname}" rulelastupdate="2020-08-08">Invalid value for Short Name of Measurement, Test or Examination (--TESTCD) variable: it should be limited to 8 characters, cannot start with a number, and cannot contain characters other than letters in upper case, numbers, or underscores. Value '{data($value)}' was found for variable {data($testname)} in dataset {data($datasetname)}.</error>
]]></rulexquery>
</sdsrule>

<sdsrule id="SD0018-SD" last-update="2020-08-08" originator="FDA" standard="SDTM" requiresDomainOrDataset="Yes">
<ruledescription>Value for --TESTCD variable may not be longer than 8 characters</ruledescription>
<ruledetaileddescription>The value of a Short Name of Measurement, Test or Examination (--TESTCD) variable should be limited to 8 characters, cannot start with a number, and cannot contain characters other than letters in upper case, numbers, or underscores</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>FINDINGS</domain>
<rulexquery><![CDATA[
(: Rule SD0018 - Invalid value for --TESTCD variable:
The value of a Short Name of Measurement, Test or Examination (--TESTCD) variable should be limited to 8 characters, cannot start with a number, and cannot contain characters other than letters in upper case, numbers, or underscores :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
declare namespace functx = "http://www.functx.com";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external;
declare variable $defineversion external;
declare variable $datasetname external;
declare function functx:is-value-in-sequence
  ( $value as xs:anyAtomicType? ,
    $seq as xs:anyAtomicType* )  as xs:boolean {

   $value = $seq
 } ;
(: let $base := '/db/fda_submissions/cdiscpilot01/' :)
(: let $define := 'define_2_0.xml' :)
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
let $numbers := ("0","1","2","3","4","5","6","7","8","9")

(: iterate over all datasets in the define.xml :)
for $itemgroup in $definedoc//odm:ItemGroupDef[@Name=$datasetname][upper-case(@def:Class)='FINDINGS' or upper-case(./def21:Class/@Name)='FINDINGS']
	let $itemgroupdefname := $itemgroup/@Name
    (: get the dataset name and location :)
    let $dsname := (
		if($defineversion = '2.1') then $itemgroup/def21:leaf/@xlink:href
		else $itemgroup/def:leaf/@xlink:href
	)
    let $datasetdoc := (
		if($dsname) then doc(concat($base,$dsname))
		else ()
	)
    (: get the OID of the "--TEST" variable :)
    let $testoid := (
    for $a in $definedoc//odm:ItemDef[ends-with(@Name,'TESTCD')]/@OID 
    where $a = $itemgroup/odm:ItemRef/@ItemOID 
    return $a 
    )
    let $testname := $definedoc//odm:ItemDef[@OID=$testoid]/@Name
    for $record in $datasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$testoid]]
        let $recnum := $record/@data:ItemGroupDataSeq
        let $value := $record/odm:ItemData[@ItemOID=$testoid]/@Value
        let $firstchar := substring($value,1,1)
        (: TODO: pattern matching does not seem to work well, though the pattern seems to be correct!  :)
        where string-length($value) > 8 or functx:is-value-in-sequence($firstchar,$numbers) or not(matches($value,'[A-Z_][A-Z0-9_]*'))
        return <error rule="SD0018" recordnumber="{data($recnum)}" dataset="{data($itemgroupdefname)}" variable="{$testname}" rulelastupdate="2020-08-08">Invalid value for Short Name of Measurement, Test or Examination (--TESTCD) variable: it should be limited to 8 characters, cannot start with a number, and cannot contain characters other than letters in upper case, numbers, or underscores. Value '{data($value)}' was found for variable {data($testname)} in dataset {data($datasetname)}.</error>
]]></rulexquery>
</sdsrule>

<sdsrule originator="FDA" id="SD1301" standard="SDTM" last-update="2020-08-08">
	<ruledescription>TDSTOFF must be either 0 or a positive ISO 8601 Duration format</ruledescription>
	<ruledetaileddescription>Offset from the Anchor (TDSTOFF) variable value must be either 0, ISO 8601 format for duration (e.g. P80Y), or null</ruledetaileddescription>
	<igversion>3.1.2</igversion>
	<igversion>3.1.3</igversion>
	<igversion>3.2</igversion>
	<domain>TD</domain>
	<synonym context="CDISC">CG0376</synonym>
	<rulexquery><![CDATA[	
(: Rule SD1301 - TDSTOFF = 0 or positive value in ISO 8601 Duration format :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external;
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: Get the TD (Trial Disease) dataset :)
let $itemgroupdef := $definedoc//odm:ItemGroupDef[@Name='TD']
(: Get the location :)
let $datasetname := (
	if($defineversion = '2.1') then $itemgroupdef/def21:leaf/@xlink:href
	else $itemgroupdef/def:leaf/@xlink:href
)
let $datasetdoc := (
	if($datasetname) then doc(concat($base,$datasetname))
	else ()
)
(: get the OID of TDSTOFF :)
let $tdstoffoid := (
    for $a in $definedoc//odm:ItemDef[@Name='TDSTOFF']/@OID 
    where $a = $itemgroupdef/odm:ItemRef/@ItemOID
    return $a
)
(: iterate over all records for which TDSTOFF is populated :)
for $record in $datasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$tdstoffoid]]
    let $recnum := $record/@data:ItemGroupDataSeq
    (: get the value of TDSTOFF :)
    let $tdstoff := $record/odm:ItemData[@ItemOID=$tdstoffoid]/@Value
    (: TDSTOFF must be either '0' or a non-negative duration, which can also be PnW :)
    where $tdstoff != '0' or starts-with($tdstoff,'-') or not($tdstoff castable as xs:duration or (starts-with($tdstoff,'P') and ends-with($tdstoff,'W')))
    return <error rule="SD1301" dataset="TD" recordnumber="{data($recnum)}" variable="TDSTOFF" rulelastupdate="2020-08-08">TDSTOFF must either be '0' or a non-negative duration. TDSTOFF='{data($tdstoff)}' was found</error>			
		]]>
	</rulexquery>
</sdsrule>

<!-- TODO (possible?): take MedDRA VERSION from define.xml into account -->
<sdsrule originator="FDA" id="SD2009" standard="SDTM" last-update="2020-08-08" requiresMedDRA="Yes">
	<ruledescription>--LLT must be a valid MedDRA lowest level term</ruledescription>
	<ruledetaileddescription>Value for Lowest Level Term Code (--LLTCD) variable must be populated using a Lowest Level Term Code of the MedDRA dictionary of a version specified in the define.xml</ruledetaileddescription>
	<igversion>3.1.3</igversion>
	<igversion>3.2</igversion>
	<domain>AE</domain>
	<domain>MH</domain>
	<domain>CE</domain>
	<synonym context="CDISC">CG0377</synonym>
	<rulexquery><![CDATA[	
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
declare namespace xs="http://www.w3.org/2001/XMLSchema";
declare namespace functx = "http://www.functx.com";
(: function to find out whether a value is in a sequence (array) :)
declare function functx:is-value-in-sequence
  ( $value as xs:anyAtomicType? ,
    $seq as xs:anyAtomicType* )  as xs:boolean {
   $value = $seq
} ;
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: the location of the MedDRA files (folder) need to be passed :)
declare variable $meddrabase external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
(: let $meddrabase := 'file:///e:/Validation_Rules_XQuery/meddra_17_1_english/MedAscii/' :)
let $meddrafile := 'llt.asc' (:   :)
let $meddralocation := concat($meddrabase,$meddrafile)
(: read the meddra file line by line :)
let $lines := unparsed-text-lines($meddralocation) 
(: get the LLT term, which is the second fied - field separator is the $ character :)
let $lltterms := (
	for $line in $lines
	return upper-case(tokenize($line,'\$')[2])
)  
(: $lltterms know contains all the MedDRA LLT terms in UPPERCASE :)
(: iterate over all AE, MH and CE dataset definitions :)
let $definedoc := doc(concat($base,$define))
for $itemgroup in $definedoc//odm:ItemGroupDef[@Name='AE' or @Name='MH' or @Name='CE' or @Domain='AE' or @Domain='MH' or @Domain='CE']
    let $name := $itemgroup/@Name
    (: get the OID of the --LLT variable :)
    let $lltoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'LLT') and string-length(@Name)=5]/@OID 
        where $a = $itemgroup/odm:ItemRef/@ItemOID
        return $a
    )
    let $lltname := $definedoc//odm:ItemDef[@OID=$lltoid]/@Name
    (: get the dataset location :)
	let $datasetlocation := (
		if($defineversion = '2.1') then $itemgroup/def21:leaf/@xlink:href
		else $itemgroup/def:leaf/@xlink:href
	)
    let $datasetdoc := (
		if($datasetlocation) then doc(concat($base,$datasetlocation))
		else ()
	)
    (: iterate over all the records in the dataset :)
    for $record in $datasetdoc//odm:ItemGroupData
        let $recnum := $record/@data:ItemGroupDataSeq
        (: get the value of --LLC :)
        let $lltvalue := $record/odm:ItemData[@ItemOID=$lltoid]/@Value
        (: give an error when the LLT value is not one of the LLT terms of MedDRA - case-insensitive :)
        where $lltvalue and not(functx:is-value-in-sequence(upper-case($lltvalue),$lltterms)) 
        return <error rule="SD2009" dataset="{data($name)}" variable="{data($lltname)}" recordnumber="{data($recnum)}" rulelastupdate="2020-08-08">Value '{data($lltvalue)}' for {data($lltname)} was not found in the MedDRA dictionary</error>						
		]]>
	</rulexquery>
</sdsrule>

<!-- 2020-08-08 SD0036  -->
<sdsrule id="SD0036" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>--STRESC must be provided, when --ORRES is provided</ruledescription>
<ruledetaileddescription>Character Result/Finding in Std Units (--STRESC) must be populated, when Result or Finding in Original Units (--ORRES) is provided</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>FINDINGS</domain>
<synonym>FDAC171</synonym>
<rulexquery><![CDATA[		
(: Rule SD0036 - Missing value for --STRESC, when --ORRES is provided:
Character Result/Finding in Std Units (--STRESC) must be populated, when Result or Finding in Original Units (--ORRES) is provided
:)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink"; 
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: Iterate over all FINDINGS domains :)
for $dataset in $definedoc//odm:ItemGroupDef[upper-case(@def:Class)='FINDINGS' or upper-case(./def21:Class/@Name)='FINDINGS']
    let $name := $dataset/@Name
	let $datasetname := (
		if($defineversion = '2.1') then $dataset/def21:leaf/@xlink:href
		else $dataset/def:leaf/@xlink:href
	)
    let $datasetdoc := (
		if($datasetname) then doc(concat($base,$datasetname))
		else ()
	)
    (: and get the OIDs of the STRESC and ORRES variables :)
    let $strescoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'STRESC')]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a 
    )
    let $strescname := $definedoc//odm:ItemDef[@OID=$strescoid]/@Name
    let $orresoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'ORRES')]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a     
    )
    let $orresname := $definedoc//odm:ItemDef[@OID=$orresoid]/@Name
    (: now iterate over all records in the dataset that DO have an ORRES :)
    for $record in $datasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$orresoid]]
        let $recnum := $record/@data:ItemGroupDataSeq
        (: and get the values for STRESC and ORRES :)
        let $strescvalue := $record/odm:ItemData[@ItemOID=$strescoid]/@Value
        let $orresvalue := $record/odm:ItemData[@ItemOID=$orresoid]/@Value
        (: we already selected on the presence of ORRES, STRESC MUST be populated :)
         where not($strescvalue) 
        return <error rule="SD0036" dataset="{data($name)}" variable="{data($strescname)}" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">Missing value for {data($strescname)}, when {data($orresname)} is provided, {data($orresname)}='{data($orresvalue)}' is provided in dataset {data($datasetname)}</error>
]]></rulexquery>
</sdsrule>

<!-- 2020-08-08 SD0036  -->
<sdsrule id="SD0036-SD" last-update="2020-08-08" originator="FDA" standard="SDTM" requiresDomainOrDataset="Yes">
<ruledescription>--STRESC must be provided, when --ORRES is provided</ruledescription>
<ruledetaileddescription>Character Result/Finding in Std Units (--STRESC) must be populated, when Result or Finding in Original Units (--ORRES) is provided</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>FINDINGS</domain>
<synonym>FDAC171</synonym>
<rulexquery><![CDATA[		
(: Rule SD0036 - Missing value for --STRESC, when --ORRES is provided:
Character Result/Finding in Std Units (--STRESC) must be populated, when Result or Finding in Original Units (--ORRES) is provided
:)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink"; 
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
declare variable $datasetname external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: Iterate over all provided datasets within the FINDINGS domains :)
for $dataset in $definedoc//odm:ItemGroupDef[@Name=$datasetname][upper-case(@def:Class)='FINDINGS' or upper-case(./def21:Class/@Name)='FINDINGS']
    let $name := $dataset/@Name
    let $dsname := (
		if($defineversion = '2.1') then $dataset/def21:leaf/@xlink:href
		else $dataset/def:leaf/@xlink:href
	)
    let $datasetdoc := (
		if($dsname) then doc(concat($base,$dsname))
		else ()
	)
    (: and get the OIDs of the STRESC and ORRES variables :)
    let $strescoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'STRESC')]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a 
    )
    let $strescname := $definedoc//odm:ItemDef[@OID=$strescoid]/@Name
    let $orresoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'ORRES')]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a     
    )
    let $orresname := $definedoc//odm:ItemDef[@OID=$orresoid]/@Name
    (: now iterate over all records in the dataset that DO have an ORRES :)
    for $record in $datasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$orresoid]]
        let $recnum := $record/@data:ItemGroupDataSeq
        (: and get the values for STRESC and ORRES :)
        let $strescvalue := $record/odm:ItemData[@ItemOID=$strescoid]/@Value
        let $orresvalue := $record/odm:ItemData[@ItemOID=$orresoid]/@Value
        (: we already selected on the presence of ORRES, STRESC MUST be populated :)
         where not($strescvalue) 
        return <error rule="SD0036" dataset="{data($name)}" variable="{data($strescname)}" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">Missing value for {data($strescname)}, when {data($orresname)} is provided, {data($orresname)}='{data($orresvalue)}' is provided in dataset {data($datasetname)}</error>
]]></rulexquery>
</sdsrule>

<!-- TODO: SD1290 -->

<sdsrule id="SD0017" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>The value of --TEST may not be more than 40 characters</ruledescription>
<ruledetaileddescription>The value of Name of Measurement, Test or Examination (--TEST) should be no more than 40 characters in length</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>FINDINGS</domain>
<rulexquery><![CDATA[
(: Rule SD0017 - Invalid value for --TEST variable:
The value of Name of Measurement, Test or Examination (--TEST) should be no more than 40 characters in length :)
(: This rule is really outdated! Even CDISC has published test names with > 40 characters. The rule is a relict of the ancient XPT format. 
ALSO:IETEST is an exception: may be up to 200 characters :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
declare namespace xs="http://www.w3.org/2001/XMLSchema";
declare namespace functx = "http://www.functx.com";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external;
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: iterate over all datasets and variables in the define.xml :)
for $itemgroup in $definedoc//odm:ItemGroupDef
	let $itemgroupdefname := $itemgroup/@Name
    (: Get the @Domain value when present, otherwise the @Name value  - 
    this is for allowing 'splitted' domains, e.g. @Name=QSCG @Domain=QS :)
    let $datasetname := $itemgroup/@Name
    let $domain := (
        if($itemgroup/@Domain != '') then $itemgroup/@Domain
        else $itemgroup/@Name
    )
    (: The --TEST variable is the concatenation of the domain code and 'TEST' :)
    let $testvar := concat($domain,'TEST')
    (: Get the OID for the --TEST variable :)
    let $testoid := (
        for $a in $definedoc//odm:ItemDef[@Name=$testvar]/@OID
        where $a = $definedoc//odm:ItemGroupDef[@Name=$datasetname]/odm:ItemRef/@ItemOID
        return $a
    )
    (: we of course only look into datasets where there really is a --TEST :)
	let $datasetname := (
		if($defineversion = '2.1') then $itemgroup/def21:leaf/@xlink:href
		else $itemgroup/def:leaf/@xlink:href
	)
    let $datasetdoc := (
		if($datasetname) then doc(concat($base,$datasetname))
		else ()
	)
    (: iterate over all the records where there is a --TEST variable :)
    for $record in $datasetdoc//odm:ItemGroupData
        (: get the record number :)
        let $recnum := $record/@data:ItemGroupDataSeq
        (: and get the value of the --TEST variable :)
        let $testvalue := $record/odm:ItemData[@ItemOID=$testoid]/@Value
        (: when it has more than 40 characters, give an error.
        Attention: IETEST is an exception (may be up to 200 characters) :)
        where string-length($testvalue) > 40 and not($testvar='IETEST')
        return <error rule="SD0017" recordnumber="{data($recnum)}" variable="{data($testvar)}" dataset="{data($itemgroupdefname)}" rulelastupdate="2020-08-08">The value for {data($testvar)} has more than 40 characters: '{data($testvalue)}' was found</error>
]]></rulexquery>
</sdsrule>


<sdsrule id="SD1109" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>EX dataset must be present</ruledescription>
<ruledetaileddescription>Exposure (EX) dataset should be included in every submission</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>EX</domain>
<rulexquery><![CDATA[
(: Rule SD1109 - Missing EX dataset :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
declare namespace request="http://exist-db.org/xquery/request";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external;
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: get the location of the EX dataset :)
let $exdatasetname := (
	if($defineversion = '2.1') then $definedoc//odm:ItemGroupDef[@Name='EX']/def21:leaf/@xlink:href
	else $definedoc//odm:ItemGroupDef[@Name='EX']/def:leaf/@xlink:href
)
(: P.S. if EX is not in define.xml, we will suppose the dataset file/location is "ex.xml" :)
let $exdatasetlocation := (
	if($exdatasetname) then concat($base,$exdatasetname)
	else concat($base,'ex.xml')
)
(: we now have the location of the dataset, check whether it is available there :)
where (not(doc-available($exdatasetlocation)))
return <warning rule="SD1109" dataset="EX" rulelastupdate="2020-08-08">EX document {data($exdatasetlocation)} could not be found in collection {data($base)}</warning>		
]]></rulexquery>
</sdsrule>

<sdsrule id="SD0001" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>Domain tables must have at least one record</ruledescription>
<ruledetaileddescription>Domain table should have at least one record</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>ALL</domain>
<rulexquery><![CDATA[
(: Rule SD0001 - No records in data source :)
(: P.S only the datasets that are listed in the define.xml are tested :)
(: REMARK: Define-XML 2.1 allows to define datasets that have no records (and thus no dataset file) :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
declare namespace request="http://exist-db.org/xquery/request";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external;
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: REMARK: Define-XML 2.1 allows to define datasets that have no records (and thus no dataset file) :)
(: Find all datasets that are NOT marked to have no data :)
for $itemgroup in $definedoc//odm:ItemGroupDef[not(@def21:HasNoData)]
    let $datasetname := $itemgroup/@Name
	let $dataset := (
		if($defineversion = '2.1') then $itemgroup/def21:leaf/@xlink:href
		else $itemgroup/def:leaf/@xlink:href
	)
    let $datasetdoc := (
		if($dataset) then doc(concat($base,$dataset))
		else ()
	)
    (: iterate over each dataset, and count the number of records :)
    for $numrecords in count($datasetdoc//odm:ItemGroupData)
    (:  and check whether there is less than 1 record :)
    where $numrecords < 1
    return <error rule="SD0001" dataset="{data($datasetname)}" rulelastupdate="2020-08-08">{$numrecords} found in dataset: {data($datasetname)}</error>
]]></rulexquery>
</sdsrule>

<sdsrule id="SD0001-SD" last-update="2020-08-08" originator="FDA" standard="SDTM" requiresDomainOrDataset="Yes">
<ruledescription>Domain tables must have at least one record</ruledescription>
<ruledetaileddescription>Domain table should have at least one record</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>ALL</domain>
<rulexquery><![CDATA[
(: Rule SD0001 - No records in data source :)
(: P.S only the datasets that are listed in the define.xml are tested :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
declare namespace request="http://exist-db.org/xquery/request";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external;
declare variable $defineversion external;
declare variable $datasetname external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: Find all provided datasets :)
for $itemgroup in $definedoc//odm:ItemGroupDef[@Name=$datasetname]
    let $name := $itemgroup/@Name
	let $dataset := (
		if($defineversion = '2.1') then $itemgroup/def21:leaf/@xlink:href
		else $itemgroup/def:leaf/@xlink:href
	)
    let $datasetdoc := (
		if($dataset) then doc(concat($base,$dataset))
		else ()
	)
    (: iterate over each dataset, and count the number of records :)
    for $numrecords in count($datasetdoc//odm:ItemGroupData)
    (:  and check whether there is less than 1 record :)
    where $numrecords < 1
    return <error rule="SD0001" dataset="{data($name)}" rulelastupdate="2020-08-08">{$numrecords} found in dataset: {data($datasetname)}</error>
]]></rulexquery>
</sdsrule>


<sdsrule id="SD1005" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>Study Identifier (STUDYID) values must match the STUDYID in Demographics (DM) domain</ruledescription>
<ruledetaileddescription>Study Identifier (STUDYID) values must match the STUDYID in Demographics (DM) domain</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>ALL</domain>
<rulexquery><![CDATA[
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external;
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: get the OID of the STUDYID variable in the define.xml :)
let $dmitemgroupdef := $definedoc//odm:ItemGroupDef[@Name='DM']
let $dmstudyoid := (
        for $a in $definedoc//odm:ItemDef[@Name='STUDYID']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name='DM']/odm:ItemRef/@ItemOID
        return $a
    ) 
(: get the DM dataset :)
let $dmdatasetname := (
	if($defineversion = '2.1') then $dmitemgroupdef/def21:leaf/@xlink:href
	else $dmitemgroupdef/def:leaf/@xlink:href
)
let $dmdatasetdoc := doc(concat($base,$dmdatasetname))
let $dmstudyidunique := distinct-values($dmdatasetdoc//odm:ItemData[@ItemOID=$dmstudyoid]/@Value)
(: Surprisingly there is no FDA rule that STUDYID must be unique within DM :)
let $dmstudyid := $dmstudyidunique[1]  (: just for the worst case that STUDYID is NOT unique in DM :)
(: now iterate over all dataset definitions in the define.xml :)
for $itemgroupdef in $definedoc//odm:ItemGroupDef
    (: Also cover the case that there are several ItemDef[@Name='STUDYID'] e.g. one per dataset or domain :)
	let $dataset := (
		if($defineversion = '2.1') then $itemgroupdef/def21:leaf/@xlink:href
		else $itemgroupdef/def:leaf/@xlink:href
	)
    (: let $datasetpath := concat($base,$dataset) :)
    let $datasetdoc := (
		if($dataset) then doc(concat($base,$dataset))
		else ()
	)
    let $datasetname := $itemgroupdef/@Name
    (: find the variable for which the name is 'STUDYID' - there should be 0 or 1 :)
    let $studyoid := (
        for $a in $definedoc//odm:ItemDef[@Name='STUDYID']/@OID 
        where $a = $itemgroupdef/odm:ItemRef/@ItemOID
        return $a
    )
    for $d in $datasetdoc//odm:ItemGroupData/odm:ItemData[@ItemOID=$studyoid]
        let $recordnumber := $d/../@data:ItemGroupDataSeq
        where not($d/@Value = $dmstudyid)
        return <error rule="SD1005" dataset="{data($datasetname)}" variable="STUDYID" rulelastupdate="2020-08-08" recordnumber="{data($recordnumber)}">STUDYID {data($d/@Value)} in dataset {data($dataset)} does not match the STUDYID {data($dmstudyid)} in the DM dataset</error>	
]]></rulexquery>
</sdsrule>

<sdsrule id="SD1060" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>VISITNUM must be unique for each subject in SV</ruledescription>
<ruledetaileddescription>The value of Visit Number (VISITNUM) variable should be unique for each record within a subject - Dataset SV</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>SV</domain>
<rulexquery><![CDATA[
(: Rule SD1060 - Duplicate VISITNUM: 
 The value of Visit Number (VISITNUM) variable should be unique for each record within a subject - Dataset SV - Warning :)
(: OPTIMIZED - made much more faster using "GROUP BY" :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external;
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdiscpilot01/' :)
(: let $define := 'define_2_0.xml' :)
let $definedoc := doc(concat($base,$define))
let $svitemgroupdef := $definedoc//odm:ItemGroupDef[@Name='SV']
(: get the OID of VISITNUM in define.xml for dataset SV :)
let $visitnumoid := (
        for $a in $definedoc//odm:ItemDef[@Name='VISITNUM']/@OID 
        where $a = $svitemgroupdef/odm:ItemRef/@ItemOID
        return $a
    )  
(: get the OID of USUBJID in the SV dataset :)
let $usubjidoid := (
        for $a in $definedoc//odm:ItemDef[@Name='USUBJID']/@OID 
        where $a = $svitemgroupdef/odm:ItemRef/@ItemOID
        return $a
    )
(: get the location of the SV dataset :)
let $svdatasetname := (
	if($defineversion = '2.1') then $definedoc//odm:ItemGroupDef[@Name='SV']/def21:leaf/@xlink:href
	else $definedoc//odm:ItemGroupDef[@Name='SV']/def:leaf/@xlink:href
)
let $svdatasetdoc := (
	if($svdatasetname) then doc(concat($base,$svdatasetname))
	else ()
)
(: group the records in the SV dataset by USUBJID and VISITNUM - there should be only one record per group :)
let $orderedrecords := (
	for $record in $svdatasetdoc//odm:ItemGroupData
		group by 
		$b := $record/odm:ItemData[@ItemOID=$usubjidoid]/@Value,
		$c := $record/odm:ItemData[@ItemOID=$visitnumoid]/@Value
		return element group {  
			$record
		}
	) 
(: iterate over all the groups :)
for $group in $orderedrecords
    let $visitnumvalue := $group/odm:ItemGroupData[1]/odm:ItemData[@ItemOID=$visitnumoid]/@Value
    let $usubjidvalue := $group/odm:ItemGroupData[1]/odm:ItemData[@ItemOID=$usubjidoid]/@Value
    let $recnum := $group/odm:ItemGroupData[1]/@data:ItemGroupDataSeq
    let $count := count($group/odm:ItemGroupData)
    where $count > 1
    return <warning rule="SD1060" dataset="SV" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">Non-unique combination of USUBID and VISITNUM in dataset SV - combination of USUBJID={data($usubjidvalue)} and VISITNUM={data($visitnumvalue)} occurrs {data($count)} times</warning>
]]></rulexquery>
</sdsrule>

<sdsrule originator="FDA" id="SD0086-SD" standard="SDTM" last-update="2020-08-08" requiresDomainOrDataset="Yes">
	<ruledescription>There may be only one record per IDVAR, IDVARVAL, and QNAM value per subject</ruledescription>
	<ruledetaileddescription>All SUPPQUAL Domains records are associated with a single parent record and must have a unique combination of Study Identifier (STUDYID), Unique Subject Identifier (USUBJID), Identifying Variable (IDVAR), Identifying Variable Value (IDVARVAL) and Qualifier Variable Name (QNAM) variable value. For all SUPPQUAL domain record all The QNAM variable should be unique for each parent record</ruledetaileddescription>
	<igversion>3.1.2</igversion>
	<igversion>3.1.3</igversion>
	<igversion>3.2</igversion>
	<domain>SUPPQUAL</domain>
	<synonym context="CDISC">CG0411</synonym>
	<rulexquery><![CDATA[
(: Rule SD0086 - IDVAR, IDVARVAL, and QNAM a unique combination per parent subject record :)
(: OPTIMIZED - made much more faster using "GROUP BY" :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external;
declare variable $defineversion external;
declare variable $datasetname external;
(: let $base := '/db/fda_submissions/cdiscpilot01/' :)
(: let $define := 'define_2_0.xml' :)
let $definedoc := doc(concat($base,$define))
(: iterate over all SUPP-- records :)
for $itemgroupdef in $definedoc//odm:ItemGroupDef[starts-with(@Name,'SUPP')][@Name=$datasetname]
    let $name := $itemgroupdef/@Name
    (: get the OID of USUBJID in the SUPP-- dataset :)
    let $usubjidoid := (
        for $a in $definedoc//odm:ItemDef[@Name='USUBJID']/@OID 
        where $a = $itemgroupdef/odm:ItemRef/@ItemOID
        return $a
    )
    (: get the OID of IDVAR in the SUPP-- dataset :)
    let $idvaroid := (
        for $a in $definedoc//odm:ItemDef[@Name='IDVAR']/@OID 
        where $a = $itemgroupdef/odm:ItemRef/@ItemOID
        return $a
    )
    (: get the OID of IDVARVAL in the SUPP-- dataset :)
    let $idvarvaloid := (
        for $a in $definedoc//odm:ItemDef[@Name='IDVARVAL']/@OID 
        where $a = $itemgroupdef/odm:ItemRef/@ItemOID
        return $a
    )
    (: get the OID of QNAM in the SUPP-- dataset :)
    let $qnamoid := (
        for $a in $definedoc//odm:ItemDef[@Name='QNAM']/@OID 
        where $a = $itemgroupdef/odm:ItemRef/@ItemOID
        return $a
    )
(: get the location of the SUPP-- dataset :)
let $suppdatasetlocation := (
	if($defineversion = '2.1') then $itemgroupdef/def21:leaf/@xlink:href
	else $itemgroupdef/def:leaf/@xlink:href
)
let $suppdatasetdoc := (
	if($suppdatasetlocation) then doc(concat($base,$suppdatasetlocation))
	else ()
)
(: group the records in the SUPP-- dataset by USUBJID,IDVAR,IDVARVAL,QNAM - there should be only one record per group :)
let $orderedrecords := (
	for $record in $suppdatasetdoc//odm:ItemGroupData
		group by 
		$b := $record/odm:ItemData[@ItemOID=$usubjidoid]/@Value,
		$c := $record/odm:ItemData[@ItemOID=$idvaroid]/@Value,
		$d := $record/odm:ItemData[@ItemOID=$idvarvaloid]/@Value,
		$e := $record/odm:ItemData[@ItemOID=$qnamoid]/@Value
		return element group {  
			$record
		}
	) 
(: iterate over all the groups - in each group, all values of USUBJID,IDVAR,IDVARVAL,QNAM :)
for $group in $orderedrecords
    let $usubjidvalue := $group/odm:ItemGroupData[1]/odm:ItemData[@ItemOID=$usubjidoid]/@Value
    let $idvar := $group/odm:ItemGroupData[1]/odm:ItemData[@ItemOID=$idvaroid]/@Value
    let $idvarval := $group/odm:ItemGroupData[1]/odm:ItemData[@ItemOID=$idvarvaloid]/@Value
    let $qnam := $group/odm:ItemGroupData[1]/odm:ItemData[@ItemOID=$qnamoid]/@Value
    let $recnum := $group/odm:ItemGroupData[1]/@data:ItemGroupDataSeq
    (: count the records in this group :)
    let $count := count($group/odm:ItemGroupData)
    where $count > 1
    return <error rule="SD0086" dataset="{data($name)}" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">The combination of USUBJID={data($usubjidvalue)}, IDVAR={data($idvar)}, IDVARVAL={data($idvarval)} and QNAM={data($qnam)} is not unique</error>			
		]]>
	</rulexquery>
</sdsrule>

<sdsrule id="SD0069" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>Every treated subject must have at least one DS record</ruledescription>
<ruledetaileddescription>All Demographics (DM) subjects (USUBJID) should have at least one record in the Disposition (DS) domain with only possible exceptions to Screen Failures (ARMCD='SCRNFAIL') and Not Assigned Treatment (ARMCD='NOTASSGN') subjects</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>DS</domain>
<domain>DM</domain>
<rulexquery><![CDATA[
(: Rule SD0069 - No Disposition record found for subject:
All Demographics (DM) subjects (USUBJID) should have at least one record in the Disposition (DS) domain with only possible exceptions to Screen Failures (ARMCD='SCRNFAIL') and Not Assigned Treatment (ARMCD='NOTASSGN') subjects
:)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external;
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: Get the DM dataset :)
let $dmdatasetname := (
	if($defineversion = '2.1') then $definedoc//odm:ItemGroupDef[@Name='DM']/def21:leaf/@xlink:href
	else $definedoc//odm:ItemGroupDef[@Name='DM']/def:leaf/@xlink:href
)
let $dmdatasetdoc := doc(concat($base,$dmdatasetname))
(: Get the DS dataset :)
let $dsdatasetname := (
	if($defineversion = '2.1') then $definedoc//odm:ItemGroupDef[@Name='DS']/def21:leaf/@xlink:href
	else $definedoc//odm:ItemGroupDef[@Name='DS']/def:leaf/@xlink:href
)
let $dsdatasetdoc := (
	if($dsdatasetname) then doc(concat($base,$dsdatasetname))
	else ()
)
(: and the OID of the USUBJID and of ARMCD in the DM dataset :) 
let $usubjidoid := (
    for $a in $definedoc//odm:ItemDef[@Name='USUBJID']/@OID 
    where $a = $definedoc//odm:ItemGroupDef[@Name='DM']/odm:ItemRef/@ItemOID
    return $a
)
let $armcdoid := (
    for $a in $definedoc//odm:ItemDef[@Name='ARMCD']/@OID 
    where $a = $definedoc//odm:ItemGroupDef[@Name='DM']/odm:ItemRef/@ItemOID
    return $a
)
(: we also need the OID of USUBJID in the DS dataset :)
let $dsusubjidoid := (
    for $a in $definedoc//odm:ItemDef[@Name='USUBJID']/@OID 
    where $a = $definedoc//odm:ItemGroupDef[@Name='DS']/odm:ItemRef/@ItemOID
    return $a
)
(: iterate over the DM dataset and  :)
for $record in $dmdatasetdoc//odm:ItemGroupData
    let $recnum := $record/@data:ItemGroupDataSeq
    let $usubjidvalue := $record/odm:ItemData[@ItemOID=$usubjidoid]/@Value
    (: $hasarmassigned sets whether the subject has been assigned to an arm :)
    let $hasarmassigned := not($record/odm:ItemData[@ItemOID=$armcdoid]/@Value = 'SCRNFAIL' or  $record/odm:ItemData[@ItemOID=$armcdoid]/@Value = 'NOTASSGN')
    (: now count the number of records for this subject in the DS dataset :)
    let $count := count($dsdatasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$dsusubjidoid and @Value=$usubjidvalue]])
    let $arm := $record/odm:ItemData[@ItemOID=$armcdoid]/@Value
    where $hasarmassigned and $count = 0
    (: and now check whether there are any records in the DS dataset  :)
    return <warning rule="SD0069" rulelastupdate="2020-08-08" dataset="DM" variable="USUBJID" recordnumber="{data($recnum)}">No disposition records were found in dataset {data($dsdatasetname)} for subject {data($usubjidvalue)} with assigned arm {data($arm)}</warning>
]]></rulexquery>
</sdsrule>

<sdsrule id="SD0004" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>Value for DOMAIN variable must be consistent</ruledescription>
<ruledetaileddescription>Domain Abbreviation (DOMAIN) variable should be consistent with the name of the dataset</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>ALL</domain>
<rulexquery><![CDATA[
(: Rule SD0004 - Inconsistent value for DOMAIN:
Domain Abbreviation (DOMAIN) variable should be consistent with the name of the dataset :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external;
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdiscpilot01/' :)
(: let $define := 'define_2_0.xml' :)
let $definedoc := doc(concat($base,$define))
(: iterate over all datasets in the define.xml :)
for $itemgroup in $definedoc//odm:ItemGroupDef
	let $itemgroupdefname := $itemgroup/@Name
    (: get the dataset name and location :)
	let $datasetname := (
		if($defineversion = '2.1') then $itemgroup/def21:leaf/@xlink:href
		else $itemgroup/def:leaf/@xlink:href
	)
    let $datasetdoc := (
		if($datasetname) then doc(concat($base,$datasetname))
		else ()
	)
    (: get the OID of the "DOMAIN" variable :)
    let $domainoid := (
    for $a in $definedoc//odm:ItemDef[@Name='DOMAIN']/@OID 
    (: where $a = $definedoc//odm:ItemGroupDef/odm:ItemRef/@ItemOID :)
    where $a = $itemgroup/odm:ItemRef/@ItemOID
    return $a 
    )
    (: now get the unique values in the dataset - the number of unique values must be 1 :)
    let $distinctdomainvalues := distinct-values($datasetdoc//odm:ItemData[@ItemOID=$domainoid]/@Value)
    where count($distinctdomainvalues) > 1
    return <error rule="SD0004" dataset="{data($itemgroupdefname)}" variable="DOMAIN" rulelastupdate="2020-08-08">Inconsistent Domain Abbreviation (DOMAIN) variable value in dataset {data($datasetname)}. Following values were found: {data($distinctdomainvalues)}</error>	
]]></rulexquery>
</sdsrule>

<sdsrule id="SD0004-SD" last-update="2020-08-08" originator="FDA" standard="SDTM" requiresDomainOrDataset="Yes">
<ruledescription>Value for DOMAIN variable must be consistent</ruledescription>
<ruledetaileddescription>Domain Abbreviation (DOMAIN) variable should be consistent with the name of the dataset</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>ALL</domain>
<rulexquery><![CDATA[
(: Rule SD0004 - Inconsistent value for DOMAIN:
Domain Abbreviation (DOMAIN) variable should be consistent with the name of the dataset :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external;
declare variable $defineversion external;
declare variable $datasetname external;
(: let $base := '/db/fda_submissions/cdiscpilot01/' :)
(: let $define := 'define_2_0.xml' :)
let $definedoc := doc(concat($base,$define))
(: iterate over all provided datasets in the define.xml :)
for $itemgroup in $definedoc//odm:ItemGroupDef[@Name=$datasetname]
	let $itemgroupdefname := $itemgroup/@Name
    (: get the dataset name and location :)
	let $dsname := (
		if($defineversion = '2.1') then $itemgroup/def21:leaf/@xlink:href
		else $itemgroup/def:leaf/@xlink:href
	)
    let $datasetdoc := (
		if($dsname) then doc(concat($base,$dsname))
		else ()
	)
    (: get the OID of the "DOMAIN" variable :)
    let $domainoid := (
    for $a in $definedoc//odm:ItemDef[@Name='DOMAIN']/@OID 
    (: where $a = $definedoc//odm:ItemGroupDef/odm:ItemRef/@ItemOID :)
    where $a = $itemgroup/odm:ItemRef/@ItemOID
    return $a 
    )
    (: now get the unique values in the dataset - the number of unique values must be 1 :)
    let $distinctdomainvalues := distinct-values($datasetdoc//odm:ItemData[@ItemOID=$domainoid]/@Value)
    where count($distinctdomainvalues) > 1
    return <error rule="SD0004" dataset="{data($itemgroupdefname)}" variable="DOMAIN" rulelastupdate="2020-08-08">Inconsistent Domain Abbreviation (DOMAIN) variable value in dataset {data($datasetname)}. Following values were found: {data($distinctdomainvalues)}</error>		
]]></rulexquery>
</sdsrule>

<sdsrule id="SD0067" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>ETCD values must be registered in TA except for unplanned</ruledescription>
<ruledetaileddescription>Element Code (ETCD) values should match entries in the Trial Elements (TE) dataset, except for unplanned Element (ETCD = 'UNPLAN')</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>SE</domain>
<domain>TA</domain>
<rulexquery><![CDATA[
(: Rule SD0067 - Invalid ETCD:
Element Code (ETCD) values should match entries in the Trial Elements (TE) dataset, except for unplanned Element (ETCD = 'UNPLAN')
:)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
declare namespace xs="http://www.w3.org/2001/XMLSchema";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external;
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: Get the TA and SE datasets :)
let $datasets := $definedoc//odm:ItemGroupDef[@Name='TA' or @Name='SE']
(: Get the TE dataset :)
let $tedatasetlocation := (
	if($defineversion = '2.1') then $definedoc//odm:ItemGroupDef[@Name='TE']/def21:leaf/@xlink:href
	else $definedoc//odm:ItemGroupDef[@Name='TE']/def:leaf/@xlink:href
)
(: and the OID for ETCD in the TE dataset :)
let $teectdoid :=
(for $a in $definedoc//odm:ItemDef[@Name='ETCD']/@OID 
    where $a = $definedoc//odm:ItemGroupDef[@Name='TE']/odm:ItemRef/@ItemOID
    return $a
)  
(: iterate over the two datasets :)
for $dataset in $datasets
    let $datasetname := $dataset/@Name
    let $datasetlocation := (
		if($defineversion = '2.1') then $dataset/def21:leaf/@xlink:href
		else $dataset/def:leaf/@xlink:href
	)
	let $datasetdoc := (
		if($datasetlocation) then doc(concat($base,$datasetlocation))
		else ()
	)
    (: Get the OID for ETCD :)
    let $etcdoid := (
		for $a in $definedoc//odm:ItemDef[@Name='ETCD']/@OID 
		where $a = $definedoc//odm:ItemGroupDef[@Name=$datasetname]/odm:ItemRef/@ItemOID
		return $a
	)
    (: now iterate over all records within the dataset :)
    for $record in $datasetdoc//odm:ItemGroupData
        let $recnum := $record/@data:ItemGroupDataSeq
        (: exclude the one for which the ETCD value is 'UNPLAN' :)
        let $etcdvalue := $record/odm:ItemData[@ItemOID=$etcdoid]/@Value
        (: and check whether present in the TE dataset, excluding the 'UNPLAN' ones :)
        where not(doc(concat($base,$tedatasetlocation))//odm:ItemGroupData/odm:ItemData[@ItemOID=$teectdoid][@Value=$etcdvalue]) and not($etcdvalue='UNPLAN')
        return <error rule="SD0067" rulelastupdate="2020-08-08" dataset="SE" variable="ETCD" recordnumber="{data($recnum)}">Invalid ETCD: Element Code ETCD={data($etcdvalue)} in dataset {data($datasetlocation)} was not found in TE dataset {data($tedatasetlocation)}</error>
]]></rulexquery>
</sdsrule>

<sdsrule id="SD1012" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>ETCD/ELEMENT combinations must match entries in TE</ruledescription>
<ruledetaileddescription>The combination of Element Code (ETCD) and Description of Element (ELEMENT) values should match entries in the Trial Elements (TE) dataset, except for unplanned Element (ETCD = 'UNPLAN') - dataset SE,TA</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>SE</domain>
<domain>TA</domain>
<rulexquery><![CDATA[
(: Rule SD1012 - Invalid ETCD/ELEMENT:
The combination of Element Code (ETCD) and Description of Element (ELEMENT) values should match entries in the Trial Elements (TE) dataset, except for unplanned Element (ETCD = 'UNPLAN') - dataset SE,TA :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external;
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: Get the TE dataset :)
let $tedatasetname := (
	if($defineversion = '2.1') then $definedoc//odm:ItemGroupDef[@Name='TE']/def21:leaf/@xlink:href
	else $definedoc//odm:ItemGroupDef[@Name='TE']/def:leaf/@xlink:href
)
let $tedatasetdoc := (
	if($tedatasetname) then doc(concat($base,$tedatasetname))
	else ()
)
(: Get the OID of ETCD and ELEMENT in the TE dataset :)
let $teetcdoid := (
    for $a in $definedoc//odm:ItemDef[@Name='ETCD']/@OID 
    where $a = $definedoc//odm:ItemGroupDef[@Name='TE']/odm:ItemRef/@ItemOID
    return $a
)
let $teelementoid := (
    for $a in $definedoc//odm:ItemDef[@Name='ELEMENT']/@OID 
    where $a = $definedoc//odm:ItemGroupDef[@Name='TE']/odm:ItemRef/@ItemOID
    return $a
)
(: Get the TA and SE datasets :)
let $datasets := $definedoc//odm:ItemGroupDef[@Name='TA' or @Name='SE']
(: iterate over these datasets :)
for $dataset in $datasets
    let $name := $dataset/@Name
	let $datasetname := (
		if($defineversion = '2.1') then $dataset/def21:leaf/@xlink:href
		else $dataset/def:leaf/@xlink:href
	)
    let $datasetdoc := (
		if($datasetname) then doc(concat($base,$datasetname))
		else ()
	)
    (: Get the OIDs of the ETCD and ELEMENT variables in the TA or SE datasets :)
    let $etcdoid := (
        for $a in $definedoc//odm:ItemDef[@Name='ETCD']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
    )
    let $elementoid := (
        for $a in $definedoc//odm:ItemDef[@Name='ELEMENT']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
    )
    (: iterate over all the records in the dataset :)
    for $record in $datasetdoc//odm:ItemGroupData
        let $recnum := $record/@data:ItemGroupDataSeq
        (: and get the value for ETCD and ELEMENT :)
        let $etcdvalue := $record/odm:ItemData[@ItemOID=$etcdoid]/@Value
        let $elementvalue := $record/odm:ItemData[@ItemOID=$elementoid]/@Value
        (: and find a record in the TE dataset - exclude however ETCD='UNPLAN' :)
        where not($tedatasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$teetcdoid][@Value=$etcdvalue] and odm:ItemData[@ItemOID=$teelementoid][@Value=$elementvalue]]) and not($etcdvalue='UNPLAN')
        return <error rule="SD1012" dataset="{data($name)}" variable="ETCD" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">Invalid ETCD/ELEMENT in dataset {data($datasetname)}. The combination of Element Code ETCD={data($etcdvalue)} and Description of Element ELEMENT={data($elementvalue)} was not found in the TE dataset {data($tedatasetname)}</error>
]]></rulexquery>
</sdsrule>

<sdsrule id="SD1053" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>Screen failures and untreated subjects may not have a value for the ARMCD variable - </ruledescription>
<ruledetaileddescription>Records for subjects who failed a screening or were not assigned to study treatment (ARMCD is 'SCRNFAIL' or 'NOTASSGN') should not be included in the Trial Arms (TA) or Trial Visits (TV) datasets</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>TA</domain>
<domain>TV</domain>
<rulexquery><![CDATA[
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external;
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: get the OID of the ARMCD variable in the define.xml for TA and TV :)
for $itemgroupdef in $definedoc//odm:ItemGroupDef[@Name='TA' or @Name='TV']
    let $itemgroupdefname := $itemgroupdef/@Name
    (: get the OID of ARMCD :)
    let $armcdoid := (
        for $a in $definedoc//odm:ItemDef[@Name='ARMCD']/@OID 
        where $a = $itemgroupdef/odm:ItemRef/@ItemOID
        return $a
    ) 
    (: and the location of the dataset :)
	let $datasetname := (
		if($defineversion = '2.1') then $itemgroupdef/def21:leaf/@xlink:href
		else $itemgroupdef/def:leaf/@xlink:href
	)
    let $datasetdoc := (
		if($datasetname) then doc(concat($base,$datasetname))
		else ()
	)
    (: iterate over all ARMCD record in the dataset :)
    for $record in $datasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$armcdoid]]
        let $recnum := $record/@data:ItemGroupDataSeq
        let $armcdvalue := $record/odm:ItemData[@ItemOID=$armcdoid]/@Value
        (: ARMCD is not allowed to be SCRNFAIL nor NOTASSGN :)
        where $armcdvalue = 'SCRNFAIL' or $armcdvalue = 'NOTASSGN'
        return <error rule="SD1053" dataset="{data($itemgroupdefname)}" variable="ARMCD" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">Unexpected value for ARMCD={data($armcdvalue)} in dataset {data($itemgroupdefname)} - 'SCRNFAIL' and 'NOTASSGN' are not allowed to appear in TA nor TV</error>
]]></rulexquery>
</sdsrule>

<sdsrule id="SD1049" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>QLABEL may not have more than 40 characters</ruledescription>
<ruledetaileddescription>Qualifier Variable Label (QLABEL) value may have up to 40 characters</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>ALL</domain>
<rulexquery><![CDATA[
(: Rule SD1049 - Invalid value for QLABEL:
 Qualifier Variable Label (QLABEL) value may have up to 40 characters :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external;
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: Get all the SUPPxx datasets :)
for $dataset in $definedoc//odm:ItemGroupDef[starts-with(@Name,'SUPP')]
    let $name := $dataset/@Name
	let $datasetname := (
		if($defineversion = '2.1') then $dataset/def21:leaf/@xlink:href
		else $dataset/def:leaf/@xlink:href
	)
    let $datasetdoc := (
		if($datasetname) then doc(concat($base,$datasetname))
		else ()
	)
    (: get the OID of the QLABEL variable :)
    let $qlabeloid := (
        for $a in $definedoc//odm:ItemDef[@Name='QLABEL']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
    )
    (: iterate over all records in the dataset :)
    for $record in $datasetdoc//odm:ItemGroupData
		let $recnum := $record/@data:ItemGroupDataSeq
		(: Get the QLABEL value :)
		let $qlabelvalue := $record/odm:ItemData[@ItemOID=$qlabeloid]/@Value
		(: and check whether more than 40 characters :)
		where string-length($qlabelvalue) > 40
		return <error rule="SD1049" rulelastupdate="2020-08-08" dataset="{data($name)}" variable="QLABEL" recordnumber="{data($recnum)}">Invalid value for QLABEL in dataset {data($datasetname)} - Label={data($qlabelvalue)} has more than 40 characters</error>
]]></rulexquery>
</sdsrule>

<sdsrule id="SD1022" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>QNAM may not have more than 8 characters</ruledescription>
<ruledetaileddescription>The value of Qualifier Variable Name (QNAM) should be limited to 8 characters, cannot start with a number, and cannot contain characters other than letters in upper case, numbers, or underscores</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>SUPPQUAL</domain>
<rulexquery><![CDATA[
(: Rule SD1022 - Invalid value for QNAM variable:
The value of Qualifier Variable Name (QNAM) should be limited to 8 characters, cannot start with a number, and cannot contain characters other than letters in upper case, numbers, or underscores
:)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external;
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: Get all the SUPPxx datasets :)
for $dataset in doc(concat($base,$define))//odm:ItemGroupDef[starts-with(@Name,'SUPP')]
    let $name := $dataset/@Name
	let $datasetname := (
		if($defineversion = '2.1') then $dataset/def21:leaf/@xlink:href
		else $dataset/def:leaf/@xlink:href
	)
    let $datasetdoc := (
		if($datasetname) then doc(concat($base,$datasetname))
		else ()
	)
    (: get the OID of the QNAM variable :)
    let $qnamoid := (
        for $a in $definedoc//odm:ItemDef[@Name='QNAM']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
    )
    (: iterate over all records in the dataset :)
    for $record in $datasetdoc//odm:ItemGroupData
    let $recnum := $record/@data:ItemGroupDataSeq
    (: Get the QNAM value :)
    let $qnamvalue := $record/odm:ItemData[@ItemOID=$qnamoid]/@Value
    (: and check whether more than 8 characters and that the character types are correct :)
    where string-length($qnamvalue) > 8 or not(matches($qnamvalue,'[A-Z_][A-Z0-9_]*'))
    return <warning rule="SD1022" variable="QNAM" dataset="{data($name)}" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">Invalid value for QNAM in dataset {data($datasetname)} - Label={data($qnamvalue)} has more than 8 characters or starts with a number or contains characters other than letters in upper case, numbers, or underscores</warning>
]]></rulexquery>
</sdsrule>


<!-- Rule SD1014 applies to INTERVENTIONS, EVENTS, FINDINGS, CO, SE  -->
<sdsrule id="SD1014" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>TAETORD must be registered in TA</ruledescription>
<ruledetaileddescription>Order of Element within Arm (TAETORD) values for all datasets should match entries in the Trial Arms (TA) dataset</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>INTERVENTIONS</domain>
<domain>EVENTS</domain>
<domain>FINDINGS</domain>
<domain>CO</domain>
<domain>SE</domain>
<rulexquery><![CDATA[
(: Rule SD1014 - Invalid TAETORD:
Order of Element within Arm (TAETORD) values for all datasets should match entries in the Trial Arms (TA) dataset
:)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external;
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: Get the TA dataset :)
let $tadataset := $definedoc//odm:ItemGroupDef[@Name='TA']
let $tadatasetname := (
	if($defineversion = '2.1') then $tadataset/def21:leaf/@xlink:href
	else $tadataset/def:leaf/@xlink:href
)
let $tadatasetdoc := (
	if($tadatasetname) then doc(concat($base,$tadatasetname))
	else ()
)
(:  and the OID of TAETORD :)
let $taetordoid := (
    for $a in $definedoc//odm:ItemDef[@Name='TAETORD']/@OID 
    where $a = $definedoc//odm:ItemGroupDef[@Name='TA']/odm:ItemRef/@ItemOID
    return $a
)
(: iterate over all other datasets within INTERVENTIONS, EVENTS, FINDINGS, CO, SE :)
let $datasets := $definedoc//odm:ItemGroupDef[upper-case(@def:Class)='INTERVENTIONS' or upper-case(@def:Class)='EVENTS' or upper-case(@def:Class)='FINDINGS' 
		or upper-case(./def21:Class/@Name)='INTERVENTIONS' or upper-case(./def21:Class/@Name)='EVENTS' or upper-case(./def21:Class/@Name)='FINDINGS'
		or @Domain='SE' or @Domain='CO']
for $dataset in $datasets
    (: get name and location :)
    let $name := $dataset/@Name
	let $datasetname := (
		if($defineversion = '2.1') then $dataset/def21:leaf/@xlink:href
		else $dataset/def:leaf/@xlink:href
	)
    let $datasetdoc := (
		if($datasetname) then doc(concat($base,$datasetname))
		else ()
	)
    (: and the OID of TAETORD :)
    let $datasettaetordoid := (
        for $a in $definedoc//odm:ItemDef[@Name='TAETORD']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
    )
    (: now iterate over all the records that have a TAETORD :)
    for $record in $datasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$datasettaetordoid]]
        let $recnum := $record/@data:ItemGroupDataSeq
        (: and get the value of TAETORD :)
        let $taetord := $record/odm:ItemData[@ItemOID=$datasettaetordoid]/@Value
        (: and check whether it can be found in the TA dataset :)
        where not($tadatasetdoc//odm:ItemGroupData/odm:ItemData[@ItemOID=$taetordoid][@Value=$taetord])
    return <error rule="SD1014" dataset="TA" variable="TAETORD" rulelastupdate="2020-08-08" recordnumer="{data($recnum)}">Invalid TAETORD in {data($name)} in dataset {data($datasetname)}. TAETORD={data($taetord)} was not found in dataset {data($tadatasetname)}</error>
]]></rulexquery>
</sdsrule>

<!-- Rule SD1014 applies to INTERVENTIONS, EVENTS, FINDINGS, CO, SE  -->
<sdsrule id="SD1014-SD" last-update="2020-08-08" originator="FDA" standard="SDTM" requiresDomainOrDataset="Yes">
<ruledescription>TAETORD must be registered in TA - Order of Element within Arm (TAETORD) values for all datasets should match entries in the Trial Arms (TA) dataset</ruledescription>
<ruledetaileddescription>Order of Element within Arm (TAETORD) values for all datasets should match entries in the Trial Arms (TA) dataset</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>INTERVENTIONS</domain>
<domain>EVENTS</domain>
<domain>FINDINGS</domain>
<domain>CO</domain>
<domain>SE</domain>
<rulexquery><![CDATA[
(: Rule SD1014 - Invalid TAETORD:
Order of Element within Arm (TAETORD) values for all datasets should match entries in the Trial Arms (TA) dataset
:)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external;
declare variable $defineversion external;
declare variable $datasetname external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: Get the TA dataset :)
let $tadataset := $definedoc//odm:ItemGroupDef[@Name='TA']
let $tadatasetname := (
	if($defineversion = '2.1') then $tadataset/def:leaf/@xlink:href
	else $tadataset/def:leaf/@xlink:href
)
let $tadatasetdoc := (
	if($tadatasetname) then doc(concat($base,$tadatasetname))
	else ()
)
(:  and the OID of TAETORD :)
let $taetordoid := (
    for $a in $definedoc//odm:ItemDef[@Name='TAETORD']/@OID 
    where $a = $definedoc//odm:ItemGroupDef[@Name='TA']/odm:ItemRef/@ItemOID
    return $a
)
(: iterate over all other provided datasets within INTERVENTIONS, EVENTS, FINDINGS, CO, SE :)
let $datasets := $definedoc//odm:ItemGroupDef[@Name=$datasetname][upper-case(@def:Class)='INTERVENTIONS' or upper-case(@def:Class)='EVENTS' or upper-case(@def:Class)='FINDINGS' 
		or upper-case(./def21:Class/@Name)='INTERVENTIONS' or upper-case(./def21:Class/@Name)='EVENTS' or upper-case(./def21:Class/@Name)='FINDINGS'
		or @Domain='SE' or @Domain='CO']
for $dataset in $datasets
    (: get name and location :)
    let $name := $dataset/@Name
    let $dsname := (
		if($defineversion = '2.1') then $dataset/def21:leaf/@xlink:href
		else $dataset/def:leaf/@xlink:href
	)
    let $datasetdoc := (
		if($dsname) then doc(concat($base,$dsname))
		else ()
	)
    (: and the OID of TAETORD :)
    let $datasettaetordoid := (
        for $a in $definedoc//odm:ItemDef[@Name='TAETORD']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
    )
    (: now iterate over all the records that have a TAETORD :)
    for $record in $datasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$datasettaetordoid]]
        let $recnum := $record/@data:ItemGroupDataSeq
        (: and get the value of TAETORD :)
        let $taetord := $record/odm:ItemData[@ItemOID=$datasettaetordoid]/@Value
        (: and check whether it can be found in the TA dataset :)
        where not($tadatasetdoc//odm:ItemGroupData/odm:ItemData[@ItemOID=$taetordoid][@Value=$taetord])
    return <error rule="SD1014" dataset="TA" variable="TAETORD" rulelastupdate="2020-08-08" recordnumer="{data($recnum)}">Invalid TAETORD in {data($name)} in dataset {data($datasetname)}. TAETORD={data($taetord)} was not found in dataset {data($tadatasetname)}</error>	
]]></rulexquery>
</sdsrule>

<sdsrule id="SD1026" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>RELTYPE must be null when IDVAR is populated with a --SEQ</ruledescription>
<ruledetaileddescription>Relationship Type (RELTYPE) in RELREC must be NULL, when Identifying Variable (IDVAR) is populated with a --SEQ value</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>RELREC</domain>
<rulexquery><![CDATA[
(: Rule SD1026 - Invalid value for RELTYPE variable: 
Relationship Type (RELTYPE) in RELREC must be NULL, when Identifying Variable (IDVAR) is populated with a --SEQ value
:)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external;
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: Get the RELREC dataset :)
let $relrecdataset := $definedoc//odm:ItemGroupDef[@Name='RELREC']
let $relrecdatasetname := (
	if($defineversion = '2.1') then $relrecdataset/def21:leaf/@xlink:href
	else $relrecdataset/def:leaf/@xlink:href
)
let $relrecdatasetdoc := (
	if($relrecdatasetname) then doc(concat($base,$relrecdatasetname))
	else ()
)
(:  and the OID of RELID and of the IDVAR :)
let $reltypeoid := (
    for $a in $definedoc//odm:ItemDef[@Name='RELTYPE']/@OID 
    where $a = $definedoc//odm:ItemGroupDef[@Name='RELREC']/odm:ItemRef/@ItemOID
    return $a
)
let $idvaroid := (
    for $a in $definedoc//odm:ItemDef[@Name='IDVAR']/@OID 
    where $a = $definedoc//odm:ItemGroupDef[@Name='RELREC']/odm:ItemRef/@ItemOID
    return $a
)
(: now iterate over all the records in the RELREC dataset :)
for $record in $relrecdatasetdoc//odm:ItemGroupData
    let $recnum := $record/@data:ItemGroupDataSeq
    let $idvarvalue := $record/odm:ItemData[@ItemOID=$idvaroid]/@Value
    let $relvalue := $record/odm:ItemData[@ItemOID=$reltypeoid]/@Value
    (: and check those that have IDVAR ending with 'SEQ' - for these RELTYPE must be null :)
    where ends-with($idvarvalue,'SEQ') and string-length($relvalue) > 0 
    return <error rule="SD1026" dataset="RELREC" variable="RELTYPE" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">Invalid value for RELTYPE variable. Non-null value={data($relvalue)} was found though identifying variable is {data($idvarvalue)}</error>
]]></rulexquery>
</sdsrule>

<sdsrule id="SD1042-A" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>--STRF Time point for non-occurred event or intervention may not be provided</ruledescription>
<ruledetaileddescription>Value of Start Relative to Reference Period (--STRF) should not be populated, when an event or intervention is marked as not occurred</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>INTERVENTIONS</domain>
<domain>EVENTS</domain>
<rulexquery><![CDATA[	
(: Rule SD1042-A Time point for non-occurred event or intervation is provided: 
Value of Start Relative to Reference Period (--STRF) should not be populated, when an event or intervention is marked as not occurred (--OCCUR='N')
:)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink"; 
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/'  :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: iterate over all the datasets , but only the INTERVENTIONS and EVENTS ones :)
for $dataset in $definedoc//odm:ItemGroupDef[upper-case(@def:Class)='INTERVENTIONS' or upper-case(@def:Class)='EVENTS' 
	or upper-case(./def21:Class/@Name)='INTERVENTIONS' or upper-case(./def21:Class/@Name)='EVENTS']
    let $name := $dataset/@Name
	let $datasetname := (
		if($defineversion = '2.1') then $dataset/def21:leaf/@xlink:href
		else $dataset/def:leaf/@xlink:href
	)
    let $datasetdoc := (
		if($datasetname) then doc(concat($base,$datasetname))
		else ()
	)
    (: Get the OID of the STRF and OCCUR variables :)
    let $strfoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'STRF')]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
    )
    let $strfname := concat($name,'STRF')
    let $occuroid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'OCCUR')]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
    )
    let $occurname := $definedoc//odm:ItemDef[@OID=$occuroid]/@Name
    (: iterate over all the records that have an --OCCUR data point :)
    for $record in $datasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$occuroid]]
        let $recnum := $record/@data:ItemGroupDataSeq
        let $occurvalue := $record/odm:ItemData[@ItemOID=$occuroid]/@Value
        (: check whether STRF is populated - if so, get the value :)
        let $strfvalue := $record/odm:ItemData[@ItemOID=$strfoid]/@Value
        (: STRF amay NOT be present when OCCUR is present :)
        where $occurvalue and $strfvalue
        return <warning rule="SD1042-A" variable="{data($strfname)}" dataset="{data($name)}" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">Value of Start Relative to Reference Period ({data($strfname)} should not be populated for non-occurred event or intervation is provided, with value of {data($occurname)}={data($occurvalue)}</warning>
]]></rulexquery>
</sdsrule>

<sdsrule id="SD1042-A-SD" last-update="2020-08-08" originator="FDA" standard="SDTM" requiresDomainOrDataset="Yes">
<ruledescription>--STRF Time point for non-occurred event or intervention may not be provided</ruledescription>
<ruledetaileddescription>Value of Start Relative to Reference Period (--STRF) should not be populated, when an event or intervention is marked as not occurred</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>INTERVENTIONS</domain>
<domain>EVENTS</domain>
<rulexquery><![CDATA[	
(: Rule SD1042-A Time point for non-occurred event or intervation is provided: 
Value of Start Relative to Reference Period (--STRF) should not be populated, when an event or intervention is marked as not occurred (--OCCUR='N')
:)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink"; 
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
declare variable $datasetname external;
(: let $base := '/db/fda_submissions/cdisc01/'  :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: iterate over all the datasets , but only the provided INTERVENTIONS and EVENTS ones :)
for $dataset in $definedoc//odm:ItemGroupDef[@Name=$datasetname][upper-case(@def:Class)='INTERVENTIONS' or upper-case(@def:Class)='EVENTS' 
		or upper-case(./def21:Class/@Name)='INTERVENTIONS' or upper-case(./def21:Class/@Name)='EVENTS']
    let $name := $dataset/@Name
	let $dsname := (
		if($defineversion = '2.1') then $dataset/def21:leaf/@xlink:href
		else $dataset/def:leaf/@xlink:href
	)
    let $datasetdoc := (
		if($dsname) then doc(concat($base,$dsname))
		else ()
	)
    (: Get the OID of the STRF and OCCUR variables :)
    let $strfoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'STRF')]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
    )
    let $strfname := concat($name,'STRF')
    let $occuroid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'OCCUR')]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
    )
    let $occurname := $definedoc//odm:ItemDef[@OID=$occuroid]/@Name
    (: iterate over all the records that have an --OCCUR data point :)
    for $record in $datasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$occuroid]]
        let $recnum := $record/@data:ItemGroupDataSeq
        let $occurvalue := $record/odm:ItemData[@ItemOID=$occuroid]/@Value
        (: check whether STRF is populated - if so, get the value :)
        let $strfvalue := $record/odm:ItemData[@ItemOID=$strfoid]/@Value
        (: STRF amay NOT be present when OCCUR is present :)
        where $occurvalue and $strfvalue
        return <warning rule="SD1042-A" variable="{data($strfname)}" dataset="{data($name)}" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">Value of Start Relative to Reference Period ({data($strfname)} should not be populated for non-occurred event or intervation is provided, with value of {data($occurname)}={data($occurvalue)}</warning>
]]></rulexquery>
</sdsrule>


<sdsrule id="SD1042-B" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>--ENRF Time point for non-occurred event or intervention may not be provided</ruledescription>
<ruledetaileddescription>Value of End Relative to Reference Period (--ENRF) should not be populated, when an event or intervention is marked as not occurred</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>INTERVENTIONS</domain>
<domain>EVENTS</domain>
<rulexquery><![CDATA[	
(: Rule SD1042- Time point for non-occurred event or intervation is provided: 
Value of Start Relative to Reference Period (--STRF) or End Relative to Reference Period (--ENRF) should not be populated, when an event or intervention is marked as not occurred (--OCCUR='N')
:)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink"; 
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/'  :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: iterate over all the datasets , but only the INTERVENTIONS and EVENTS ones :)
for $dataset in $definedoc//odm:ItemGroupDef[upper-case(@def:Class)='INTERVENTIONS' or upper-case(@def:Class)='EVENTS' 
		or upper-case(./def21:Class/@Name)='INTERVENTIONS' or upper-case(./def21:Class/@Name)='EVENTS']
    let $name := $dataset/@Name
	let $datasetname := (
		if($defineversion = '2.1') then $dataset/def21:leaf/@xlink:href
		else $dataset/def:leaf/@xlink:href
	)
    let $datasetdoc := (
		if($datasetname) then doc(concat($base,$datasetname))
		else ()
	)
    (: Get the OID of the ENRF and OCCUR variables :)
    let $enrfoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'ENRF')]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
    )
    let $enrfname := concat($name,'ENRF')
    let $occuroid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'OCCUR')]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
    )
    let $occurname := $definedoc//odm:ItemDef[@OID=$occuroid]/@Name
    (: iterate over all the records that have an --OCCUR data point :)
    for $record in $datasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$occuroid]]
        let $recnum := $record/@data:ItemGroupDataSeq
        let $occurvalue := $record/odm:ItemData[@ItemOID=$occuroid]/@Value
        (: check whether ENRF are populated - if so, get the value :)
        let $enrfvalue := $record/odm:ItemData[@ItemOID=$enrfoid]/@Value
        (: ENRF may NOT be present when OCCUR is present :)
        where $occurvalue and $enrfvalue
        return <warning rule="SD1042-B" dataset="{data($name)}" variable="{data($enrfname)}" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">Value of End Relative to Reference Period ({data($enrfname)} should not be populated for non-occurred event or intervation is provided, with value of {data($occurname)}={data($occurvalue)}</warning>
]]></rulexquery>
</sdsrule>

<sdsrule id="SD1042-B-SD" last-update="2020-08-08" originator="FDA" standard="SDTM" requiresDomainOrDataset="Yes">
<ruledescription>--ENRF Time point for non-occurred event or intervention may not be provided</ruledescription>
<ruledetaileddescription>Value of End Relative to Reference Period (--ENRF) should not be populated, when an event or intervention is marked as not occurred</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>INTERVENTIONS</domain>
<domain>EVENTS</domain>
<rulexquery><![CDATA[	
(: Rule SD1042- Time point for non-occurred event or intervation is provided: 
Value of Start Relative to Reference Period (--STRF) or End Relative to Reference Period (--ENRF) should not be populated, when an event or intervention is marked as not occurred (--OCCUR='N')
:)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink"; 
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
declare variable $datasetname external;
(: let $base := '/db/fda_submissions/cdisc01/'  :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: iterate over all the provided datasets, but only the INTERVENTIONS and EVENTS ones :)
for $dataset in $definedoc//odm:ItemGroupDef[@Name=$datasetname][upper-case(@def:Class)='INTERVENTIONS' or upper-case(@def:Class)='EVENTS' 
		or upper-case(./def21:Class/@Name)='INTERVENTIONS' or upper-case(./def21:Class/@Name)='EVENTS']
    let $name := $dataset/@Name
	let $dsname := (
		if($defineversion = '2.1') then $dataset/def21:leaf/@xlink:href
		else $dataset/def:leaf/@xlink:href
	)
    let $datasetdoc := (
		if($dsname) then doc(concat($base,$dsname))
		else ()
	)
    (: Get the OID of the ENRF and OCCUR variables :)
    let $enrfoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'ENRF')]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
    )
    let $enrfname := concat($name,'ENRF')
    let $occuroid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'OCCUR')]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
    )
    let $occurname := $definedoc//odm:ItemDef[@OID=$occuroid]/@Name
    (: iterate over all the records that have an --OCCUR data point :)
    for $record in $datasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$occuroid]]
        let $recnum := $record/@data:ItemGroupDataSeq
        let $occurvalue := $record/odm:ItemData[@ItemOID=$occuroid]/@Value
        (: check whether ENRF are populated - if so, get the value :)
        let $enrfvalue := $record/odm:ItemData[@ItemOID=$enrfoid]/@Value
        (: ENRF may NOT be present when OCCUR is present :)
        where $occurvalue and $enrfvalue
        return <warning rule="SD1042-B" dataset="{data($name)}" variable="{data($enrfname)}" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">Value of End Relative to Reference Period ({data($enrfname)} should not be populated for non-occurred event or intervation is provided, with value of {data($occurname)}={data($occurvalue)}</warning>	
]]></rulexquery>
</sdsrule>


<sdsrule id="SD0043" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>--TRTV must be populated, when --VAMT is not NULL</ruledescription>
<ruledetaileddescription>Treatment Vehicle (--TRTV) should be provided, when Treatment Vehicle Amount (--VAMT) is specified</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>INTERVENTIONS</domain>
<rulexquery><![CDATA[	
(: TODO: needs further testing, did not have a good test dataset :)
(: Rule SD0043 - Missing value for --TRTV, when --VAMT is not NULL
Treatment Vehicle (--TRTV) should be provided, when Treatment Vehicle Amount (--VAMT) is specified
INTERVENTIONS
:)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: iterate over all INTERVENTION domains :)
for $dataset in $definedoc//odm:ItemGroupDef[upper-case(@def:Class)='INTERVENTIONS' or upper-case(./def21:Class/@Name)='INTERVENTIONS']
    let $name := $dataset/@Name
    let $domain := $dataset/@Domain
	let $datasetname := (
		if($defineversion = '2.1') then $dataset/def21:leaf/@xlink:href
		else $dataset/def:leaf/@xlink:href
	)
    let $datasetdoc := (
		if($datasetname) then doc(concat($base,$datasetname))
		else ()
	)
    (: get the OID of TRTV and VAMT variables :)
    let $trtvoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'TRTV')]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
    )
    let $trtvname := $definedoc//odm:ItemDef[@OID=$trtvoid]/@Name
    let $vamtoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'VAMT')]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
    )
    let $vamtname := $definedoc//odm:ItemDef[@OID=$vamtoid]/@Name
    (: iterate over all records for which VAMT is populated :)
    for $record in $datasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$vamtoid]]
        let $recnum := $record/@data:ItemGroupDataSeq
        (: and get the value of the VAMT variable :)
        let $vamtvalue := $record/odm:ItemData[@ItemOID=$vamtoid]/@Value
        (: and check whether TRTV is populated :)
        let $trtvvalue := $record/odm:ItemData[@ItemOID=$trtvoid]/@Value
        where not($trtvvalue)
        return <warning rule="SD0043" dataset="{data($name)}" variable="{data($trtvname)}" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">Missing value for {data($trtvname)}, when {data($vamtname)} is not NULL</warning>
]]></rulexquery>
</sdsrule>

<sdsrule id="SD0043-SD" last-update="2020-08-08" originator="FDA" standard="SDTM" requiresDomainOrDataset="Yes">
<ruledescription>--TRTV must be populated, when --VAMT is not NULL</ruledescription>
<ruledetaileddescription>Treatment Vehicle (--TRTV) should be provided, when Treatment Vehicle Amount (--VAMT) is specified</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>INTERVENTIONS</domain>
<rulexquery><![CDATA[	
(: TODO: needs further testing, did not have a good test dataset :)
(: Rule SD0043 - Missing value for --TRTV, when --VAMT is not NULL
Treatment Vehicle (--TRTV) should be provided, when Treatment Vehicle Amount (--VAMT) is specified
INTERVENTIONS
:)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
declare variable $datasetname external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: iterate over all provided INTERVENTION domains :)
for $dataset in $definedoc//odm:ItemGroupDef[@Name=$datasetname][upper-case(@def:Class)='INTERVENTIONS' or upper-case(./def21:Class/@Name)='INTERVENTIONS']
    let $name := $dataset/@Name
    let $domain := $dataset/@Domain
	let $dsname := (
		if($defineversion = '2.1') then $dataset/def21:leaf/@xlink:href
		else $dataset/def:leaf/@xlink:href
	)
    let $datasetdoc := (
		if($dsname) then doc(concat($base,$dsname))
		else ()
	)
    (: get the OID of TRTV and VAMT variables :)
    let $trtvoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'TRTV')]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
    )
    let $trtvname := $definedoc//odm:ItemDef[@OID=$trtvoid]/@Name
    let $vamtoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'VAMT')]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
    )
    let $vamtname := $definedoc//odm:ItemDef[@OID=$vamtoid]/@Name
    (: iterate over all records for which VAMT is populated :)
    for $record in $datasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$vamtoid]]
        let $recnum := $record/@data:ItemGroupDataSeq
        (: and get the value of the VAMT variable :)
        let $vamtvalue := $record/odm:ItemData[@ItemOID=$vamtoid]/@Value
        (: and check whether TRTV is populated :)
        let $trtvvalue := $record/odm:ItemData[@ItemOID=$trtvoid]/@Value
        where not($trtvvalue)
        return <warning rule="SD0043" dataset="{data($name)}" variable="{data($trtvname)}" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">Missing value for {data($trtvname)}, when {data($vamtname)} is not NULL</warning>
]]></rulexquery>
</sdsrule>

<!-- Rule SD0029: Missing value for -STRESU, when -STRESC is provided.
This rule is NONSENSE. There are so many tests that do not have a unit! Example: pH and ALL ordinal tests (+1,+2,…), as well tests that have "NEGATIVE", "POSITIVE", etc.' What idiot ever developed this rule?  -->

<sdsrule id="SD0030" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>--STRESC must be populated, when --STRESU is provided</ruledescription>
<ruledetaileddescription>Character Result/Finding in Std Units (--STRESC) should not be NULL, when Standard Units (--STRESU) is provided</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>FINDINGS</domain>
<rulexquery><![CDATA[
(: Rule SD0030 - Missing value for --STRESC, when --STRESU is provided:
Character Result/Finding in Std Units (--STRESC) should not be NULL, when Standard Units (--STRESU) is provided
:)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define)) 
(: Iterate over all FINDINGS domains :)
for $dataset in $definedoc//odm:ItemGroupDef[upper-case(@def:Class)='FINDINGS' or upper-case(./def21:Class/@Name)='FINDINGS']
    let $name := $dataset/@Name
	let $datasetname := (
		if($defineversion = '2.1') then $dataset/def21:leaf/@xlink:href
		else $dataset/def:leaf/@xlink:href
	)
    let $datasetdoc := (
		if($datasetname) then doc(concat($base,$datasetname))
		else ()
	)
    (: and get the OIDs of the STRESU and STRESC variables :)
    let $strescoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'STRESC')]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a 
    )
    let $strescname := $definedoc//odm:ItemDef[@OID=$strescoid]/@Name
    let $stresuoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'STRESU')]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a     
    )
    let $stresuname := $definedoc//odm:ItemDef[@OID=$stresuoid]/@Name
    (: now iterate over all records in the dataset that DO have an STRESU :)
    for $record in $datasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$stresuoid]]
        let $recnum := $record/@data:ItemGroupDataSeq
        (: and get the values for STRESC and STRESU :)
        let $strescvalue := $record/odm:ItemData[@ItemOID=$strescoid]/@Value
        let $stresuvalue := $record/odm:ItemData[@ItemOID=$stresuoid]/@Value
        (: we already selected on the presence of STRESU, STRESC MUST be populated :)
         where not($strescvalue) 
        return <warning rule="SD0030" dataset="{data($name)}" variable="{data($strescname)}" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">Missing value for {data($strescname)}, when {data($stresuname)} is provided, value={data($stresuvalue)} is provided in dataset {data($datasetname)}</warning>	
]]></rulexquery>
</sdsrule>

<sdsrule id="SD0030-SD" last-update="2020-08-08" originator="FDA" standard="SDTM" requiresDomainOrDataset="Yes">
<ruledescription>--STRESC must be populated, when --STRESU is provided</ruledescription>
<ruledetaileddescription>Character Result/Finding in Std Units (--STRESC) should not be NULL, when Standard Units (--STRESU) is provided</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>FINDINGS</domain>
<rulexquery><![CDATA[
(: Rule SD0030 - Missing value for --STRESC, when --STRESU is provided:
Character Result/Finding in Std Units (--STRESC) should not be NULL, when Standard Units (--STRESU) is provided
:)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
declare variable $datasetname external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define)) 
(: Iterate over all provided FINDINGS domains :)
for $dataset in $definedoc//odm:ItemGroupDef[@Name=$datasetname][upper-case(@def:Class)='FINDINGS' or upper-case(./def21:Class/@Name)='FINDINGS']
    let $name := $dataset/@Name
	let $dsname := (
		if($defineversion = '2.1') then $dataset/def21:leaf/@xlink:href
		else $dataset/def:leaf/@xlink:href
	)
    let $datasetdoc := (
		if($dsname) then doc(concat($base,$dsname))
		else ()
	)
    (: and get the OIDs of the STRESU and STRESC variables :)
    let $strescoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'STRESC')]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a 
    )
    let $strescname := $definedoc//odm:ItemDef[@OID=$strescoid]/@Name
    let $stresuoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'STRESU')]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a     
    )
    let $stresuname := $definedoc//odm:ItemDef[@OID=$stresuoid]/@Name
    (: now iterate over all records in the dataset that DO have an STRESU :)
    for $record in $datasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$stresuoid]]
        let $recnum := $record/@data:ItemGroupDataSeq
        (: and get the values for STRESC and STRESU :)
        let $strescvalue := $record/odm:ItemData[@ItemOID=$strescoid]/@Value
        let $stresuvalue := $record/odm:ItemData[@ItemOID=$stresuoid]/@Value
        (: we already selected on the presence of STRESU, STRESC MUST be populated :)
         where not($strescvalue) 
        return <warning rule="SD0030" dataset="{data($name)}" variable="{data($strescname)}" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">Missing value for {data($strescname)}, when {data($stresuname)} is provided, value={data($stresuvalue)} is provided in dataset {data($datasetname)}</warning>	
]]></rulexquery>
</sdsrule>

<sdsrule id="SD0045" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>--STRESC must be populated, when --RESCAT is populated</ruledescription>
<ruledetaileddescription>Character Result/Finding in Std Units (--STRESC) should be provided, when Result Category (--RESCAT) is specified</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>FINDINGS</domain>
<rulexquery><![CDATA[	
(: TODO: Further testing - we did not have a good test sample :)
(: Rule SD0045 - Missing value for --STRESC, when --RESCAT is populated:
Character Result/Finding in Std Units (--STRESC) should be provided, when Result Category (--RESCAT) is specified
:)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink"; 
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/'  :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: Iterate over all FINDINGS domains :)
for $dataset in $definedoc//odm:ItemGroupDef[upper-case(@def:Class)='FINDINGS' or upper-case(./def21:Class/@Name)='FINDINGS']
    let $name := $dataset/@Name
	let $datasetname := (
		if($defineversion = '2.1') then $dataset/def21:leaf/@xlink:href
		else $dataset/def:leaf/@xlink:href
	)
    let $datasetdoc := (
		if($datasetname) then doc(concat($base,$datasetname))
		else ()
	)
    (: and get the OIDs of the STRESC and RESCAT variables :)
    let $strescoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'STRESC')]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a 
    )
    let $strescname := $definedoc//odm:ItemDef[@OID=$strescoid]/@Name
    let $rescatoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'RESCAT')]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a     
    )
    let $rescatname := $definedoc//odm:ItemDef[@OID=$rescatoid]/@Name
    (: now iterate over all records in the dataset that DO have an RESCAT data point :)
    for $record in $datasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$rescatoid]]
        let $recnum := $record/@data:ItemGroupDataSeq
        (: and get the values for STRESC and ORRES :)
        let $strescvalue := $record/odm:ItemData[@ItemOID=$strescoid]/@Value 
        let $rescatvalue := $record/odm:ItemData[@ItemOID=$rescatoid]/@Value
        (: we already selected on the presence of RESCAT,check whether STRESC is present :)
        where not($strescvalue) 
        return <warning rule="SD0045" dataset="{data($name)}" variable="{data($strescname)}" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">Missing {data($strescname)}, when {data($rescatname)} value={data($rescatvalue)} is provided in dataset {data($datasetname)}</warning>	
]]></rulexquery>
</sdsrule>

<sdsrule id="SD0045-SD" last-update="2020-08-08" originator="FDA" standard="SDTM" requiresDomainOrDataset="Yes">
<ruledescription>--STRESC must be populated, when --RESCAT is populated</ruledescription>
<ruledetaileddescription>Character Result/Finding in Std Units (--STRESC) should be provided, when Result Category (--RESCAT) is specified</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>FINDINGS</domain>
<rulexquery><![CDATA[	
(: TODO: Further testing - we did not have a good test sample :)
(: Rule SD0045 - Missing value for --STRESC, when --RESCAT is populated:
Character Result/Finding in Std Units (--STRESC) should be provided, when Result Category (--RESCAT) is specified
:)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink"; 
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
declare variable $datasetname external;
(: let $base := '/db/fda_submissions/cdisc01/'  :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: Iterate over all provided FINDINGS domains :)
for $dataset in $definedoc//odm:ItemGroupDef[@Name=$datasetname][upper-case(@def:Class)='FINDINGS' or upper-case(./def21:Class/@Name)='FINDINGS']
    let $name := $dataset/@Name
	let $dsname := (
		if($defineversion = '2.1') then $dataset/def21:leaf/@xlink:href
		else $dataset/def:leaf/@xlink:href
	)
    let $datasetdoc := (
		if($dsname) then doc(concat($base,$dsname))
		else ()
	)
    (: and get the OIDs of the STRESC and RESCAT variables :)
    let $strescoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'STRESC')]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a 
    )
    let $strescname := $definedoc//odm:ItemDef[@OID=$strescoid]/@Name
    let $rescatoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'RESCAT')]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a     
    )
    let $rescatname := $definedoc//odm:ItemDef[@OID=$rescatoid]/@Name
    (: now iterate over all records in the dataset that DO have an RESCAT data point :)
    for $record in $datasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$rescatoid]]
        let $recnum := $record/@data:ItemGroupDataSeq
        (: and get the values for STRESC and ORRES :)
        let $strescvalue := $record/odm:ItemData[@ItemOID=$strescoid]/@Value 
        let $rescatvalue := $record/odm:ItemData[@ItemOID=$rescatoid]/@Value
        (: we already selected on the presence of RESCAT,check whether STRESC is present :)
        where not($strescvalue) 
        return <warning rule="SD0045" dataset="{data($name)}" variable="{data($strescname)}" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">Missing {data($strescname)}, when {data($rescatname)} value={data($rescatvalue)} is provided in dataset {data($datasetname)}</warning>	
]]></rulexquery>
</sdsrule>


<sdsrule id="SD1036" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>--TOXGR must be populated when --TOX is populated</ruledescription>
<ruledetaileddescription>Toxicity Grade (--TOXGR) should be provided, when Toxicity (--TOX) is populated</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>AE</domain>
<domain>MH</domain>
<domain>CE</domain>
<domain>EG</domain>
<domain>LB</domain>
<domain>PC</domain>
<domain>PP</domain>
<rulexquery><![CDATA[	
(: Rule SD1036 - Missing value for --TOXGR, when --TOX is populated:
Toxicity Grade (--TOXGR) should be provided, when Toxicity (--TOX) is populated
Domains: AE, MH, CE, EG, LB, PC, PP
:)
(: TODO: better testing, we did not have a good test dataset :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(:  iterate over the AE, MH, CE, EG, LB, PC, PP datasets :)
for $dataset in $definedoc/odm:ItemGroupDef[starts-with(@Name,'AE') or @Domain='AE' or starts-with(@Name,'MH') or @Domain='MH' or starts-with(@Name,'CE') or @Domain='CE' or starts-with(@Name,'EG') or @Domain='EG' or starts-with(@Name,'LB') or @Domain='LB' or starts-with(@Name,'PC') or @Domain='PC' or starts-with(@Name,'PP') or @Domain='PP']
    let $name := $dataset/@Name
	let $datasetname := (
		if($defineversion = '2.1') then $dataset/def21:leaf/@xlink:href
		else $dataset/def:leaf/@xlink:href
	)
    let $datasetdoc := (
		if($datasetname) then doc(concat($base,$datasetname))
		else ()
	)
    (: Get the OIDs of the TOX and TOXGR variables :)
    let $toxoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'TOX')]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
    )
    let $toxname := $definedoc//odm:ItemDef[@OID=$toxoid]/@Name
    let $toxgroid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'TOXGR')]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
    )
    let $toxgrname := $definedoc//odm:ItemDef[@OID=$toxgroid]/@Name
    (: get all records for which TOX is populated :)
    for $record in $datasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$toxoid]]
        let $recnum := $record/@data:ItemGroupDataSeq
        (: and the value of TOX :)
        let $toxvalue := $record/odm:ItemData[@ItemOID=$toxoid]/@Value
        (: and check whether TOXGR is populated :)
        let $toxgrvalue := $record/odm:ItemData[@ItemOID=$toxgroid]/@Value
        where not($toxgrvalue)
        return <warning rule="SD1036" dataset="{data($name)}" variable="{data($toxgrname)}" rulelastupdate="2020-08-08" recordnumber="{$recnum}">Missing value for {data($toxgrname)}, when {data($toxname)} is populated, value={data($toxvalue)}</warning>
]]></rulexquery>
</sdsrule>

<sdsrule id="SD1036-SD" last-update="2020-08-08" originator="FDA" standard="SDTM" requiresDomainOrDataset="Yes">
<ruledescription>--TOXGR must be populated when --TOX is populated</ruledescription>
<ruledetaileddescription>Toxicity Grade (--TOXGR) should be provided, when Toxicity (--TOX) is populated</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>AE</domain>
<domain>MH</domain>
<domain>CE</domain>
<domain>EG</domain>
<domain>LB</domain>
<domain>PC</domain>
<domain>PP</domain>
<rulexquery><![CDATA[	
(: Rule SD1036 - Missing value for --TOXGR, when --TOX is populated:
Toxicity Grade (--TOXGR) should be provided, when Toxicity (--TOX) is populated
Domains: AE, MH, CE, EG, LB, PC, PP
:)
(: TODO: better testing, we did not have a good test dataset :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
declare variable $datasetname external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(:  iterate over the provided AE, MH, CE, EG, LB, PC, PP datasets :)
for $dataset in $definedoc/odm:ItemGroupDef[@Name=$datasetname][starts-with(@Name,'AE') or @Domain='AE' or starts-with(@Name,'MH') or @Domain='MH' or starts-with(@Name,'CE') or @Domain='CE' or starts-with(@Name,'EG') or @Domain='EG' or starts-with(@Name,'LB') or @Domain='LB' or starts-with(@Name,'PC') or @Domain='PC' or starts-with(@Name,'PP') or @Domain='PP']
    let $name := $dataset/@Name
	let $dsname := (
		if($defineversion = '2.1') then $dataset/def21:leaf/@xlink:href
		else $dataset/def:leaf/@xlink:href
	)
    let $datasetdoc := (
		if($dsname) then doc(concat($base,$dsname))
		else ()
	)
    (: Get the OIDs of the TOX and TOXGR variables :)
    let $toxoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'TOX')]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
    )
    let $toxname := $definedoc//odm:ItemDef[@OID=$toxoid]/@Name
    let $toxgroid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'TOXGR')]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
    )
    let $toxgrname := $definedoc//odm:ItemDef[@OID=$toxgroid]/@Name
    (: get all records for which TOX is populated :)
    for $record in $datasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$toxoid]]
        let $recnum := $record/@data:ItemGroupDataSeq
        (: and the value of TOX :)
        let $toxvalue := $record/odm:ItemData[@ItemOID=$toxoid]/@Value
        (: and check whether TOXGR is populated :)
        let $toxgrvalue := $record/odm:ItemData[@ItemOID=$toxgroid]/@Value
        where not($toxgrvalue)
        return <warning rule="SD1036" dataset="{data($name)}" variable="{data($toxgrname)}" rulelastupdate="2020-08-08" recordnumber="{$recnum}">Missing value for {data($toxgrname)}, when {data($toxname)} is populated, value={data($toxvalue)}</warning>	
]]></rulexquery>
</sdsrule>

<!-- Rule SD1098 applies to INTERVENTIONS, EVENTS, FINDINGS -->
<sdsrule id="SD1098" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>--CAT value must be populated, when --SCAT value is populated</ruledescription>
<ruledetaileddescription>Value for Category (--CAT) variable should be populated, when value for Subcategory (--SCAT) variable is populated</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>INTERVENTIONS</domain>
<domain>EVENTS</domain>
<domain>FINDINGS</domain>
<rulexquery><![CDATA[	
(: Rule SD1098 - Missing --CAT value, when --SCAT value is populated:
Value for Category (--CAT) variable should be populated, when value for Subcategory (--SCAT) variable is populated
INTERVENTIONS, EVENTS, FINDINGS :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/'  :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: iterate over all INTERVENTIONS, EVENTS and FINDINGS domains :)
for $dataset in $definedoc//odm:ItemGroupDef[upper-case(@def:Class)='INTERVENTIONS' or upper-case(@def:Class)='EVENTS' or upper-case(@def:Class)='FINDINGS' 
		or upper-case(./def21:Class/@Name)='INTERVENTIONS' or upper-case(./def21:Class/@Name)='EVENTS' or upper-case(./def21:Class/@Name)='FINDINGS']
    let $name := $dataset/@Name
    let $domain := $dataset/@Domain
	let $datasetname := (
		if($defineversion = '2.1') then $dataset/def21:leaf/@xlink:href
		else $dataset/def:leaf/@xlink:href
	)
    let $datasetdoc := (
		if($datasetname) then doc(concat($base,$datasetname))
		else ()
	)
    (: get the OID of --CAT and --SCAT and VAMT variables :)
    let $catoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'CAT') and string-length(@Name)=5]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
    )
    let $catname := $definedoc//odm:ItemDef[@OID=$catoid]/@Name
    let $scatoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'SCAT') and string-length(@Name)=6]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
    )
    let $scatname := $definedoc//odm:ItemDef[@OID=$scatoid]/@Name
    (: iterate over all records for which --SCAT is populated :)
    for $record in $datasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$scatoid]]
        let $recnum := $record/@data:ItemGroupDataSeq
        (: and get the value of the --SCAT variable :)
        let $scatvalue := $record/odm:ItemData[@ItemOID=$scatoid]/@Value
        (: and check whether CAT is populated :)
        let $catvalue := $record/odm:ItemData[@ItemOID=$catoid]/@Value
        where not($catvalue)
        return <error rule="SD1098" dataset="{data($name)}" variable="{data($catname)}" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">Missing {data($catname)} value, when {data($scatname)} value is populated</error>	
]]></rulexquery>
</sdsrule>

<!-- Rule SD1098 applies to INTERVENTIONS, EVENTS, FINDINGS -->
<sdsrule id="SD1098-SD" last-update="2020-08-08" originator="FDA" standard="SDTM" requiresDomainOrDataset="Yes">
<ruledescription>--CAT value must be populated, when --SCAT value is populated</ruledescription>
<ruledetaileddescription>Value for Category (--CAT) variable should be populated, when value for Subcategory (--SCAT) variable is populated</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>INTERVENTIONS</domain>
<domain>EVENTS</domain>
<domain>FINDINGS</domain>
<rulexquery><![CDATA[	
(: Rule SD1098 - Missing --CAT value, when --SCAT value is populated:
Value for Category (--CAT) variable should be populated, when value for Subcategory (--SCAT) variable is populated
INTERVENTIONS, EVENTS, FINDINGS :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
declare variable $datasetname external;
(: let $base := '/db/fda_submissions/cdisc01/'  :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: iterate over all INTERVENTIONS, EVENTS and FINDINGS domains :)
for $dataset in $definedoc//odm:ItemGroupDef[@Name=$datasetname][upper-case(@def:Class)='INTERVENTIONS' or upper-case(@def:Class)='EVENTS' or upper-case(@def:Class)='FINDINGS' 
		or upper-case(./def21:Class/@Name)='INTERVENTIONS' or upper-case(./def21:Class/@Name)='EVENTS' or upper-case(./def21:Class/@Name)='FINDINGS']
    let $name := $dataset/@Name
    let $domain := $dataset/@Domain
	let $dsname := (
		if($defineversion = '2.1') then $dataset/def21:leaf/@xlink:href
		else $dataset/def:leaf/@xlink:href
	)
    let $datasetdoc := (
		if($dsname) then doc(concat($base,$dsname))
		else ()
	)
    (: get the OID of --CAT and --SCAT and VAMT variables :)
    let $catoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'CAT') and string-length(@Name)=5]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
    )
    let $catname := $definedoc//odm:ItemDef[@OID=$catoid]/@Name
    let $scatoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'SCAT') and string-length(@Name)=6]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
    )
    let $scatname := $definedoc//odm:ItemDef[@OID=$scatoid]/@Name
    (: iterate over all records for which --SCAT is populated :)
    for $record in $datasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$scatoid]]
        let $recnum := $record/@data:ItemGroupDataSeq
        (: and get the value of the --SCAT variable :)
        let $scatvalue := $record/odm:ItemData[@ItemOID=$scatoid]/@Value
        (: and check whether CAT is populated :)
        let $catvalue := $record/odm:ItemData[@ItemOID=$catoid]/@Value
        where not($catvalue)
        return <error rule="SD1098" dataset="{data($name)}" variable="{data($catname)}" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">Missing {data($catname)} value, when {data($scatname)} value is populated</error>
]]></rulexquery>
</sdsrule>

<!-- Rule SD1099 applies to INTERVENTIONS, EVENTS, FINDINGS -->
<sdsrule id="SD1099" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>--CAT variable must be present, when --SCAT variable is present</ruledescription>
<ruledetaileddescription>Category (--CAT) variable should be included into dataset, when Subcategory (--SCAT) variable is present</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>INTERVENTIONS</domain>
<domain>EVENTS</domain>
<domain>FINDINGS</domain>
<rulexquery><![CDATA[	
(:  Rule SD1099 - Missing --CAT variable, when --SCAT variable is present:
Category (--CAT) variable should be included into dataset, when Subcategory (--SCAT) variable is present.
This is to be tested on the level of define.xml
:)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: iterate over all INTERVENTIONS, EVENTS and FINDINGS domains :)
for $dataset in $definedoc//odm:ItemGroupDef[upper-case(@def:Class)='INTERVENTIONS' or upper-case(@def:Class)='EVENTS' or upper-case(@def:Class)='FINDINGS' 
		or upper-case(./def21:Class/@Name)='INTERVENTIONS' or upper-case(./def21:Class/@Name)='EVENTS' or upper-case(./def21:Class/@Name)='FINDINGS']
    let $name := $dataset/@Name
    let $domain := $dataset/@Domain
    (: get the OID of --CAT and --SCAT variables :)
    let $catoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'CAT') and string-length(@Name)=5]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
    )
    let $catname := concat($domain,'CAT')
    let $scatoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'SCAT') and string-length(@Name)=6]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
    )
    let $scatname := //odm:ItemDef[@OID=$scatoid]/@Name
    (: testing on level of define.xml - we just test whether there is an ItemRef/ItemDef for --CAT :)
    where $scatoid and not($catoid)
    return <warning rule="SD1099" dataset="{data($name)}" rulelastupdate="2020-08-08">Missing {data($catname)} variable, when {data($scatname)} variable is present</warning>
]]></rulexquery>
</sdsrule>


<sdsrule originator="FDA" id="SD2022" standard="SDTM" last-update="2020-08-08">
	<ruledescription>AGEU must be populated, when AGE or AGETXT is populated</ruledescription>
	<ruledetaileddescription>Age Units (AGEU) variable values must be provided, when Age (AGE) or Age Range (AGETXT) variables values are populated</ruledetaileddescription>
	<igversion>3.1.3</igversion>
	<igversion>3.2</igversion>
	<domain>DM</domain>
	<synonym context="CDISC">CG0432</synonym>
	<rulexquery><![CDATA[
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/'  :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define)) 
(: Get the DM dataset :)
for $dataset in $definedoc//odm:ItemGroupDef[@Name='DM']
	let $datasetname := (
		if($defineversion = '2.1') then $dataset/def21:leaf/@xlink:href
		else $dataset/def:leaf/@xlink:href
	)
    let $datasetdoc := (
		if($datasetname) then doc(concat($base,$datasetname))
		else ()
	)
    (: get the OID of the AGE, AGETXT and AGEU variables (when present) :)
    let $ageoid := (
        for $a in $definedoc//odm:ItemDef[@Name='AGE']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name='DM']/odm:ItemRef/@ItemOID
        return $a
    )
    let $agetxtoid := (
        for $a in $definedoc//odm:ItemDef[@Name='AGETXT']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name='DM']/odm:ItemRef/@ItemOID
        return $a
    )
    let $ageuoid := (
        for $a in $definedoc//odm:ItemDef[@Name='AGEU']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name='DM']/odm:ItemRef/@ItemOID
        return $a
    )
    (: iterate over all the records in the DM dataset that have AGE or AGETXT populated :)
    for $record in $datasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$ageoid] or odm:ItemData[@ItemOID=$agetxtoid]]
        let $recnum := $record/@data:ItemGroupDataSeq
        (: and get the values of AGE, AGETXT and AGEU (when populated) :)
        let $agevalue := $record/odm:ItemData[@ItemOID=$ageoid]/@Value
        let $agetxtvalue := $record/odm:ItemData[@ItemOID=$agetxtoid]/@Value
        let $ageuvalue := $record/odm:ItemData[@ItemOID=$ageuoid]/@Value
        (: AGEU must be populated :)
        where not($ageuvalue)
        return <error rule="SD2022" dataset="DM" variable="AGEU" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">AGEU=null, although AGE or AGETXT is populated</error>	
		]]>
	</rulexquery>
</sdsrule>

<sdsrule id="SD2021" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>One of AGE or AGETXT  must be populated, when AGEU is provided</ruledescription>
<ruledetaileddescription>Either of Age (AGE) or Age Range (AGETXT) variable values should be provided, when Age Units (AGEU) are populated</ruledetaileddescription>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>DM</domain>
<rulexquery><![CDATA[	
(: Rule SD2021 - Missing values for both AGE and AGETXT, when AGEU is provided:
Either of Age (AGE) or Age Range (AGETXT) variable values should be provided, when Age Units (AGEU) are populated
:)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/'  :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: Get the DM dataset :)
for $dataset in $definedoc//odm:ItemGroupDef[@Name='DM']
	let $datasetname := (
		if($defineversion = '2.1') then $dataset/def21:leaf/@xlink:href
		else $dataset/def:leaf/@xlink:href
	)
    let $datasetdoc := (
		if($datasetname) then doc(concat($base,$datasetname))
		else ()
	)
    (: get the OID of the AGE, AGETXT and AGEU variables (when present) :)
    let $ageoid := (
        for $a in $definedoc//odm:ItemDef[@Name='AGE']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name='DM']/odm:ItemRef/@ItemOID
        return $a
    )
    let $agetxtoid := (
        for $a in $definedoc//odm:ItemDef[@Name='AGETXT']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name='DM']/odm:ItemRef/@ItemOID
        return $a
    )
    let $ageuoid := (
        for $a in $definedoc//odm:ItemDef[@Name='AGEU']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name='DM']/odm:ItemRef/@ItemOID
        return $a
    )
    (: iterate over all the records in the DM dataset that have AGEU populated :)
    for $record in $datasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$ageuoid]]
        let $recnum := $record/@data:ItemGroupDataSeq
        (: and get the values of AGE, AGETXT(when populated) :)
        let $agevalue := $record/odm:ItemData[@ItemOID=$ageoid]/@Value
        let $agetxtvalue := $record/odm:ItemData[@ItemOID=$agetxtoid]/@Value
        (: one AGE or AGETXT must be populated :)
        where not($agevalue or $agetxtvalue)
        return <error rule="SD2021" dataset="DM" variable="AGE" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">Missing values for both AGE and AGETXT, when AGEU is provided</error>
]]></rulexquery>
</sdsrule>

<sdsrule id="SD2004" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>DTHFL must be populated, when DTHDTC is populated</ruledescription>
<ruledetaileddescription>Subject Death Flag (DTHFL) value must equal 'Y', when Date/Time of Death (DTHDTC) is provided</ruledetaileddescription>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>DM</domain>
<rulexquery><![CDATA[	
(: Rule SD2004 - Missing value for DTHFL, when DTHDTC is populated:
Subject Death Flag (DTHFL) value must equal 'Y', when Date/Time of Death (DTHDTC) is provided 
:)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: Get the DM dataset :)
for $dataset in $definedoc//odm:ItemGroupDef[@Name='DM']
	let $datasetname := (
		if($defineversion = '2.1') then $dataset/def21:leaf/@xlink:href
		else $dataset/def:leaf/@xlink:href
	)
    let $datasetdoc := (
		if($datasetname) then doc(concat($base,$datasetname))
		else ()
	)
    (: get the OID of the DTHFL and of DTHDTC :)
    let $dthfloid := (
        for $a in $definedoc//odm:ItemDef[@Name='DTHFL']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name='DM']/odm:ItemRef/@ItemOID
        return $a
    )
    let $dhdtcoid := (
        for $a in $definedoc//odm:ItemDef[@Name='DTHDTC']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name='DM']/odm:ItemRef/@ItemOID
        return $a
    )
    (: iterate over all the records in the DM dataset that have DTHDTC populated :)
    for $record in $datasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$dhdtcoid]]
        let $recnum := $record/@data:ItemGroupDataSeq
        (: get the value of DTHFL :)
        let $dthflvalue := $record/odm:ItemData[@ItemOID=$dthfloid]/@Value
        (: and check whether DTHFL=Y :)
        where not($dthflvalue='Y')
        return <error rule="SD2004" dataset="DM" variable="DTHFL" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">Missing value for DTHFL, when DTHDTC is populated</error>	
]]></rulexquery>
</sdsrule>


<sdsrule id="SD2016" last-update="2020-08-08" originator="FDA" requiresMedDRA="Yes" standard="SDTM">
<ruledescription>Value for --SOCCD must be in MedDRA dictionary</ruledescription>
<ruledetaileddescription>Value for System Organ Class Code (--SOCCD) variable must be populated using a System Organ Class Code of the MedDRA dictionary of a version specified in the define.xml</ruledetaileddescription>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>AE</domain>
<domain>MH</domain>
<domain>CE</domain>
<rulexquery><![CDATA[
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
declare namespace xs="http://www.w3.org/2001/XMLSchema";
declare namespace functx = "http://www.functx.com";
(: function to find out whether a value is in a sequence (array) :)
declare function functx:is-value-in-sequence
  ( $value as xs:anyAtomicType? ,
    $seq as xs:anyAtomicType* )  as xs:boolean {
   $value = $seq
} ;
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
declare variable $meddrabase external; 
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
(: let $meddrabase := 'file:///e:/Validation_Rules_XQuery/meddra_17_1_english/MedAscii/' :)
let $meddrafile := 'soc.asc' 
let $meddralocation := concat($meddrabase,$meddrafile)
(: read the meddra file line by line :)
let $lines := unparsed-text-lines($meddralocation) 
(: get the SOC codes, which is the first fied - field separator is the $ character :)
let $soccodes := (
	for $line in $lines
	return tokenize($line,'\$')[2]
)  
(: $soccodes know contains all the MedDRA SOC codes :)
(: iterate over all AE, MH and CE dataset definitions :)
let $definedoc := doc(concat($base,$define))
for $itemgroup in $definedoc//odm:ItemGroupDef[@Name='AE' or @Name='MH' or @Name='CE' or @Domain='AE' or @Domain='MH' or @Domain='CE']
    let $name := $itemgroup/@Name
    (: get the OID of the --SOCCD variable :)
    let $soccdoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'SOCCD') and string-length(@Name)=7]/@OID 
        where $a = $itemgroup/odm:ItemRef/@ItemOID
        return $a
    )
    let $soccdname := $definedoc//odm:ItemDef[@OID=$soccdoid]/@Name
    (: get the dataset location :)
	let $datasetlocation := (
		if($defineversion = '2.1') then $itemgroup/def21:leaf/@xlink:href
		else $itemgroup/def:leaf/@xlink:href
	)
    let $datasetdoc := (
		if($datasetlocation) then doc(concat($base,$datasetlocation))
		else ()
	)
    (: iterate over all the records in the dataset :)
    for $record in $datasetdoc//odm:ItemGroupData
        let $recnum := $record/@data:ItemGroupDataSeq
        (: get the value of --SOCCD :)
        let $soccdvalue := $record/odm:ItemData[@ItemOID=$soccdoid]/@Value
        (: give an error when the SOCCD value is not one of the SOC codes in MedDRA :)
        where $soccdvalue and not(functx:is-value-in-sequence($soccdvalue,$soccodes)) 
        return <error rule="SD2016" dataset="{data($name)}" variable="{data($soccdname)}" recordnumber="{data($recnum)}" rulelastupdate="2020-08-08">Value '{data($soccdvalue)}' for {data($soccdname)} was not found in the MedDRA dictionary</error>			
]]></rulexquery>
</sdsrule>

<sdsrule id="SD1217" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>TSVAL value for AGEMIN must be in ISO 8601 'duration' formal or null</ruledescription>
<ruledetaileddescription>TSVAL value must be either ISO 8601 format for time period (e.g. P80Y) or null, when TSPARMCD='AGEMIN'</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>TS</domain>
<rulexquery><![CDATA[	
(: Rule SD1217 - Invalid TSVAL value for AGEMIN:
TSVAL value must be either ISO 8601 format for time period (e.g. P80Y) or null, when TSPARMCD='AGEMIN'
:)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
declare namespace xs="http://www.w3.org/2001/XMLSchema";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: get the TS dataset :)
let $tsdataset := $definedoc//odm:ItemGroupDef[@Name='TS']
let $tsdatasetname := (
	if($defineversion = '2.1') then $tsdataset/def21:leaf/@xlink:href
	else $tsdataset/def:leaf/@xlink:href
)
let $tsdatasetdoc := (
	if($tsdatasetname) then doc(concat($base,$tsdatasetname))
	else ()
)
(: get the OID of the TSPARMCD variable and of the TSVAL variable :)
let $tsparmcdoid := (
    for $a in $definedoc//odm:ItemDef[@Name='TSPARMCD']/@OID 
    where $a = $definedoc//odm:ItemGroupDef[@Name='TS']/odm:ItemRef/@ItemOID
    return $a
)
let $tsvaloid := (
    for $a in $definedoc//odm:ItemDef[@Name='TSVAL']/@OID 
    where $a = $definedoc//odm:ItemGroupDef[@Name='TS']/odm:ItemRef/@ItemOID
    return $a
)
(: get the record in the TS dataset that has TSPARMCD=AGEMIN :)
let $tsageminrecord := $tsdatasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$tsparmcdoid and @Value='AGEMIN']] 
let $tsageminrecordnumber := $tsageminrecord/@data:ItemGroupDataSeq
(: and get the TSVAL value :)
let $tsvalvalue := $tsageminrecord/odm:ItemData[@ItemOID=$tsvaloid]/@Value
(: TSVAL must be either null or in ISO-8601 "duration" format :)
where $tsvalvalue and not($tsvalvalue castable as xs:duration)
return <error rule="SD1217" dataset="TS" variable="TSVAL" rulelastupdate="2020-08-08" recordnumber="{data($tsageminrecordnumber)}">Invalid (non-ISO8601) TSVAL value={data($tsvalvalue)} for AGEMIN in dataset TS</error>	
]]></rulexquery>
</sdsrule>

<sdsrule id="SD1219" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>TSVAL value for LENGTH must be in ISO 8601 'duration' formal or null</ruledescription>
<ruledetaileddescription>TSVAL value must be either ISO 8601 format for time period (e.g. P80Y) or null, when TSPARMCD='LENGTH'</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>TS</domain>
<rulexquery><![CDATA[	
(: Rule SD1219 - Invalid TSVAL value for LENGTH:
TSVAL value must be either ISO 8601 format for time period (e.g. P80Y) or null, when TSPARMCD='LENGTH'
:)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
declare namespace xs="http://www.w3.org/2001/XMLSchema";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: get the TS dataset :)
let $tsdataset := $definedoc//odm:ItemGroupDef[@Name='TS']
let $tsdatasetname := (
	if($defineversion = '2.1') then $tsdataset/def21:leaf/@xlink:href
	else $tsdataset/def:leaf/@xlink:href
)
let $tsdatasetdoc := (
	if($tsdatasetname) then doc(concat($base,$tsdatasetname))
	else ()
)
(: get the OID of the TSPARMCD variable and of the TSVAL variable :)
let $tsparmcdoid := (
    for $a in $definedoc//odm:ItemDef[@Name='TSPARMCD']/@OID 
    where $a = $definedoc//odm:ItemGroupDef[@Name='TS']/odm:ItemRef/@ItemOID
    return $a
)
let $tsvaloid := (
    for $a in $definedoc//odm:ItemDef[@Name='TSVAL']/@OID 
    where $a = $definedoc//odm:ItemGroupDef[@Name='TS']/odm:ItemRef/@ItemOID
    return $a
)
(: get the record in the TS dataset that has TSPARMCD=LENGTH :)
let $lengthrecord := $tsdatasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$tsparmcdoid and @Value='LENGTH']] 
let $lengthrecordnumber := $lengthrecord/@data:ItemGroupDataSeq
(: and get the TSVAL value :)
let $tsvalvalue := $lengthrecord/odm:ItemData[@ItemOID=$tsvaloid]/@Value
(: TSVAL must be either null or in ISO-8601 "duration" format :)
where $tsvalvalue and not($tsvalvalue castable as xs:duration)
return <error rule="SD1219" dataset="TS" variable="TSVAL" rulelastupdate="2020-08-08" recordnumber="{data($lengthrecordnumber)}">Invalid (non-ISO8601) TSVAL value={data($tsvalvalue)} for LENGTH in dataset TS</error>
]]></rulexquery>
</sdsrule>

<!-- Shouldn't it be "a positive integer"? -->
<sdsrule id="SD1221" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>TSVAL value for PLANSUB record must be numeric</ruledescription>
<ruledetaileddescription>TSVAL for PLANSUB record must be numeric</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>TS</domain>
<rulexquery><![CDATA[	
(: Rule SD1221 - Invalid TSVAL value for PLANSUB:
TSVAL for PLANSUB record must be numeric
:)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
declare namespace xs="http://www.w3.org/2001/XMLSchema";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: get the TS dataset :)
let $tsdataset := $definedoc//odm:ItemGroupDef[@Name='TS']
let $tsdatasetname := (
	if($defineversion = '2.1') then $tsdataset/def21:leaf/@xlink:href
	else $tsdataset/def:leaf/@xlink:href
)
let $tsdatasetdoc := (
	if($tsdatasetname) then doc(concat($base,$tsdatasetname))
	else ()
)
(: get the OID of the TSPARMCD variable and of the TSVAL variable :)
let $tsparmcdoid := (
    for $a in $definedoc//odm:ItemDef[@Name='TSPARMCD']/@OID 
    where $a = $definedoc//odm:ItemGroupDef[@Name='TS']/odm:ItemRef/@ItemOID
    return $a
)
let $tsvaloid := (
    for $a in $definedoc//odm:ItemDef[@Name='TSVAL']/@OID 
    where $a = $definedoc//odm:ItemGroupDef[@Name='TS']/odm:ItemRef/@ItemOID
    return $a
)
(: get the record in the TS dataset that has TSPARMCD=PLANSUB :)
let $tsageminrecord := $tsdatasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$tsparmcdoid and @Value='PLANSUB']] 
let $tsageminrecordnumber := $tsageminrecord/@data:ItemGroupDataSeq
(: and get the TSVAL value :)
let $tsvalvalue := $tsageminrecord/odm:ItemData[@ItemOID=$tsvaloid]/@Value
(: TSVAL must be an integer :)
where $tsvalvalue and not($tsvalvalue castable as xs:integer)
return <error rule="SD1221" rulelastupdate="2020-08-08" dataset="TS" variable="TSVAL" recordnumber="{data($tsageminrecordnumber)}">Invalid TSVAL value for PLANSUB, value={data($tsvalvalue)} in dataset TS</error>
]]></rulexquery>
</sdsrule>

<sdsrule id="SD2208" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>STOPRULE Trial Summary Parameter must be present</ruledescription>
<ruledetaileddescription>'Study Stop Rules' (STOPRULE) record may be populated in Trial Summay (TS) domain. It is permissible for SDTM IG v3.1.2 data and in all more recent SDTM versions</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>TS</domain>
<rulexquery><![CDATA[
(: Rule SD2208 - Missing STOPRULE Trial Summary Parameter:
'Study Stop Rules' (STOPRULE) record may be populated in Trial Summay (TS) domain. It is permissible for SDTM IG v3.1.2 data and in all more recent SDTM versions.
:)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
declare namespace xs="http://www.w3.org/2001/XMLSchema";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: get the TS dataset :)
let $tsdataset := $definedoc//odm:ItemGroupDef[@Name='TS']
let $tsdatasetname := (
	if($defineversion = '2.1') then $tsdataset/def21:leaf/@xlink:href
	else $tsdataset/def:leaf/@xlink:href
)
let $tsdatasetdoc := (
	if($tsdatasetname) then doc(concat($base,$tsdatasetname))
	else ()
)
(: get the OID of the TSPARMCD variable :)
let $tsparmcdoid := (
    for $a in $definedoc//odm:ItemDef[@Name='TSPARMCD']/@OID 
    where $a = $definedoc//odm:ItemGroupDef[@Name='TS']/odm:ItemRef/@ItemOID
    return $a
)
(: count the number of records for which TSPARMCD=STOPRULE :)
let $count := count($tsdatasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$tsparmcdoid and @Value='STOPRULE']])
(: The number of STOPRULE records may not be 0 :)
where $count = 0
return <error rule="SD2208" dataset="TS" rulelastupdate="2020-08-08">Missing STOPRULE Trial Summary Parameter in dataset TS</error>
]]></rulexquery>
</sdsrule>

<sdsrule id="SD2250" last-update="2020-08-08" originator="FDA" standard="SDTM" webservice="Uses NLM UNII Web Service">
<ruledescription>TSVAL value for CURTRT must be from FDA-SRS</ruledescription>
<ruledetaileddescription>TSVAL for CURTRT record must be a valid preferred term from FDA Substance Registration System (SRS)</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>TS</domain>
<rulexquery><![CDATA[	
(: Rule SD2250 - Invalid TSVAL value for CURTRT:
TSVAL for CURTRT record must be a valid preferred term from FDA Substance Registration System (SRS)
:)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
declare namespace xs="http://www.w3.org/2001/XMLSchema";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: get the TS dataset :)
let $tsdataset := $definedoc//odm:ItemGroupDef[@Name='TS']
let $tsdatasetname := (
	if($defineversion = '2.1') then $tsdataset/def21:leaf/@xlink:href
	else $tsdataset/def:leaf/@xlink:href
)
let $tsdatasetdoc := (
	if($tsdatasetname) then doc(concat($base,$tsdatasetname))
	else ()
)
(: get the OID of the TSPARMCD and TSVAL variables :)
let $tsparmcdoid := (
    for $a in $definedoc//odm:ItemDef[@Name='TSPARMCD']/@OID 
    where $a = $definedoc//odm:ItemGroupDef[@Name='TS']/odm:ItemRef/@ItemOID
    return $a
)
let $tsvaloid := (
    for $a in $definedoc//odm:ItemDef[@Name='TSVAL']/@OID 
    where $a = $definedoc//odm:ItemGroupDef[@Name='TS']/odm:ItemRef/@ItemOID
    return $a
)
(: get all the records that have TSPARMCD=CURTRT :)
for $record in $tsdatasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$tsparmcdoid and @Value='CURTRT']]
    let $recnum := $record/@data:ItemGroupDataSeq
    (: get the value of the TSVAL variable :)
    let $tsvalvalue := $record/odm:ItemData[@ItemOID=$tsvaloid]/@Value
    (: invoke the NLM webservice to check whether this is a valid preferred term :)
    let $webservice := concat('https://dailymed.nlm.nih.gov/dailymed/services/v2/uniis.xml?active_moeity=',$tsvalvalue)
    (: the webservice returns an XML document with the structure /uniis/unii - count the number of unii elements :)
    let $webserviceresult := doc($webservice)
    let $uniicount := count($webserviceresult/uniis/unii)
    (: the preferred term is invalid when the count=0 :)
    (: TODO: the web service also gives uniis even when the preferred term is invalid, e.g.:
    https://dailymed.nlm.nih.gov/dailymed/services/v2/uniis.xml?active_moeity=nothing
    TODO: ask the authors of the webservice :)
    where $uniicount=0
    return <error rule="SD2250" dataset="TS" variable="TSVAL" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">Invalid TSVAL value={data($tsvalvalue)} for CURTRT in dataset TS</error>
]]></rulexquery>
</sdsrule>

<sdsrule id="SD2251" last-update="2020-08-08" originator="FDA" standard="SDTM" webservice="Uses NLM UNII Web Service">
<ruledescription>Invalid TSVALCD value for CURTRT</ruledescription>
<ruledetaileddescription>TSVALCD for CURTRT record must be a valid unique ingredient identifier from FDA Substance Registration System (SRS)</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>TS</domain>
<rulexquery><![CDATA[
(: Rule SD2251 - Invalid TSVALCD value for CURTRT must be from FDA-SRS  - 
TSVALCD for CURTRT record must be a valid unique ingredient identifier from FDA Substance Registration System (SRS)
:)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
declare namespace xs="http://www.w3.org/2001/XMLSchema";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external;
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define)) 
(: get the TS dataset :)
let $tsdataset := $definedoc//odm:ItemGroupDef[@Name='TS']
let $tsdatasetname := (
	if($defineversion = '2.1') then $tsdataset/def21:leaf/@xlink:href
	else $tsdataset/def:leaf/@xlink:href
)
let $tsdatasetdoc := (
	if($tsdatasetname) then doc(concat($base,$tsdatasetname))
	else ()
)
(: get the OID of the TSPARMCD and TSVALCD variables :)
let $tsparmcdoid := (
    for $a in $definedoc//odm:ItemDef[@Name='TSPARMCD']/@OID 
    where $a = $definedoc//odm:ItemGroupDef[@Name='TS']/odm:ItemRef/@ItemOID
    return $a
)
let $tsvalcdoid := (
    for $a in $definedoc//odm:ItemDef[@Name='TSVALCD']/@OID 
    where $a = $definedoc//odm:ItemGroupDef[@Name='TS']/odm:ItemRef/@ItemOID
    return $a
)
(: get all the records that have TSPARMCD=CURTRT :)
for $record in $tsdatasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$tsparmcdoid and @Value='CURTRT']]
    let $recnum := $record/@data:ItemGroupDataSeq
    (: get the value of the TSVALCD variable :)
    let $tsvalcdvalue := $record/odm:ItemData[@ItemOID=$tsvalcdoid]/@Value
    (: invoke the NLM webservice to check whether this is a valid preferred term :)
    let $webservice := concat('https://dailymed.nlm.nih.gov/dailymed/services/v2/uniis.xml?unii_code=',$tsvalcdvalue)
    (: the webservice returns an XML document with the structure /uniis/unii - count the number of unii elements :)
    let $webserviceresult := doc($webservice)
    let $uniicount := count($webserviceresult/uniis/unii/unii_code)
    (: the preferred term is invalid when the count=0 :)
    where $uniicount=0
    return <error rule="SD2251" dataset="TS" variable="TSVALCD" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">Invalid TSVALCD value={data($tsvalcdvalue)} for CURTRT</error>
]]></rulexquery>
</sdsrule>

<sdsrule id="SD2241" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>TSVCDREF variable value must be 'UNII', when TSPARMCD='CURTRT'</ruledescription>
<ruledetaileddescription>TSVCDREF variable value must be 'UNII', when TSPARMCD='CURTRT'</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>TS</domain>
<rulexquery><![CDATA[
(: Rule SD2241 - Invalid TSVCDREF value for CURTRT:
TSVCDREF variable value must be 'UNII', when TSPARMCD='CURTRT'
:)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
declare namespace xs="http://www.w3.org/2001/XMLSchema";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external;
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: get the TS dataset :)
let $tsdataset := $definedoc//odm:ItemGroupDef[@Name='TS']
let $tsdatasetname := (
	if($defineversion = '2.1') then $tsdataset/def21:leaf/@xlink:href
	else $tsdataset/def:leaf/@xlink:href
)
let $tsdatasetdoc := (
	if($tsdatasetname) then doc(concat($base,$tsdatasetname))
	else ()
)
(: get the OID of the TSPARMCD variable :)
let $tsparmcdoid := (
    for $a in $definedoc//odm:ItemDef[@Name='TSPARMCD']/@OID 
    where $a = $definedoc//odm:ItemGroupDef[@Name='TS']/odm:ItemRef/@ItemOID
    return $a
)
(: and the OID of the TSVCDREF variable :)
let $tsvcdrefoid := (
    for $a in $definedoc//odm:ItemDef[@Name='TSVCDREF']/@OID 
    where $a = $definedoc//odm:ItemGroupDef[@Name='TS']/odm:ItemRef/@ItemOID
    return $a
)
(: iterate over all records that have TSPARMCD=CURTRT - essentially there should be only one(?) :)
for $record in $tsdatasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$tsparmcdoid and @Value='CURTRT']]
    let $recnum := $record/@data:ItemGroupDataSeq
    (: and get the value of TSVCDREF :)
    let $tsvcdrefvalue := $record/odm:ItemData[@ItemOID=$tsvcdrefoid]/@Value
    (: the value of TSVCDREF MUST be 'UNII' :)
    where not($tsvcdrefvalue='UNII')
    return <error rule="SD2241" dataset="TS" variable="TSVCDREF" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">Invalid TSVCDREF value={data($tsvcdrefvalue)} for CURTRT in dataset TS. The value must be 'UNII'</error>
]]></rulexquery>
</sdsrule>

<sdsrule id="SD2242" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>TSVCDREF value for TRT must be 'UNII'</ruledescription>
<ruledetaileddescription>TSVCDREF variable value must be 'UNII', when  TSPARMCD='TRT'</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>TS</domain>
<rulexquery><![CDATA[
(: Rule SD2242: Invalid TSVCDREF value for TRT: TSVCDREF variable value must be 'UNII', when  TSPARMCD='TRT' :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
declare namespace xs="http://www.w3.org/2001/XMLSchema";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdiscpilot01/' :)
(: let $define := 'define_2_0.xml' :)
let $definedoc := doc(concat($base,$define))
(: Get the TS dataset :)
let $tsitemgroupdef := $definedoc//odm:ItemGroupDef[@Name='TS']
let $tsdatasetname := (
	if($defineversion = '2.1') then $tsitemgroupdef/def21:leaf/@xlink:href
	else tsitemgroupdef/def:leaf/@xlink:href
)
let $tsdatasetdoc := (
	if($tsdatasetname) then doc(concat($base,$tsdatasetname))
	else ()
)
(: get the OID of the TSPARMCD variable :)
let $tsparmcdoid := (
    for $a in $definedoc//odm:ItemDef[@Name='TSPARMCD']/@OID 
    where $a = $definedoc//odm:ItemGroupDef[@Name='TS']/odm:ItemRef/@ItemOID
    return $a
)
(: get the OID of the TSVCDREF variable :)
let $tsvcdrefoid := (
    for $a in $definedoc//odm:ItemDef[@Name='TSVCDREF']/@OID 
    where $a = $definedoc//odm:ItemGroupDef[@Name='TS']/odm:ItemRef/@ItemOID
    return $a
)
(: Now iterate over all records in the TS dataset for which TSPARMCD='TRT' :)
for $record in $tsdatasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$tsparmcdoid and @Value='TRT']]
    let $recnum := $record/@data:ItemGroupDataSeq
    (: get the value of TSVCDREF :)
    let $tsvcdref := $record/odm:ItemData[@ItemOID=$tsvcdrefoid]/@Value
    (: The value must be UNII  :)
    where not($tsvcdref='UNII')
    (: TODO: what if TSVCDREF is absent? This currently also gives an error :)
    return  <error rule="SD2242" dataset="TS" variable="TSVCDREF" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">Invalid TSVCDREF value for TRT: TSVCDREF variable value must be 'UNII', when  TSPARMCD='TRT', value found is '{data($tsvcdref)}'</error>
]]></rulexquery>
</sdsrule>

<sdsrule id="SD2256" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>TSVCDREF value for COMPTRT must be 'UNII'</ruledescription>
<ruledetaileddescription>TSVCDREF variable value must be 'UNII', when TSPARMCD='COMPTRT'</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>TS</domain>
<rulexquery><![CDATA[
(: Rule SD2256 - Invalid TSVCDREF value for COMPTRT:
TSVCDREF variable value must be 'UNII', when TSPARMCD='COMPTRT'
:)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external;
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: get the TS dataset :)
let $tsdataset := $definedoc//odm:ItemGroupDef[@Name='TS']
let $tsdatasetname := (
	if($defineversion = '2.1') then $tsdataset/def21:leaf/@xlink:href
	else $tsdataset/def:leaf/@xlink:href
)
let $tsdatasetdoc := (
	if($tsdatasetname) then doc(concat($base,$tsdatasetname))
	else ()
)
(: get the OID of the TSPARMCD variable :)
let $tsparmcdoid := (
    for $a in $definedoc//odm:ItemDef[@Name='TSPARMCD']/@OID 
    where $a = $definedoc//odm:ItemGroupDef[@Name='TS']/odm:ItemRef/@ItemOID
    return $a
)
(: and the OID of the TSVCDREF variable :)
let $tsvcdrefoid := (
    for $a in $definedoc//odm:ItemDef[@Name='TSVCDREF']/@OID 
    where $a = $definedoc//odm:ItemGroupDef[@Name='TS']/odm:ItemRef/@ItemOID
    return $a
)
(: iterate over all records that have TSPARMCD=COMPTRT :)
for $record in $tsdatasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$tsparmcdoid and @Value='COMPTRT']]
    let $recnum := $record/@data:ItemGroupDataSeq
    (: and get the value of TSVCDREF :)
    let $tsvcdrefvalue := $record/odm:ItemData[@ItemOID=$tsvcdrefoid]/@Value
    (: the value of TSVCDREF MUST be 'UNII' :)
    where not($tsvcdrefvalue='UNII')
    return <error rule="SD2256" dataset="TS" variable="TSVCDREF" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">Invalid TSVCDREF value={data($tsvcdrefvalue)} for COMPTRT. The value must be 'UNII'</error>
]]></rulexquery>
</sdsrule>

<sdsrule id="SD2253" last-update="2020-08-08" originator="FDA" standard="SDTM" webservice="Uses NLM UNII Web Service">
<ruledescription>TSVAL value for COMPTRT must be from FDA-SRS</ruledescription>
<ruledetaileddescription>TSVAL for COMPTRT record must be a valid preferred term from FDA Substance Registration System (SRS)</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>TS</domain>
<rulexquery><![CDATA[	
(: Rule SD2253 - Invalid TSVAL value for COMPTRT:
TSVAL for COMPTRT record must be a valid preferred term from FDA Substance Registration System (SRS)
:)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
declare namespace xs="http://www.w3.org/2001/XMLSchema";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external;
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: get the TS dataset :)
let $tsdataset := $definedoc//odm:ItemGroupDef[@Name='TS']
let $tsdatasetname := (
	if($defineversion = '2.1') then $tsdataset/def21:leaf/@xlink:href
	else $tsdataset/def:leaf/@xlink:href
)
let $tsdatasetdoc := (
	if($tsdatasetname) then doc(concat($base,$tsdatasetname))
	else ()
)
(: get the OID of the TSPARMCD and TSVAL variables :)
let $tsparmcdoid := (
    for $a in $definedoc//odm:ItemDef[@Name='TSPARMCD']/@OID 
    where $a = $definedoc//odm:ItemGroupDef[@Name='TS']/odm:ItemRef/@ItemOID
    return $a
)
let $tsvaloid := (
    for $a in $definedoc//odm:ItemDef[@Name='TSVAL']/@OID 
    where $a = $definedoc//odm:ItemGroupDef[@Name='TS']/odm:ItemRef/@ItemOID
    return $a
)
(: get all the records that have TSPARMCD=COMPTRT :)
for $record in $tsdatasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$tsparmcdoid and @Value='COMPTRT']]
    let $recnum := $record/@data:ItemGroupDataSeq
    (: get the value of the TSVAL variable :)
    let $tsvalvalue := $record/odm:ItemData[@ItemOID=$tsvaloid]/@Value
    (: invoke the NLM webservice to check whether this is a valid preferred term :)
    let $webservice := concat('https://dailymed.nlm.nih.gov/dailymed/services/v2/uniis.xml?active_moeity=',$tsvalvalue)
    (: the webservice returns an XML document with the structure /uniis/unii - count the number of unii elements :)
    let $webserviceresult := doc($webservice)
    let $uniicount := count($webserviceresult/uniis/unii)
    (: the preferred term is invalid when the count=0 :)
    (: TODO: the web service also gives uniis even when the preferred term is invalid, e.g.:
    https://dailymed.nlm.nih.gov/dailymed/services/v2/uniis.xml?active_moeity=nothing
    TODO: ask the authors of the webservice
    :)
    where $uniicount=0
    return <error rule="SD2253" dataset="TS" variable="TSVAL" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">Invalid TSVAL value={data($tsvalvalue)} for COMPTRT in dataset TS</error>
]]></rulexquery>
</sdsrule>

<sdsrule id="SD2254" last-update="2020-08-08" originator="FDA" standard="SDTM" webservice="Uses NLM UNII Web Service">
<ruledescription>TSVALCD value for COMPTRT must be from FDA-SRS</ruledescription>
<ruledetaileddescription>TSVALCD for COMPTRT record must be a valid unique ingredient identifier from FDA Substance Registration System (SRS)</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>TS</domain>
<rulexquery><![CDATA[	
(: Rule SD2254 - Invalid TSVALCD value for COMPTRT:
TSVALCD for COMPTRT record must be a valid unique ingredient identifier from FDA Substance Registration System (SRS)
:)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
declare namespace xs="http://www.w3.org/2001/XMLSchema";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external;
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/'  :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: get the TS dataset :)
let $tsdataset := $definedoc//odm:ItemGroupDef[@Name='TS']
let $tsdatasetname := (
	if($defineversion = '2.1') then $tsdataset/def21:leaf/@xlink:href
	else $tsdataset/def:leaf/@xlink:href
)
let $tsdatasetdoc := (
	if($tsdatasetname) then doc(concat($base,$tsdatasetname))
	else ()
)
(: get the OID of the TSPARMCD and TSVALCD variables :)
let $tsparmcdoid := (
    for $a in $definedoc//odm:ItemDef[@Name='TSPARMCD']/@OID 
    where $a = $definedoc//odm:ItemGroupDef[@Name='TS']/odm:ItemRef/@ItemOID
    return $a
)
let $tsvalcdoid := (
    for $a in $definedoc//odm:ItemDef[@Name='TSVALCD']/@OID 
    where $a = $definedoc//odm:ItemGroupDef[@Name='TS']/odm:ItemRef/@ItemOID
    return $a
)
(: get all the records that have TSPARMCD=COMPTRT :)
for $record in $tsdatasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$tsparmcdoid and @Value='COMPTRT']]
    let $recnum := $record/@data:ItemGroupDataSeq
    (: get the value of the TSVAL variable :)
    let $tsvalcdvalues := $record/odm:ItemData[@ItemOID=$tsvalcdoid]/@Value
    (: there should be either 0 or 1 TSVALCD values,
    we don'T want to call the web service when there is no TSVALCD value:)
    for $tsvalcdvalue in $tsvalcdvalues
        (: invoke the NLM webservice to check whether this is a valid preferred term :)
        let $webservice := concat('https://dailymed.nlm.nih.gov/dailymed/services/v2/uniis.xml?unii_code=',$tsvalcdvalue)
        (: the webservice returns an XML document with the structure /uniis/unii - count the number of unii elements :)
        let $webserviceresult := doc($webservice)
        let $uniicount := count($webserviceresult/uniis/unii)
        (: count the number of active moeities for this code - when 0, the code is invalid :)
        where $uniicount=0
        return <error rule="SD2254" dataset="TS" variable="TSVALCD" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">Invalid TSVALCD value={data($tsvalcdvalue)} for COMPTRT in dataset TS</error>
]]></rulexquery>
</sdsrule>

<!-- Rule SD2257: TSVAL for INDIC must be a valid term from SNOMED CT
		TODO: is there a web service for SNOMED-CT? Yes: see http://rxnav.nlm.nih.gov/SnomedCTAPI.html, e.g. http://rxnav.nlm.nih.gov/REST/SnomedCT/status?id=10532003
 		However, it does not check on the SNOMED term, only on the SNOMED code. 
 		Other possibility: using RESTful web services from UMLS? Requires TGT.
-->

<!-- Rule SD2267: TSVAL for TDIGRP must be a valid term from SNOMED CT
		TODO: is there a web service for SNOMED-CT? Yes: see http://rxnav.nlm.nih.gov/SnomedCTAPI.html, e.g. http://rxnav.nlm.nih.gov/REST/SnomedCT/status?id=10532003
 		However, it does not check on the SNOMED term, only on the SNOMED code. 
 		Other possibility: using RESTful web services from UMLS? Requires TGT.
-->

<!-- Rule SD2269: TSVAL/TSVALCD value mismatch for TDIGRP
		TODO: is there a web service for SNOMED-CT? Yes: see http://rxnav.nlm.nih.gov/SnomedCTAPI.html, e.g. http://rxnav.nlm.nih.gov/REST/SnomedCT/status?id=10532003
 		However, it does not check on the SNOMED term, only on the SNOMED code. 
 		Other possibility: using RESTful web services from UMLS? Requires TGT.
-->

<sdsrule id="SD2258" last-update="2020-08-08" originator="FDA" standard="SDTM" webservice="Uses NLM RxNav Web Service">
<ruledescription>TSVALCD for INDIC record must be a valid concept id from SNOMED CT</ruledescription>
<ruledetaileddescription>TSVALCD for INDIC record must be a valid concept ID from SNOMED CT</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>TS</domain>
<rulexquery><![CDATA[
(: Rule SD2258 - Invalid TSVALCD value for INDIC:
TSVALCD for INDIC record must be a valid concept id from SNOMED CT
Remark: uses NLM Web Service: http://rxnav.nlm.nih.gov/SnomedCTAPI.html, e.g. http://rxnav.nlm.nih.gov/REST/SnomedCT/status?id=10532003
:)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
declare namespace xs="http://www.w3.org/2001/XMLSchema";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external;
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdiscpilot01/' :)
(: let $define := 'define_2_0.xml' :)
let $definedoc := doc(concat($base,$define))
(: get the TS dataset :)
let $tsdataset := $definedoc//odm:ItemGroupDef[@Name='TS']
let $tsdatasetname := (
	if($defineversion = '2.1') then $tsdataset/def21:leaf/@xlink:href
	else $tsdataset/def:leaf/@xlink:href
)
let $tsdatasetdoc := (
	if($tsdatasetname) then doc(concat($base,$tsdatasetname))
	else ()
)
(: get the OID of the TSPARMCD and TSVALCD variables :)
let $tsparmcdoid := (
    for $a in $definedoc//odm:ItemDef[@Name='TSPARMCD']/@OID 
    where $a = $definedoc//odm:ItemGroupDef[@Name='TS']/odm:ItemRef/@ItemOID
    return $a
)
let $tsvalcdoid := (
    for $a in $definedoc//odm:ItemDef[@Name='TSVALCD']/@OID 
    where $a = $definedoc//odm:ItemGroupDef[@Name='TS']/odm:ItemRef/@ItemOID
    return $a
)
(: get all the records that have TSPARMCD=INDIC :)
for $record in $tsdatasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$tsparmcdoid and @Value='INDIC']]
    let $recnum := $record/@data:ItemGroupDataSeq
    (: get the value of the TSVAL variable :)
    let $tsvalcdvalues := $record/odm:ItemData[@ItemOID=$tsvalcdoid]/@Value
    (: TESTING ONLY let $tsvalcdvalue := '12345678' :)
    (: There should be zero or one such value, but we don't want to call the web service when there is none :)
    for $tsvalcdvalue in $tsvalcdvalues
	    (: invoke the NLM webservice to check whether this is a valid preferred term :)
	    let $webservice := concat('https://rxnav.nlm.nih.gov/REST/SnomedCT/status?id=',$tsvalcdvalue)
	    (: the webservice returns an XML document with the structure /snomedctStatus/status - the value should be '1' - '-1' means an invalid satus :)
	    let $webserviceresult := doc($webservice)
	    let $status := $webserviceresult/snomedctStatus/status
	    where $status != 1
	    return <error rule="SD2258" dataset="TS" variable="TSVALCD" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">Invalid TSVALCD, value={data($tsvalcdvalue)} for INDIC is not a valid SNOMED-CT code in dataset TS</error>
]]></rulexquery>
</sdsrule>

<sdsrule id="SD2268" last-update="2020-08-08" originator="FDA" standard="SDTM" webservice="Uses NLM RxNav Web Service">
<ruledescription>TSVALCD for TDIGRP record must be a valid concept id from SNOMED CT</ruledescription>
<ruledetaileddescription>TSVALCD for TDIGRP record must be a valid concept ID from SNOMED CT</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>TS</domain>
<rulexquery><![CDATA[
(: Rule SD2268 - Invalid TSVALCD value for TDIGRP:
TSVALCD for TDIGRP record must be a valid concept id from SNOMED CT
Remark: uses NLM Web Service: http://rxnav.nlm.nih.gov/SnomedCTAPI.html, e.g. http://rxnav.nlm.nih.gov/REST/SnomedCT/status?id=10532003
:)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
declare namespace xs="http://www.w3.org/2001/XMLSchema";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external;
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdiscpilot01/' :)
(: let $define := 'define_2_0.xml' :)
let $definedoc := doc(concat($base,$define))
(: get the TS dataset :)
let $tsdataset := $definedoc//odm:ItemGroupDef[@Name='TS']
let $tsdatasetname := (
	if($defineversion = '2.1') then $tsdataset/def21:leaf/@xlink:href
	else $tsdataset/def:leaf/@xlink:href
)
let $tsdatasetdoc := (
	if($tsdatasetname) then doc(concat($base,$tsdatasetname))
	else ()
)
(: get the OID of the TSPARMCD and TSVALCD variables :)
let $tsparmcdoid := (
    for $a in $definedoc//odm:ItemDef[@Name='TSPARMCD']/@OID 
    where $a = $definedoc//odm:ItemGroupDef[@Name='TS']/odm:ItemRef/@ItemOID
    return $a
)
let $tsvalcdoid := (
    for $a in $definedoc//odm:ItemDef[@Name='TSVALCD']/@OID 
    where $a = $definedoc//odm:ItemGroupDef[@Name='TS']/odm:ItemRef/@ItemOID
    return $a
)
(: get all the records that have TSPARMCD=TDIGRP :)
for $record in $tsdatasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$tsparmcdoid and @Value='TDIGRP']]
    let $recnum := $record/@data:ItemGroupDataSeq
    (: get the value of the TSVAL variable :)
    let $tsvalcdvalues := $record/odm:ItemData[@ItemOID=$tsvalcdoid]/@Value
    (: TESTING ONLY let $tsvalcdvalue := '12345678' :)
    (: There should be zero or one such value, but we don't want to call the web service when there is none :)
    for $tsvalcdvalue in $tsvalcdvalues
	    (: invoke the NLM webservice to check whether this is a valid preferred term :)
	    let $webservice := concat('https://rxnav.nlm.nih.gov/REST/SnomedCT/status?id=',$tsvalcdvalue)
	    (: the webservice returns an XML document with the structure /snomedctStatus/status - the value should be '1' - '-1' means an invalid satus :)
	    let $webserviceresult := doc($webservice)
	    let $status := $webserviceresult/snomedctStatus/status
	    where $status != 1
	    return <error rule="SD2268" dataset="TS" variable="TSVALCD" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">Invalid TSVALCD, value={data($tsvalcdvalue)} for TDIGRP is not a valid SNOMED-CT code in dataset TS</error>
]]></rulexquery>
</sdsrule>

<sdsrule id="SD2260" last-update="2020-08-08" originator="FDA" standard="SDTM" webservice="Uses NLM UNII Web Service">
<ruledescription>TSVAL value for TRT must be from FDA-SRS</ruledescription>
<ruledetaileddescription>TSVAL for TRT record must be a valid preferred term from FDA Substance Registration System (SRS)</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>TS</domain>
<rulexquery><![CDATA[	
(: Rule SD2260: Invalid TSVALCD value for TRT: TSVALCD for TRT record must be a valid preferred term  from FDA Substance Registration System (SRS) :)
(: This query uses the DailyMed NLM web service - see https://dailymed.nlm.nih.gov/dailymed/webservices-help/v2/uniis_api.cfm
 e.g.: http://dailymed.nlm.nih.gov/dailymed/services/v2/uniis.xml?active_moiety=NAFARELIN :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
declare namespace xs="http://www.w3.org/2001/XMLSchema";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external;
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdiscpilot01/' :)
(: let $define := 'define_2_0.xml'  :)
let $definedoc := doc(concat($base,$define))
(: Get the TS dataset :)
let $tsitemgroupdef := $definedoc//odm:ItemGroupDef[@Name='TS']
let $tsdatasetname := (
	if($defineversion = '2.1') then $tsitemgroupdef/def21:leaf/@xlink:href
	else $tsitemgroupdef/def:leaf/@xlink:href
)
let $tsdatasetdoc := (
	if($tsdatasetname) then doc(concat($base,$tsdatasetname))
	else ()
)
(: get the OID of the TSPARMCD variable :)
let $tsparmcdoid := (
    for $a in $definedoc//odm:ItemDef[@Name='TSPARMCD']/@OID 
    where $a = $definedoc//odm:ItemGroupDef[@Name='TS']/odm:ItemRef/@ItemOID
    return $a
)
(: get the OID of the TSVAL variable :)
let $tsvaloid := (
    for $a in $definedoc//odm:ItemDef[@Name='TSVAL']/@OID 
    where $a = $definedoc//odm:ItemGroupDef[@Name='TS']/odm:ItemRef/@ItemOID
    return $a
)
(: now get the records TSPARMCD=TRT, but only when there also IS a TSVALCD variable value present :)
for $record in $tsdatasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$tsparmcdoid and @Value='TRT'] and odm:ItemData[@ItemOID=$tsvaloid]]
    let $recnum := $record/@data:ItemGroupDataSeq
    (: get the ItemData point for TSVAL :)
    (: REMARK that we are forgiving regarding case-sensitiveness :)
    let $tsval := upper-case($record/odm:ItemData[@ItemOID=$tsvaloid]/@Value)
    (: now invoke the web service to check whether it is a valid UNII, e.g.:
    https://dailymed.nlm.nih.gov/dailymed/services/v2/uniis.xml?active_moiety=NAFARELIN
    :)
    (: ONLY FOR TESTING :)
    (: let $tsval := 'NAFARELINx' :)
    let $webservice := concat('https://dailymed.nlm.nih.gov/dailymed/services/v2/uniis.xml?active_moiety=',$tsval)
    let $webserviceresult := doc($webservice) 
    (: this is a document with the structure /uniis/unii/unii_code - it is an invalid code when there is no such an unii element :)
    let $count := count($webserviceresult/uniis/unii/unii_code)
    where $count = 0
    return <error rule="SD2260" dataset="TS" variable="TSVAL" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">Invalid TSVAL value for TRT in dataset {data($tsdatasetname)}: TSVAL for TRT record must be a valid preferred term from FDA Substance Registration System (SRS): value '{data($tsval)}' is not a valid preferred term identifier from SRS</error> 
]]></rulexquery>
</sdsrule>

<sdsrule id="SD2261" last-update="2020-08-08" originator="FDA" standard="SDTM" webservice="Uses NLM UNII Web Service">
<ruledescription>TSVALCD value for TRT must be from FDA-SRS</ruledescription>
<ruledetaileddescription>TSVALCD for TRT record must be a valid unique ingredient identifier from FDA Substance Registration System (SRS)</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>TS</domain>
<rulexquery><![CDATA[	
(: Rule SD2261: Invalid TSVALCD value for TRT: TSVALCD for TRT record must be a valid unique ingredient identifier from FDA Substance Registration System (SRS) :)
(: This query uses the DailyMed NLM web service - see https://dailymed.nlm.nih.gov/dailymed/webservices-help/v2/uniis_api.cfm
 e.g.: https://dailymed.nlm.nih.gov/dailymed/services/v2/uniis.xml?unii_code=1X0094V6JV :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
declare namespace xs="http://www.w3.org/2001/XMLSchema";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external;
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdiscpilot01/' :)
(: let $define := 'define_2_0.xml' :)
let $definedoc := doc(concat($base,$define))
(: Get the TS dataset :)
let $tsitemgroupdef := $definedoc//odm:ItemGroupDef[@Name='TS']
let $tsdatasetname := (
	if($defineversion = '2.1') then $tsitemgroupdef/def21:leaf/@xlink:href
	else $tsitemgroupdef/def:leaf/@xlink:href
)
let $tsdatasetdoc := (
	if($tsdatasetname) then doc(concat($base,$tsdatasetname))
	else ()
)
(: get the OID of the TSPARMCD variable :)
let $tsparmcdoid := (
    for $a in $definedoc//odm:ItemDef[@Name='TSPARMCD']/@OID 
    where $a = $definedoc//odm:ItemGroupDef[@Name='TS']/odm:ItemRef/@ItemOID
    return $a
)
(: get the OID of the TSVALCD variable :)
let $tsvalcdoid := (
    for $a in $definedoc//odm:ItemDef[@Name='TSVALCD']/@OID 
    where $a = $definedoc//odm:ItemGroupDef[@Name='TS']/odm:ItemRef/@ItemOID
    return $a
)
(: now get the records TSPARMCD=TRT, but only when there also IS a TSVALCD variable value present :)
for $record in $tsdatasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$tsparmcdoid and @Value='TRT'] and odm:ItemData[@ItemOID=$tsvalcdoid]]
    let $recnum := $record/@data:ItemGroupDataSeq
    (: get the ItemData point for TSVAL :)
    let $tsval := $record/odm:ItemData[@ItemOID=$tsvalcdoid]/@Value
    (: now invoke the web service to check whether it is a valid UNII, e.g.:
    https://dailymed.nlm.nih.gov/dailymed/services/v2/uniis.xml?unii_code=1X0094V6JV
    :)
    (: ONLY FOR TESTING let $tsval := '1X0094V6JVx' :)
    let $webservice := concat('https://dailymed.nlm.nih.gov/dailymed/services/v2/uniis.xml?unii_code=',$tsval)
    let $webserviceresult := doc($webservice) 
    (: this is a document with the structure /uniis/unii/unii_code - it is an invalid code when there is no such an unii element :)
    let $count := count($webserviceresult/uniis/unii/unii_code)
    where $count = 0
    return <error rule="SD2261" dataset="TS" variable="TSVALCD" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">Invalid TSVALCD value for TRT in dataset {data($tsdatasetname)}: TSVALCD for TRT record must be a valid unique ingredient identifier from FDA Substance Registration System (SRS): value '{data($tsval)}' is not a valid unique ingredient identifier from SRS</error>
]]></rulexquery>
</sdsrule>

<sdsrule id="SD2263" last-update="2020-08-08" originator="FDA" standard="SDTM" webservice="Uses NLM NDF-RT Web Service">
<ruledescription>TSVAL value for PCLAS must be from NDF-RT</ruledescription>
<ruledetaileddescription>TSVAL for PCLAS record must be a valid term from NDF-RT</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>TS</domain>
<rulexquery><![CDATA[
(: Rule SD2263 - Invalid TSVAL value for PCLAS - TSVAL for PCLAS record must be a valid term from NDF-RT :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
declare namespace xs="http://www.w3.org/2001/XMLSchema";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external;
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdiscpilot01/' :)
(: let $define := 'define_2_0.xml' :)
let $definedoc := doc(concat($base,$define))
(: Get the TS dataset :)
let $tsitemgroupdef := $definedoc//odm:ItemGroupDef[@Name='TS']
let $tsdatasetname := (
	if($defineversion = '2.1') then $tsitemgroupdef/def21:leaf/@xlink:href
	else $tsitemgroupdef/def:leaf/@xlink:href
)
let $tsdatasetdoc := (
	if($tsdatasetname) then doc(concat($base,$tsdatasetname))
	else ()
)
(: get the OID of the TSPARMCD variable :)
let $tsparmcdoid := (
    for $a in $definedoc//odm:ItemDef[@Name='TSPARMCD']/@OID 
    where $a = $definedoc//odm:ItemGroupDef[@Name='TS']/odm:ItemRef/@ItemOID
    return $a
)
(: get the OID of the TSVAL variable :)
let $tsvaloid := (
    for $a in $definedoc//odm:ItemDef[@Name='TSVAL']/@OID 
    where $a = $definedoc//odm:ItemGroupDef[@Name='TS']/odm:ItemRef/@ItemOID
    return $a
)
(: now get the records TSPARMCD=PCLAS, but only when there also IS a TSVAL variable value present :)
for $record in $tsdatasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$tsparmcdoid and @Value='PCLAS']][odm:ItemData[@ItemOID=$tsvaloid]]
    let $recnum := $record/@data:ItemGroupDataSeq
    (: get the ItemData point for TSVAL :)
    let $tsval := $record/odm:ItemData[@ItemOID=$tsvaloid]/@Value
    (: now invoke the web service to check whether it is a valid NDF-RT, e.g.:
    https://rxnav.nlm.nih.gov/REST/Ndfrt/search?conceptName=Anti-epileptic%20Agent
    :)
    let $webservice := concat('https://rxnav.nlm.nih.gov/REST/Ndfrt/search?conceptName=',$tsval)
    let $webserviceresult := doc($webservice) 
    (: this is a document with the structure /ndfrtdata/groupConcepts/concept/conceptName - it is an invalid code when there is no such an conceptName element :)
    let $count := count($webserviceresult/ndfrtdata/groupConcepts/concept[conceptName=$tsval])
    where $count = 0
    return <error rule="SD2263" dataset="TS" variable="TSVALCD" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">Invalid TSVAL value for PCLASS in dataset {data($tsdatasetname)}: TSVAL for TSPARMCD=PCLAS record must be a valid a valid term from NDF-RT: value '{data($tsval)}' was found</error> 
]]></rulexquery>
</sdsrule>

<sdsrule id="SD2264" last-update="2020-08-08" originator="FDA" standard="SDTM" webservice="Uses NLM NDF-RT Web Service">
<ruledescription>TSVALCD value for PCLAS must be from NDF-RT - TSVALCD for PCLAS record must be a valid code from NDF-RT</ruledescription>
<ruledetaileddescription>TSVALCD for PCLAS record must be a valid code from NDF-RT</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>TS</domain>
<rulexquery><![CDATA[
(: Rule SD2264 - Invalid TSVALCD value for PCLAS:
TSVALCD for PCLAS record must be a valid code from NDF-RT
:)
(: uses NLM webservice - see http://rxnav.nlm.nih.gov/NdfrtAPIREST.html
E.g. http://rxnav.nlm.nih.gov/REST/Ndfrt/parentConcepts.xml?nui=N0000153235 :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: get the TS dataset :)
let $tsdataset := $definedoc//odm:ItemGroupDef[@Name='TS']
let $tsdatasetname := (
	if($defineversion = '2.1') then $tsdataset/def21:leaf/@xlink:href
	else $tsdataset/def:leaf/@xlink:href
)
let $tsdatasetdoc := (
	if($tsdatasetname) then doc(concat($base,$tsdatasetname))
	else ()
)
(: get the OID of the TSPARMCD and TSVALCD :)
let $tsparmcdoid := (
    for $a in $definedoc//odm:ItemDef[@Name='TSPARMCD']/@OID 
    where $a = $definedoc//odm:ItemGroupDef[@Name='TS']/odm:ItemRef/@ItemOID
    return $a
)
let $tsvalcdoid := (
    for $a in $definedoc//odm:ItemDef[@Name='TSVALCD']/@OID 
    where $a = $definedoc//odm:ItemGroupDef[@Name='TS']/odm:ItemRef/@ItemOID
    return $a
)
(: get the records with TSPARMCD=PCLAS :)
for $pclassrecord in $tsdatasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$tsparmcdoid and @Value='PCLAS']]
    let $recnum := $pclassrecord/@data:ItemGroupDataSeq
    (: and get the value of TSVALCD :)
    let $tsvalcdvalues := $pclassrecord/odm:ItemData[@ItemOID=$tsvalcdoid]/@Value
    (: just for testing: let $tsvalcdvalue := 'N0000175565' :)
    (: There should be 0 or 1 TSVALCD values, but we don't want to call the web service when there is none :)
    for $tsvalcdvalue in $tsvalcdvalues
	    (: check it using the NLM webservice :)
	    let $webserviceresult := doc(concat('http://rxnav.nlm.nih.gov/REST/Ndfrt/parentConcepts.xml?nui=',$tsvalcdvalue))
	    (: This rerturns an XML document with the structure /ndfrtdata/groupConcepts/concept
	    with child elements conceptName, conceptNui, conceptKind 
	    If the code is invalid, no "concept" element will be present :)
	    where not($webserviceresult/ndfrtdata/groupConcepts/concept)
	    return <error rule="SD2264" dataset="TS" variable="TSVALCD" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">Invalid TSVALCD value={data($tsvalcdvalue)} for PCLAS</error>
]]></rulexquery>
</sdsrule>

<sdsrule id="SD2243" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>TSVCDREF value for PCLAS must be 'NDF-RT'</ruledescription>
<ruledetaileddescription>TSVCDREF variable value must be 'NDF-RT', when TSPARMCD='PCLAS'</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>TS</domain>
<rulexquery><![CDATA[
(: Rule SD2243 - Invalid TSVCDREF value for PCLAS:
TSVCDREF variable value must be 'NDF-RT', when TSPARMCD='PCLAS'
:)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: get the TS dataset :)
let $tsdataset := $definedoc//odm:ItemGroupDef[@Name='TS']
let $tsdatasetname := (
	if($defineversion = '2.1') then $tsdataset/def21:leaf/@xlink:href
	else $tsdataset/def:leaf/@xlink:href
)
let $tsdatasetdoc := (
	if($tsdatasetname) then doc(concat($base,$tsdatasetname))
	else ()
)
(: get the OID of the TSPARMCD variable :)
let $tsparmcdoid := (
    for $a in $definedoc//odm:ItemDef[@Name='TSPARMCD']/@OID 
    where $a = $definedoc//odm:ItemGroupDef[@Name='TS']/odm:ItemRef/@ItemOID
    return $a
)
(: and the OID of the TSVCDREF variable :)
let $tsvcdrefoid := (
    for $a in $definedoc//odm:ItemDef[@Name='TSVCDREF']/@OID 
    where $a = $definedoc//odm:ItemGroupDef[@Name='TS']/odm:ItemRef/@ItemOID
    return $a
)
(: iterate over all records that have TSPARMCD=PCLAS :)
for $record in $tsdatasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$tsparmcdoid and @Value='PCLAS']]
    let $recnum := $record/@data:ItemGroupDataSeq
    (: and get the value of TSVCDREF :)
    let $tsvcdrefvalue := $record/odm:ItemData[@ItemOID=$tsvcdrefoid]/@Value
    (: the value of TSVCDREF MUST be 'NDF-RT' :)
    where not($tsvcdrefvalue='NDF-RT')
    return <error rule="SD2243" dataset="TS" variable="TSVCDREF" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">Invalid TSVCDREF value={data($tsvcdrefvalue)} for PCLAS. The value must be 'NDF-RT'</error>
]]></rulexquery>
</sdsrule>

<!-- TODO: Rule SD1323: TSVAL for FCNTRY record must be a valid term from ISO 3166
see similar rule for COUNTRY in DM -->


<sdsrule id="SD2244" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>TSVCDREF variable value must be "ISO 3166", when TSPARMCD='FCNTRY'</ruledescription>
<ruledetaileddescription>TSVCDREF variable value must be "ISO 3166", when TSPARMCD='FCNTRY'</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>TS</domain>
<rulexquery><![CDATA[
(: Rule SD2244 - Invalid TSVCDREF value for FCNTRY
TSVCDREF variable value must be "ISO 3166", when TSPARMCD='FCNTRY'
:)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: get the TS dataset :)
let $tsdataset := $definedoc//odm:ItemGroupDef[@Name='TS']
let $tsdatasetname := (
	if($defineversion = '2.1') then $tsdataset/def21:leaf/@xlink:href
	else $tsdataset/def:leaf/@xlink:href
)
let $tsdatasetdoc := (
	if($tsdatasetname) then doc(concat($base,$tsdatasetname))
	else ()
)
(: get the OID of the TSPARMCD variable :)
let $tsparmcdoid := (
    for $a in $definedoc//odm:ItemDef[@Name='TSPARMCD']/@OID 
    where $a = $definedoc//odm:ItemGroupDef[@Name='TS']/odm:ItemRef/@ItemOID
    return $a
)
(: and the OID of the TSVCDREF variable :)
let $tsvcdrefoid := (
    for $a in $definedoc//odm:ItemDef[@Name='TSVCDREF']/@OID 
    where $a = $definedoc//odm:ItemGroupDef[@Name='TS']/odm:ItemRef/@ItemOID
    return $a
)
(: iterate over all records that have TSPARMCD=FCNTRY :)
for $record in $tsdatasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$tsparmcdoid and @Value='FCNTRY']]
    let $recnum := $record/@data:ItemGroupDataSeq
    (: and get the value of TSVCDREF :)
    let $tsvcdrefvalue := $record/odm:ItemData[@ItemOID=$tsvcdrefoid]/@Value
    (: the value of TSVCDREF MUST be 'ISO 3166' :)
    where not($tsvcdrefvalue='ISO 3166')
    return <error rule="SD2244" dataset="TS" variable="TSVCDREF" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">Invalid TSVCDREF value={data($tsvcdrefvalue)} for FCNTRY. The value must be 'ISO 3166'</error>
]]></rulexquery>
</sdsrule>


<sdsrule id="SD2249" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>TSVAL value for ACTSUB must be numeric</ruledescription>
<ruledetaileddescription>TSVAL variable value must be numeric, when TSPARMCD='ACTSUB'</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>TS</domain>
<rulexquery><![CDATA[
(: Rule SD2249 - Invalid TSVAL value for ACTSUB:
TSVAL variable value must be numeric, when TSPARMCD='ACTSUB' :)
(: we suppose there cannot be 'fractional' subjects, so we implement as 'integer' :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: get the TS dataset :)
let $tsdataset := $definedoc//odm:ItemGroupDef[@Name='TS']
let $tsdatasetname := (
	if($defineversion = '2.1') then $tsdataset/def21:leaf/@xlink:href
	else $tsdataset/def:leaf/@xlink:href
)
let $tsdatasetdoc := (
	if($tsdatasetname) then doc(concat($base,$tsdatasetname))
	else ()
)
(: get the OID of the TSPARMCD and TSVAL :)
let $tsparmcdoid := (
    for $a in $definedoc//odm:ItemDef[@Name='TSPARMCD']/@OID 
    where $a = $definedoc//odm:ItemGroupDef[@Name='TS']/odm:ItemRef/@ItemOID
    return $a
)
let $tsvaloid := (
    for $a in $definedoc//odm:ItemDef[@Name='TSVAL']/@OID 
    where $a = $definedoc//odm:ItemGroupDef[@Name='TS']/odm:ItemRef/@ItemOID
    return $a
)
(: get the record with TSPARMCD=ACTSUB and get the value :)
let $actsubrecord := $tsdatasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$tsparmcdoid and @Value='ACTSUB']]
let $recnum := $actsubrecord/@data:ItemGroupDataSeq
(: and get the value for TSVAL :)
let $actsubval := $actsubrecord/odm:ItemData[@ItemOID=$tsvaloid]/@Value
(: and get whether it is an integer - we do NOT allow fractions of subjects :)
where $actsubrecord and not($actsubval castable as xs:integer)
return <error rule="SD2249" dataset="TS" variable="TSVAL" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">Invalid TSVAL value {data($actsubval)} for Trial Summary Parameter ACTSUB in dataset {data($tsdatasetname)}</error>
]]></rulexquery>
</sdsrule>

<sdsrule id="SD2266" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>TSVCDREF variable value must be "SNOMED", when TSPARAMCD='INDIC'</ruledescription>
<ruledetaileddescription>TSVCDREF variable value must be "SNOMED", when TSPARAMCD='INDIC'</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>TS</domain>
<rulexquery><![CDATA[
(: Rule SD2266 - Invalid TSVCDREF value for INDIC
TSVCDREF variable value must be "SNOMED", when TSPARAMCD='INDIC'
:)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external;
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: get the TS dataset :)
let $tsdataset := $definedoc//odm:ItemGroupDef[@Name='TS']
let $tsdatasetname := (
	if($defineversion = '2.1') then $tsdataset/def21:leaf/@xlink:href
	else $tsdataset/def:leaf/@xlink:href
)
let $tsdatasetdoc := (
	if($tsdatasetname) then doc(concat($base,$tsdatasetname))
	else ()
)
(: get the OID of the TSPARMCD variable :)
let $tsparmcdoid := (
    for $a in $definedoc//odm:ItemDef[@Name='TSPARMCD']/@OID 
    where $a = $definedoc//odm:ItemGroupDef[@Name='TS']/odm:ItemRef/@ItemOID
    return $a
)
(: and the OID of the TSVCDREF variable :)
let $tsvcdrefoid := (
    for $a in $definedoc//odm:ItemDef[@Name='TSVCDREF']/@OID 
    where $a = $definedoc//odm:ItemGroupDef[@Name='TS']/odm:ItemRef/@ItemOID
    return $a
)
(: iterate over all records that have TSPARMCD=INDIC :)
for $record in $tsdatasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$tsparmcdoid and @Value='INDIC']]
    let $recnum := $record/@data:ItemGroupDataSeq
    (: and get the value of TSVCDREF :)
    let $tsvcdrefvalue := $record/odm:ItemData[@ItemOID=$tsvcdrefoid]/@Value
    (: the value of TSVCDREF MUST be 'SNOMED' :)
    where not($tsvcdrefvalue='SNOMED')
    return <error rule="SD2266" dataset="TS" variable="TSVCDREF" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">Invalid TSVCDREF value={data($tsvcdrefvalue)} for INDIC. The value must be 'SNOMED'</error>
]]></rulexquery>
</sdsrule>

<!-- SD1279 TODO: testing
Only applies to SDTM-IG 3.2 -->
<sdsrule originator="FDA" id="SD1279" standard="SDTM" last-update="2020-08-08" >
	<ruledescription>ECDOSTXT may not be null when ECDOSE is null and ECOCCUR does not equal 'N'</ruledescription>
	<ruledetaileddescription>Dose Description (ECDOSTXT) should be populated when Dose (ECDOSE) is null (and ECOCCUR does not equal 'N')</ruledetaileddescription>
	<igversion>3.2</igversion>
	<domain>EC</domain>
	<rulexquery><![CDATA[
(: Rule SD1279 - ECDOSTXT may not be null when ECDOSE is null and ECOCCUR does not equal 'N' :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' 
let $define := 'define2-0-0-example-sdtm.xml'  :)
let $definedoc := doc(concat($base,$define))
(: get the EC dataset :)
let $ecdataset := $definedoc//odm:ItemGroupDef[@Name='EC']
let $ecdatasetname := (
	if($defineversion = '2.1') then $ecdataset/def21:leaf/@xlink:href
	else $ecdataset/def:leaf/@xlink:href
)
let $ecdatasetdoc := (
	if($ecdatasetname) then doc(concat($base,$ecdatasetname))
	else ()
)
(: get the OID of ECDOSE, ECOCCUR and ECDOSTXT :)
let $ecdoseoid := (
    for $a in $definedoc//odm:ItemDef[@Name='ECDOSE']/@OID 
    where $a = $definedoc//odm:ItemGroupDef[@Domain='EC' or @Name='EC']/odm:ItemRef/@ItemOID
    return $a
)
let $ecoccuroid := (
    for $a in $definedoc//odm:ItemDef[@Name='ECOCCUR']/@OID 
    where $a = $definedoc//odm:ItemGroupDef[@Domain='EC' or @Name='EC']/odm:ItemRef/@ItemOID
    return $a
)
let $ecdostxtoid := (
    for $a in $definedoc//odm:ItemDef[@Name='ECDOSTXT']/@OID 
    where $a = $definedoc//odm:ItemGroupDef[@Domain='EC' or @Name='EC']/odm:ItemRef/@ItemOID
    return $a
)
(: iterate over all the records in the EC for which ECDOSE = null and ECOCCUR != 'N' :)
for $record in $ecdatasetdoc//odm:ItemGroupData[not(odm:ItemData[@ItemOID=$ecdoseoid]) and not(odm:ItemData[@ItemOID=$ecoccuroid]/@Value = 'N')]
    let $recnum := $record/@data:ItemGroupDataSeq
    (: get the value of ECDOSTXT :)
    let $ecdostxt := $record/odm:ItemData[@ItemOID=$ecdostxtoid]/@Value
    (: ECDOSTXT may not be null :)
    where not($ecdostxt) or string-length($ecdostxt)=0
    return <error rule="SD1279" dataset="EC" variable="ECDOSTXT" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">ECDOSTXT must be populated when ECDOSE=null</error>							
]]>
	</rulexquery>
</sdsrule>
	
<sdsrule id="SD0056" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>SDTM Required variables must always be included</ruledescription>
<ruledetaileddescription>Variables described in SDTM as Required must be included in the dataset</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>ALL</domain>
<rulexquery><![CDATA[
(: Rule SD0056: SDTM Required variable must always be included - Variables described in SDTM as Required must be included in the dataset :)
(: The following Query relies on that the define.xml is complete and 
 that Mandatory='Yes' is set for each required variable :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
declare namespace request="http://exist-db.org/xquery/request";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external;
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: iterate over all datasets mentioned in the define.xml :)
for $itemgroup in $definedoc//odm:ItemGroupDef
    (: get all the ItemRef-OIDs which have 'Mandatory="Yes" :)
    let $mandatory := $itemgroup/odm:ItemRef[@Mandatory='Yes']/@ItemOID
    (: get the dataset itself :)
    let $datasetfilename := (
		if($defineversion = '2.1') then $itemgroup/def21:leaf/@xlink:href
		else $itemgroup/def:leaf/@xlink:href
	)
    let $datasetdoc := (
		if($datasetfilename) then doc(concat($base,$datasetfilename))
		else ()
	)
    let $datasetname := $itemgroup/@Name
    (: iterate over all the records :)
    for $record in $datasetdoc//odm:ItemGroupData
    	let $recnum := $record/@data:ItemGroupDataSeq
    	(: iterate over the 'mandatory' OIDs  :)
    	for $m in $mandatory
        	(: and give an error when there is no such ItemData/@ItemOID  :)
        	let $varname := $definedoc//odm:ItemDef[@OID=$m]/@Name
        	where not($record/odm:ItemData[@ItemOID=$m])
        	return <error rule="SD0056" datasetname="{$datasetname}" variable="{data($varname)}" rulelastupdate="2020-08-08" recordnumber="{$recnum}">No data found for required variable {data($varname)} in record number {data($recnum)} in dataset {data($datasetname)}</error>
]]></rulexquery>
</sdsrule>

<sdsrule id="SD0056-SD" last-update="2020-08-08" originator="FDA" requiresDomainOrDataset="Yes" standard="SDTM">
<ruledescription>SDTM Required variables must always be included</ruledescription>
<ruledetaileddescription>Variables described in SDTM as Required must be included in the dataset</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>ALL</domain>
<rulexquery><![CDATA[
(: Rule SD0056-FDAC018: SDTM Required variable not found - Variables described in SDTM as Required must be included in the dataset :)
(: The following Query relies on that the define.xml is complete and 
 that Mandatory='Yes' is set for each required variable :)
(: TODO: base "required" on information from the CDISC Library API through a RESTful web service :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
declare namespace request="http://exist-db.org/xquery/request";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external;
declare variable $defineversion external;
declare variable $datasetname external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: iterate over all datasets mentioned in the define.xml and passed by the calling program through external variable $domain :)
for $itemgroup in $definedoc//odm:ItemGroupDef[@Name=$datasetname]
    (: get all the ItemRef-OIDs which have 'Mandatory="Yes" :)
    let $mandatory := $itemgroup/odm:ItemRef[@Mandatory='Yes']/@ItemOID
    (: get the dataset itself :)
	let $datasetfilename := (
		if($defineversion = '2.1') then $itemgroup/def21:leaf/@xlink:href
		else $itemgroup/def:leaf/@xlink:href
	)
    let $datasetdoc := (
		if($datasetfilename) then doc(concat($base,$datasetfilename))
		else ()
	)
    let $name := $itemgroup/@Name
    (: iterate over all the records :)
    for $record in $datasetdoc//odm:ItemGroupData
    	let $recnum := $record/@data:ItemGroupDataSeq
    	(: iterate over the 'mandatory' OIDs  :)
    	for $m in $mandatory
        	(: and give an error when there is no such ItemData/@ItemOID  :)
        	let $varname := $definedoc//odm:ItemDef[@OID=$m]/@Name
        	where not($record/odm:ItemData[@ItemOID=$m])
        	return <error rule="SD0056" datasetname="{$name}" variable="{data($varname)}" rulelastupdate="2020-08-08" recordnumber="{$recnum}">No data found for required variable {data($varname)} in record number {data($recnum)} in dataset {data($datasetname)}</error>
]]></rulexquery>
</sdsrule>


<sdsrule id="SD0033" last-update="2020-08-08" originator="FDA" standard="SDTM">
<ruledescription>Value for --TPTNUM must be provided, when value for --TPT is provided</ruledescription>
<ruledetaileddescription>Planned Time Point Number (--TPTNUM) variable must be included into the domain, when Planned Time Point Name (--TPT) variable is present</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>ALL</domain>
<rulexquery><![CDATA[	
(: Rule SD0033 - Missing value for --TPTNUM, when --TPT is provided:
Planned Time Point Number (--TPTNUM) variable must be included into the domain, when Planned Time Point Name (--TPT) variable is present
:)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/'  :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: iterate over all the datasets :)
for $dataset in $definedoc//odm:ItemGroupDef
    let $name := $dataset/@Name 
	let $datasetname := (
		if($defineversion = '2.1') then $dataset/def21:leaf/@xlink:href
		else $dataset/def:leaf/@xlink:href
	)
    let $datasetdoc := (
		if($datasetname) then doc(concat($base,$datasetname))
		else ()
	)
    (: Get the OIDs of TPT and TPTNUM (if any) :)
    let $tptoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'TPT')]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
    )
    let $tptname := $definedoc//odm:ItemDef[@OID=$tptoid]/@Name
    (: Get the OIDs of TPT and TPTNUM (if any) :)
    let $tptnumoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'TPTNUM')]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
    )
    let $tptnumname := $definedoc//odm:ItemDef[@OID=$tptnumoid]/@Name
    (: iterate over the records in the dataset that do have a TPT  :)
    for $record in $datasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$tptoid]]
        let $recnum := $record/@data:ItemGroupDataSeq 
		(: Get the -TPT value :)
        let $tptvalue := $record/odm:ItemData[@ItemOID=$tptoid]/@Value
		(: Report when --TPTNUM is absent :)
        where not($record/odm:ItemData[@ItemOID=$tptnumoid])
        return <warning rule="SD0033" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">Planned Time Point Number {data($tptnumname)} may not be NULL, when Planned Time Point Name {data($tptname)} is populated, value = {data($tptvalue)}</warning>
]]></rulexquery>
</sdsrule>

<sdsrule id="SD0033-SD" last-update="2020-08-08" originator="FDA" standard="SDTM" requiresDomainOrDataset="Yes">
<ruledescription>Value for --TPTNUM must be provided, when value for --TPT is provided</ruledescription>
<ruledetaileddescription>Planned Time Point Number (--TPTNUM) variable must be included into the domain, when Planned Time Point Name (--TPT) variable is present</ruledetaileddescription>
<igversion>3.1.2</igversion>
<igversion>3.1.3</igversion>
<igversion>3.2</igversion>
<domain>ALL</domain>
<rulexquery><![CDATA[	
(: Rule SD0033 - Missing value for --TPTNUM, when --TPT is provided:
Planned Time Point Number (--TPTNUM) variable must be included into the domain, when Planned Time Point Name (--TPT) variable is present
:)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
declare variable $datasetname external;
(: let $base := '/db/fda_submissions/cdisc01/'  :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: iterate over all the datasets :)
for $dataset in $definedoc//odm:ItemGroupDef[@Name=$datasetname]
    let $name := $dataset/@Name 
	let $dsname := (
		if($defineversion = '2.1') then $dataset/def21:leaf/@xlink:href
		else $dataset/def:leaf/@xlink:href
	)
    let $datasetdoc := (
		if($dsname) then doc(concat($base,$dsname))
		else ()
	)
    (: Get the OIDs of TPT and TPTNUM (if any) :)
    let $tptoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'TPT')]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
    )
    let $tptname := $definedoc//odm:ItemDef[@OID=$tptoid]/@Name
    (: Get the OIDs of TPT and TPTNUM (if any) :)
    let $tptnumoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'TPTNUM')]/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
    )
    let $tptnumname := $definedoc//odm:ItemDef[@OID=$tptnumoid]/@Name
    (: iterate over the records in the dataset that do have a TPT  :)
    for $record in $datasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$tptoid]]
        let $recnum := $record/@data:ItemGroupDataSeq 
		(: Get the -TPT value :)
        let $tptvalue := $record/odm:ItemData[@ItemOID=$tptoid]/@Value
		(: Report when --TPTNUM is absent :)
        where not($record/odm:ItemData[@ItemOID=$tptnumoid])
        return <warning rule="SD0033" rulelastupdate="2020-08-08" recordnumber="{data($recnum)}">Planned Time Point Number {data($tptnumname)} may not be NULL, when Planned Time Point Name {data($tptname)} is populated, value = {data($tptvalue)}</warning>
]]></rulexquery>
</sdsrule>


<sdsrule originator="FDA" id="SD1250" standard="SDTM" last-update="2020-08-08">
	<ruledescription>--TPTNUM variable must be present, when --TPT variable is present</ruledescription>
	<ruledetaileddescription>Planned Time Point Number (--TPTNUM) variable must be included into the domain, when Planned Time Point Name (--TPT) variable is present</ruledetaileddescription>
	<igversion>3.1.2</igversion>
	<igversion>3.1.3</igversion>
	<igversion>3.2</igversion>
	<domain>ALL</domain>
	<synonym context="CDISC">CG0468</synonym>
	<rulexquery><![CDATA[
(: Rule SD1250 - When --TPTNUM present in dataset then --TPT must be present in dataset :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdiscpilot01/' :)
(: let $define := 'define_2_0.xml' :)
let $definedoc := doc(concat($base,$define))
(: Applies to: all datasets :)
for $itemgroupdef in $definedoc//odm:ItemGroupDef
    let $name := $itemgroupdef/@Name
    let $domain := (
        if($itemgroupdef/@Domain) then $itemgroupdef/@Domain
        else substring($name,1,2) (: first 2 characters :)
    )
    (: get the dataset :)
    let $name := $itemgroupdef/@Name
    (: get the OID and name of --TPT :)
    let $tptoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'TPT')]/@OID 
        where $a = $itemgroupdef/odm:ItemRef/@ItemOID
        return $a
    )
    let $tptname := $definedoc//odm:ItemDef[@OID=$tptoid]/@Name
    (: get the OID of --TPTNUM (if any) :)
    let $tptnumoid := (
        for $a in $definedoc//odm:ItemDef[ends-with(@Name,'TPTNUM')]/@OID 
        where $a = $itemgroupdef/odm:ItemRef/@ItemOID
        return $a
    )
    (: if there is no --TPTNUM, we cannot know its full name, so we must then construct it :)
    let $tptnumname := (
        if($tptnumoid) then $definedoc//odm:ItemDef[@OID=$tptoid]/@Name
        else concat($domain,'TPTNUM')
    )
    (: When --TPT present in dataset then --TPTNUM must be present in dataset :)
    where $tptoid and not($tptnumoid)
    return
        <error rule="SD1250" rulelastupdate="2020-08-08" dataset="{data($name)}" variable="{data($tptnumname)}" >Variable {data($tptname)} is present in dataset {data($name)} but {data($tptnumname)} is not present in the dataset</error>			
		]]>
	</rulexquery>
</sdsrule>

</sdsrules>
