<?xml version="1.0" encoding="UTF-8"?>
<sdsrules last-update="2020-07-91T13:09:00" originator="FDA" standard="SEND" version="2019">

<title>FDA SEND validation rules v.1.3 June 2019</title>

<!-- Comment: as FDA does not publish whether it is a warning or error or info, the severity is taken from the P21 implementation, g
or, when the P21 doesn't make sense, we change it using common sense -->

<!-- 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="SD1129" last-update="2020-06-25" originator="FDA" standard="SEND">
<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>
<!-- Applicable for both SENDIG 3.0 and 3.1 -->
<igversion>3.0</igversion>
<igversion>3.1</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' :)
 
(: Get the DM dataset :)
for $dataset in doc(concat($base,$define))//odm:ItemGroupDef[@Name='DM']
    (: get the OID of the AGE and AGETXT variables (when present) :)
    let $ageoid := (
        for $a in doc(concat($base,$define))//odm:ItemDef[@Name='AGE']/@OID 
        where $a = doc(concat($base,$define))//odm:ItemGroupDef[@Name='DM']/odm:ItemRef/@ItemOID
        return $a
    )
    let $agetxtoid := (
        for $a in doc(concat($base,$define))//odm:ItemDef[@Name='AGETXT']/@OID 
        where $a = doc(concat($base,$define))//odm:ItemGroupDef[@Name='DM']/odm:ItemRef/@ItemOID
        return $a
    )
    where not($ageoid) and not($agetxtoid)
    return <error rule="SD1129" dataset="DM" rulelastupdate="2020-06-25">Neither AGE nor AGETXT variables are present</error>	
]]></rulexquery>
</sdsrule>

<sdsrule id="SD1121" last-update="2020-06-25" originator="FDA" standard="SEND">
<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>
<!-- Applicable to both SENDIG 3.0 and 3.1 -->
<igversion>3.0</igversion>
<igversion>3.1</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' :)

(: Get the DM dataset :)
for $dataset in doc(concat($base,$define))//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)
	let $datasetdoc := doc($datasetlocation)
    (: get the OID of the AGE and AGETXT variables (when present) :)
    let $ageoid := (
        for $a in doc(concat($base,$define))//odm:ItemDef[@Name='AGE']/@OID 
        where $a = doc(concat($base,$define))//odm:ItemGroupDef[@Name='DM']/odm:ItemRef/@ItemOID
        return $a
    )
    let $agetxtoid := (
        for $a in doc(concat($base,$define))//odm:ItemDef[@Name='AGETXT']/@OID 
        where $a = doc(concat($base,$define))//odm:ItemGroupDef[@Name='DM']/odm:ItemRef/@ItemOID
        return $a
    )
    (: we also need the OID of the ARMCD variable :)
    let $armcdoid := (
        for $a in doc(concat($base,$define))//odm:ItemDef[@Name='ARMCD']/@OID 
        where $a = doc(concat($base,$define))//odm:ItemGroupDef[@Name='DM']/odm:ItemRef/@ItemOID
        return $a
    )
    (: iterate over all the records in the DM dataset :)
    for $record in $datasetdoc//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-06-25" recordnumber="{data($recnum)}">Neither of AGE or AGETXT variables values are populated</warning>
]]></rulexquery>
</sdsrule>

<sdsrule id="SD2023" last-update="2020-06-25" originator="FDA" standard="SEND">
<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.0</igversion>
<igversion>3.1</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 $datasetdoc := doc(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 datasetdoc//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-06-25" recordnumber="{data($recnum)}">AGE is not provided</error>
]]></rulexquery>
</sdsrule>

<sdsrule id="SE2201" last-update="2020-06-25" originator="FDA" standard="SEND">
<ruledescription>Age or age range must be provided for all subjects, except for Screen Failures</ruledescription>
<ruledetaileddescription>'Age' (AGE) or 'Age Text' (AGETXT) records must be populated in Trial Summary (TS) domain</ruledetaileddescription>
<igversion>3.0</igversion>
<igversion>3.1</igversion>
<domain>TS</domain>
<rulexquery><![CDATA[		
(: Rule SE2201:   :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external program :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := 'SEND_3_0_PDS2014/' :)
(: let $define := 'define2-0-0_DS.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=AGE or TXTPARMCD=AGETXT :)
let $count := count(doc($tsdatasetlocation)//odm:ItemGroupData/odm:ItemData[@ItemOID=$tsparmcdoid and (@Value='AGE' or @Value='AGETXT')])
(: there must be not more than 1 such records :)
where $count = 0
return <error rule="SE2201" dataset="TS" variable="TSPARMCD" rulelastupdate="2020-06-25">No PARAMCD=AGE nor PARAMCD=AGETXT record was found</error>
]]>
</rulexquery>
</sdsrule>


<sdsrule id="SD2020" last-update="2020-06-26" originator="FDA" standard="SEND">
<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>
<igversion>3.0</igversion>
<igversion>3.1</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 $datasetdoc := doc(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 $datasetdoc//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="2019-08-13" recordnumber="{data($recnum)}">Both AGE and AGETXT variables values are populated</warning>	
]]></rulexquery>
</sdsrule>

<sdsrule id="SD1205" last-update="2020-06-25" originator="FDA" standard="SEND">
<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 SENDIG 3.0 -->
<igversion>3.1</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]/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 or EC dataset :)
let $ecexdataset := (
	if($defineversion='2.1') then $definedoc//odm:ItemGroupDef[@Name=$datasetname][@Name='EX' or @Name='EC']/def21:leaf/@xlink:href
	else $definedoc//odm:ItemGroupDef[@Name=$datasetname][@Name='EX' or @Name='EC']/def:leaf/@xlink:href
)
let $ecexdoc := doc(concat($base,$ecexdataset))
(: 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="2019-08-13">{data($stdtcname)} date '{data($stdtcvalue)}' is before RFXSTDTC '{data($rfxstdtcvalue)}'</error>
]]>
</rulexquery>
</sdsrule>

<sdsrule id="SD1148" last-update="2020-06-25" originator="FDA" standard="SEND">
<ruledescription>Start Date/Time of Treatment (EXSTDTC) variable value must be greater than or equal to Date/Time of Experimental Start Date (EXPSTDTC) in Trial Summary (TS) domain</ruledescription>
<igversion>3.0</igversion>
<igversion>3.1</igversion>
<domain>EX</domain>
<rulexquery><![CDATA[	
(: Rule SD1148: Start Date/Time of Treatment (EXSTDTC) variable value must be greater than or equal to Date/Time of Experimental Start Date (EXPSTDTC) in Trial Summary (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 := 'SEND_3_0_PDS2014/' :)
(: let $define := 'define2-0-0_DS.xml' :)
let $definedoc := doc(concat($base,$define))
(: get the TS dataset :)
let $tsdataset := $definedoc//odm:ItemGroupDef[@Name='TS']
let $tsdatasetloc := (
	if($defineversion='2.1') then $tsdataset/def21:leaf/@xlink:href
	else $tsdataset/def:leaf/@xlink:href
)
(: and the TS document itself :)
let $tsdoc := (
	if($tsdataset) then doc(concat($base,$tsdatasetloc))
    else()
)
(: get the OID of the TSPARMCD, ans 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 value of the EXPSTDTC parameter :)
let $expstdtc := $tsdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$tsparmcdoid and @Value='EXPSTDTC']]/odm:ItemData[@ItemOID=$tsvaloid]/@Value
(: Iterate over the EX datasets - there should be only one :)
for $exdataset in $definedoc//odm:ItemGroupDef[starts-with(@Name,'EX')]
	let $name := $exdataset/@Name
    (: get the location and the document itself :)
	let $exdatasetloc := (
		if($defineversion='2.1') then $exdataset/def21:leaf/@xlink:href
		else $exdataset/def:leaf/@xlink:href
	)
    let $exdoc := doc(concat($base,$exdatasetloc))
    (: get the OID of the EXSTDTC variable :)
	let $exstdtcoid := (
    	for $a in $definedoc//odm:ItemDef[@Name='EXSTDTC']/@OID 
    	where $a = $exdataset/odm:ItemRef/@ItemOID
    	return $a
    )
	(: Iterate over all records and get the value of EXSTDTC :)
    for $record in $exdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$exstdtcoid]]
    	let $recnum := $record/@data:ItemGroupDataSeq
        let $exstdtc := $record/odm:ItemData[@ItemOID=$exstdtcoid]/@Value
        (: Value of EXSTDTC must be on or after EXPSTDTC :)
		(:  TODO 2020-08-05: case that one or both values are of type xs:date :)
        where $exstdtc and xs:dateTime($exstdtc) < xs:dateTime($expstdtc) 
		return <error rule="SD1148" dataset="{data($name)}" variable="EXSTDTC" recordnumber="{data($recnum)}" rulelastupdate="2020-06-25">Value of EXSTDTC={data($exstdtc)} is before TS.EXPSTDTC={data($expstdtc)}</error>  
]]>
</rulexquery>
</sdsrule>

<sdsrule id="SD0040" last-update="2019-10-15" originator="FDA" standard="SEND">
<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.0</igversion>
<igversion>3.1</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[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)
    (: 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="2019-10-15" 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="2019-10-15" originator="FDA" standard="SEND" 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.0</igversion>
<igversion>3.1</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][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)
    (: 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="2019-10-15" 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="2019-08-14" originator="FDA" standard="SEND">
<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.0</igversion>
<igversion>3.1</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' :)
let $definedoc := doc(concat($base,$define))
(: iterate over all datasets that have --TESTCD and --TEST :)
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="2016-03-24" 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="2019-08-14" originator="FDA" requiresDomainOrDataset="Yes" standard="SEND">
<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.0</igversion>
<igversion>3.1</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' :)
let $definedoc := doc(concat($base,$define))
(: iterate over all datasets that have --TESTCD and --TEST :)
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 $datasetlocation := concat($base,$dsname)
    (: 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 doc($datasetlocation)//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="2019-08-14" 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="2019-08-14" originator="FDA" standard="SEND">
<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.0</igversion>
<igversion>3.1</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="2019-08-14">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="2019-08-14" originator="FDA" standard="SEND">
<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.0</igversion>
<igversion>3.1</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="2019-08-14">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="2019-08-14" originator="FDA" standard="SEND">
<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.0</igversion>
<igversion>3.1</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 $datasetdoc := (
		if($datasetname) then doc(concat($base,$datasetname))
		else ()
	)
    (: 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($datasetdoc//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 := $datasetdoc//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="2019-08-14">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="2019-08-14" originator="FDA" standard="SEND">
<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.0</igversion>
<igversion>3.1</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 $datasetdoc := (
		if($datasetname) then doc(concat($base,$datasetname))
		else ()
	)
    (: 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($datasetdoc//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 := $datasetdoc//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="2019-08-14">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="2019-08-14" originator="FDA" standard="SEND">
<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.0</igversion>
<igversion>3.1</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 if any :)
let $tedataset := $definedoc//odm:ItemGroupDef[@Name='TE']
let $tedatasetloc := (
	if($defineversion='2.1') then $tedataset/def21:leaf/@xlink:href
	else $tedataset/def:leaf/@xlink:href
)
let $tedoc := (
	if($tedatasetloc) then doc(concat($base,$tedatasetloc))
	else ()  (: TE not declared :)
)
(: 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 $tedoc//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 $tedoc//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="2019-08-14" 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="2019-08-14" originator="FDA" standard="SEND">
<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.0</igversion>
<igversion>3.1</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 if any :)
let $tedataset := $definedoc//odm:ItemGroupDef[@Name='TE']
let $tedatasetloc := (
	if($defineversion='2.1') then $tedataset/def21:leaf/@xlink:href
	else $tedataset/def:leaf/@xlink:href
)
let $tedoc := (
	if($tedatasetloc) then doc(concat($base,$tedatasetloc))
	else ()  (: TE not declared :)
)
(: 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 $tedoc//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 $tedoc//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="2019-08-14" 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="2019-08-14" originator="FDA" standard="SEND">
<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.0</igversion>
<igversion>3.1</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 $datasetdoc := (
		if($datasetname) then doc(concat($base,$datasetname))
		else ()
	)
    (: 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 $datasetdoc//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="2019-08-14">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="SD1112" last-update="2019-08-14" originator="FDA" standard="SEND">
<ruledescription>TA dataset must be present</ruledescription>
<ruledetaileddescription>Trial Arms (TA) dataset must be included in every submission</ruledetaileddescription>
<igversion>3.0</igversion>
<igversion>3.1</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 $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 $tadatasetlocation := concat($base,$tadatasetname)
(: we now have the location of the dataset, check whether it is available there :)
where (not(doc-available($tadatasetlocation)))
return <warning rule="SD1112" dataset="TA" rulelastupdate="2019-08-14">Document {data($tadatasetname)} could not be found in collection {data($base)}</warning>	
]]></rulexquery>
</sdsrule>

<sdsrule id="SD1113" last-update="2019-08-14" originator="FDA" standard="SEND">
<ruledescription>TE dataset must be present</ruledescription>
<ruledetaileddescription>Trial Elements (TE) dataset should be included in every submission</ruledetaileddescription>
<igversion>3.0</igversion>
<igversion>3.1</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 $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 $tedatasetlocation := concat($base,$tedatasetname)
(: we now have the location of the dataset, check whether it is available there :)
where (not(doc-available($tedatasetlocation)))
return <warning rule="SD1113" dataset="TE" rulelastupdate="2019-08-14">Document {data($tedatasetname)} could not be found in collection {data($base)}</warning>
]]></rulexquery>
</sdsrule>

<!-- Rule "SE0007 Missing TX dataset" is only for SEND -->
<sdsrule id="SE0007" last-update="2020-06-25" originator="FDA" standard="SEND">
<ruledescription>TX dataset must be present</ruledescription>
<ruledetaileddescription>Trial Sets (TX) dataset must be included in every submission</ruledetaileddescription>
<igversion>3.0</igversion>
<igversion>3.1</igversion>
<domain>TX</domain>
<rulexquery><![CDATA[	
(: Rule SE0007 - Missing TX 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 TX dataset :)
let $definedoc := doc(concat($base,$define))
let $txdatasetname := (
	if($defineversion='2.1') then $definedoc//odm:ItemGroupDef[@Name='TX']/def21:leaf/@xlink:href
	else $definedoc//odm:ItemGroupDef[@Name='TX']/def:leaf/@xlink:href
)
let $txdatasetlocation := concat($base,$txdatasetname)
(: we now have the location of the dataset, check whether it is available there :)
where (not(doc-available($txdatasetlocation)))
return <warning rule="SE0007" dataset="TX" rulelastupdate="2020-06-25">Document TX could not be found in collection {data($base)}</warning>
]]></rulexquery>
</sdsrule>


<sdsrule id="SE2202" last-update="2020-06-25" originator="FDA" standard="SEND">
<ruledescription>'Age Unit' (AGEU) record must be populated in Trial Summary (TS)</ruledescription>
<igversion>3.0</igversion>
<igversion>3.1</igversion>
<domain>TS</domain>
<rulexquery><![CDATA[
(: Rule SE2202 - TSPARMCD=AGEU record must be present in 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 variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: let $base := 'SEND_3_0_PDS2014/' :)
(: let $define := 'define2-0-0_DS.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
)
(: An TSPARMCD=AGEU must be present :)
let $tsparmcdvalue := $tsdatasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$tsparmcdoid and @Value='AGEU']]
where not($tsparmcdvalue)
return <error rule="SE2202" dataset="TS" rulelastupdate="2020-06-25">No TSPARMCD=AGEU record found is TS</error>
]]></rulexquery>
</sdsrule>

<sdsrule id="SE2203" last-update="2020-06-25" originator="FDA" standard="SEND">
<ruledescription>'Study Design' (SDESIGN) record must be populated in Trial Summary (TS) domain</ruledescription>
<igversion>3.0</igversion>
<igversion>3.1</igversion>
<domain>TS</domain>
<rulexquery><![CDATA[
(: Rule SE2203 - 'Study Design' (SDESIGN) record must be populated in Trial Summary (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 := 'SEND_3_0_PDS2014/' :)
(: let $define := 'define2-0-0_DS.xml' :)
(: get the TS dataset :)
let $definedoc := doc(concat($base,$define))
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
)
(: An TSPARMCD=SDESIGN must be present :)
let $tsparmcdvalue := $tsdatasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$tsparmcdoid and @Value='SDESIGN']]
where not($tsparmcdvalue)
return <error rule="SE2203" dataset="TS" rulelastupdate="2020-06-25">No TSPARMCD=SDESIGN record found is TS</error>
]]>
</rulexquery>
</sdsrule>


<sdsrule id="SE2204" last-update="2020-06-25" originator="FDA" standard="SEND">
<ruledescription>'Study Design' (SDESIGN) record must be populated in Trial Summary (TS) domain</ruledescription>
<igversion>3.0</igversion>
<igversion>3.1</igversion>
<domain>TS</domain>
<rulexquery><![CDATA[
(: Rule SE2204 - 'Study Design' (SDESIGN) record must be populated in Trial Summary (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 := 'SEND_3_0_PDS2014/' :)
(: let $define := 'define2-0-0_DS.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
)
(: An TSPARMCD=SDESIGN must be present :)
let $tsparmcdvalue := $tsdatasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$tsparmcdoid and @Value='SDESIGN']]
where not($tsparmcdvalue)
return <error rule="SE2204" dataset="TS" rulelastupdate="2020-06-25">No TSPARMCD=SDESIGN record found is TS</error>
]]>
</rulexquery>
</sdsrule>


<sdsrule id="SE2205" last-update="2020-06-25" originator="FDA" standard="SEND">
<ruledescription>'Experimental End Date' (EXPENDTC) record must be populated in Trial Summary (TS) domain</ruledescription>
<igversion>3.0</igversion>
<igversion>3.1</igversion>
<domain>TS</domain>
<rulexquery><![CDATA[
(: Rule SE2205 - 'Experimental End Date' (EXPENDTC) record must be populated in Trial Summary (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 := 'SEND_3_0_PDS2014/' :)
(: let $define := 'define2-0-0_DS.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
)
(: An TSPARMCD=EXPENDTC must be present :)
let $tsparmcdvalue := $tsdatasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$tsparmcdoid and @Value='EXPENDTC']]
where not($tsparmcdvalue)
return <error rule="SE2205" dataset="TS" rulelastupdate="2020-06-25">No TSPARMCD=EXPENDTC record found is TS</error>
]]>
</rulexquery>
</sdsrule>

<sdsrule id="SE2206" last-update="2020-06-25" originator="FDA" standard="SEND">
<ruledescription>'Experimental Start Date' (EXPSTDTC) record must be populated in Trial Summary (TS) domain</ruledescription>
<igversion>3.0</igversion>
<igversion>3.1</igversion>
<domain>TS</domain>
<rulexquery><![CDATA[
(: Rule SE2206 - 'Experimental Start Date' (EXPSTDTC) record must be populated in Trial Summary (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 := 'SEND_3_0_PDS2014/' :)
(: let $define := 'define2-0-0_DS.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
)
(: An TSPARMCD=EXPSTDTC must be present :)
let $tsparmcdvalue := $tsdatasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$tsparmcdoid and @Value='EXPSTDTC']]
where not($tsparmcdvalue)
return <error rule="SE2206" dataset="TS" rulelastupdate="2020-06-25">No TSPARMCD=EXPSTDTC record found is TS</error>
]]>
</rulexquery>
</sdsrule>


<sdsrule id="SE2207" last-update="2020-06-25" originator="FDA" standard="SEND">
<ruledescription>'Good Laboratory Practice Type' (GLPTYP) record must be populated in Trial Summary (TS) domain</ruledescription>
<igversion>3.0</igversion>
<igversion>3.1</igversion>
<domain>TS</domain>
<rulexquery><![CDATA[
(: Rule SE2207 - 'Good Laboratory Practice Type' (GLPTYP) record must be populated in Trial Summary (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 := 'SEND_3_0_PDS2014/' :)
(: let $define := 'define2-0-0_DS.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
)
(: An TSPARMCD=GLPTYP must be present :)
let $tsparmcdvalue := $tsdatasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$tsparmcdoid and @Value='GLPTYP']]
where not($tsparmcdvalue)
return <error rule="SE2207" dataset="TS" rulelastupdate="2020-06-25">No TSPARMCD=GLPTYP record found is TS</error>
]]>
</rulexquery>
</sdsrule>

<sdsrule id="SE2208" last-update="2020-06-25" originator="FDA" standard="SEND">
<ruledescription>'Route of Administration' (ROUTE) record must be populated in Trial Summary (TS) domain</ruledescription>
<igversion>3.0</igversion>
<igversion>3.1</igversion>
<domain>TS</domain>
<rulexquery><![CDATA[
(: Rule SE2208 - 'Route of Administration' (ROUTE) record must be populated in Trial Summary (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 := 'SEND_3_0_PDS2014/' :)
(: let $define := 'define2-0-0_DS.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
)
(: An TSPARMCD=ROUTE must be present :)
let $tsparmcdvalue := $tsdatasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$tsparmcdoid and @Value='ROUTE']]
where not($tsparmcdvalue)
return <error rule="SE2208" dataset="TS" rulelastupdate="2020-06-25">No TSPARMCD=ROUTE record found is TS</error>
]]>
</rulexquery>
</sdsrule>

<sdsrule id="SE2209" last-update="2020-06-25" originator="FDA" standard="SEND">
<ruledescription>'SEND Implementation Guide Version' (SNDIGVER) record must be populated in Trial Summary (TS) domain</ruledescription>
<igversion>3.0</igversion>
<igversion>3.1</igversion>
<domain>TS</domain>
<rulexquery><![CDATA[
(: Rule SE2209 - 'SEND Implementation Guide Version' (SNDIGVER) record must be populated in Trial Summary (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 := 'SEND_3_0_PDS2014/' :)
(: let $define := 'define2-0-0_DS.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
)
(: An TSPARMCD=SNDIGVER must be present :)
let $tsparmcdvalue := $tsdatasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$tsparmcdoid and @Value='SNDIGVER']]
where not($tsparmcdvalue)
return <error rule="SE2209" dataset="TS" rulelastupdate="2020-06-25">No TSPARMCD=SNDIGVER record found is TS</error>
]]>
</rulexquery>
</sdsrule>

<sdsrule id="SE2210" last-update="2020-06-25" originator="FDA" standard="SEND">
<ruledescription>'SEND Control Terminology Version' (SNDCTVER) record must be populated in Trial Summary (TS) domain</ruledescription>
<igversion>3.0</igversion>
<igversion>3.1</igversion>
<domain>TS</domain>
<rulexquery><![CDATA[
(: Rule SE2210 - 'SEND Control Terminology Version' (SNDCTVER) record must be populated in Trial Summary (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 := 'SEND_3_0_PDS2014/' :)
(: let $define := 'define2-0-0_DS.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
)
(: An TSPARMCD=SNDCTVER must be present :)
let $tsparmcdvalue := $tsdatasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$tsparmcdoid and @Value='SNDCTVER']]
where not($tsparmcdvalue)
return <error rule="SE2210" dataset="TS" rulelastupdate="2020-06-25">No TSPARMCD=SNDCTVER record found is TS</error>
]]>
</rulexquery>
</sdsrule>

<sdsrule id="SE2211" last-update="2020-06-25" originator="FDA" standard="SEND">
<ruledescription>'Species' (SPECIES) record should be populated in Trial Summary (TS) domain record must be populated in Trial Summary (TS) domain</ruledescription>
<igversion>3.0</igversion>
<igversion>3.1</igversion>
<domain>TS</domain>
<rulexquery><![CDATA[
(: Rule SE2211 - 'Species' (SPECIES) record should be populated in Trial Summary (TS) domain record must be populated in Trial Summary (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 := 'SEND_3_0_PDS2014/' :)
(: let $define := 'define2-0-0_DS.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
)
(: An TSPARMCD=SPECIES must be present :)
let $tsparmcdvalue := $tsdatasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$tsparmcdoid and @Value='SPECIES']]
where not($tsparmcdvalue)
return <error rule="SE2211" dataset="TS" rulelastupdate="2020-06-25">No TSPARMCD=SPECIES record found is TS</error>
]]>
</rulexquery>
</sdsrule>

<sdsrule id="SE2212" last-update="2020-06-25" originator="FDA" standard="SEND">
<ruledescription>'Test Subject Supplier' (SPLRNAM) record should be populated in Trial Summary (TS) domain record must be populated in Trial Summary (TS) domain</ruledescription>
<igversion>3.0</igversion>
<igversion>3.1</igversion>
<domain>TS</domain>
<rulexquery><![CDATA[
(: Rule SE2212 - 'Test Subject Supplier' (SPLRNAM) record should be populated in Trial Summary (TS) domain record must be populated in Trial Summary (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 := 'SEND_3_0_PDS2014/' :)
(: let $define := 'define2-0-0_DS.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
)
(: An TSPARMCD=SPLRNAM must be present :)
let $tsparmcdvalue := $tsdatasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$tsparmcdoid and @Value='SPLRNAM']]
where not($tsparmcdvalue)
return <error rule="SE2212" dataset="TS" rulelastupdate="2020-06-25">No TSPARMCD=SPLRNAM record found is TS</error>
]]>
</rulexquery>
</sdsrule>

<sdsrule id="SE2213" last-update="2020-06-25" originator="FDA" standard="SEND">
<ruledescription>'Sponsor's Reference ID' (SPREFID) record should be populated in Trial Summary (TS) domain record must be populated in Trial Summary (TS) domain</ruledescription>
<igversion>3.0</igversion>
<igversion>3.1</igversion>
<domain>TS</domain>
<rulexquery><![CDATA[
(: Rule SE2213 - 'Sponsor's Reference ID' (SPREFID) record should be populated in Trial Summary (TS) domain record must be populated in Trial Summary (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 := 'SEND_3_0_PDS2014/' :)
(: let $define := 'define2-0-0_DS.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
)
(: An TSPARMCD=SPREFID must be present :)
let $tsparmcdvalue := $tsdatasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$tsparmcdoid and @Value='SPREFID']]
where not($tsparmcdvalue)
return <error rule="SE2213" dataset="TS" rulelastupdate="2020-06-25">No TSPARMCD=SPREFID record found is TS</error>
]]>
</rulexquery>
</sdsrule>

<sdsrule id="SE2214" last-update="2020-06-25" originator="FDA" standard="SEND">
<ruledescription>'Sponsoring Organization' (SSPONSOR) record should be populated in Trial Summary (TS) domain record must be populated in Trial Summary (TS) domain</ruledescription>
<igversion>3.0</igversion>
<igversion>3.1</igversion>
<domain>TS</domain>
<rulexquery><![CDATA[
(: Rule SE2214 - 'Sponsoring Organization' (SSPONSOR) record should be populated in Trial Summary (TS) domain record must be populated in Trial Summary (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 := 'SEND_3_0_PDS2014/' :)
(: let $define := 'define2-0-0_DS.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
)
(: An TSPARMCD=SSPONSOR must be present :)
let $tsparmcdvalue := $tsdatasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$tsparmcdoid and @Value='SSPONSOR']]
where not($tsparmcdvalue)
return <error rule="SE2214" dataset="TS" rulelastupdate="2020-06-25">No TSPARMCD=SSPONSOR record found is TS</error>
]]>
</rulexquery>
</sdsrule>

<sdsrule id="SE2215" last-update="2020-06-25" originator="FDA" standard="SEND">
<ruledescription>'Study Category' (STCAT) record should be populated in Trial Summary (TS) domain record must be populated in Trial Summary (TS) domain</ruledescription>
<igversion>3.0</igversion>
<igversion>3.1</igversion>
<domain>TS</domain>
<rulexquery><![CDATA[
(: Rule SE2215 - 'Study Category' (STCAT) record should be populated in Trial Summary (TS) domain record must be populated in Trial Summary (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 := 'SEND_3_0_PDS2014/' :)
(: let $define := 'define2-0-0_DS.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
)
(: An TSPARMCD=STCAT must be present :)
let $tsparmcdvalue := $tsdatasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$tsparmcdoid and @Value='STCAT']]
where not($tsparmcdvalue)
return <error rule="SE2215" dataset="TS" rulelastupdate="2020-06-25">No TSPARMCD=STCAT record found is TS</error>
]]>
</rulexquery>
</sdsrule>

<sdsrule id="SE2216" last-update="2020-06-25" originator="FDA" standard="SEND">
<ruledescription>'Study Director' (STDIR) record should be populated in Trial Summary (TS) domain record must be populated in Trial Summary (TS) domain</ruledescription>
<igversion>3.0</igversion>
<igversion>3.1</igversion>
<domain>TS</domain>
<rulexquery><![CDATA[
(: Rule SE2216 - 'Study Director' (STDIR) record should be populated in Trial Summary (TS) domain record must be populated in Trial Summary (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 := 'SEND_3_0_PDS2014/' :)
(: let $define := 'define2-0-0_DS.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
)
(: An TSPARMCD=STDIR must be present :)
let $tsparmcdvalue := $tsdatasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$tsparmcdoid and @Value='STDIR']]
where not($tsparmcdvalue)
return <error rule="SE2216" dataset="TS" rulelastupdate="2020-06-25">No TSPARMCD=STDIR record found is TS</error>
]]>
</rulexquery>
</sdsrule>

<sdsrule id="SE2217" last-update="2020-06-25" originator="FDA" standard="SEND">
<ruledescription>'Strain/Substrain' (STRAIN) record should be populated in Trial Summary (TS) domain record must be populated in Trial Summary (TS) domain</ruledescription>
<igversion>3.0</igversion>
<igversion>3.1</igversion>
<domain>TS</domain>
<rulexquery><![CDATA[
(: Rule SE2217 - 'Strain/Substrain' (STRAIN) record should be populated in Trial Summary (TS) domain record must be populated in Trial Summary (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 := 'SEND_3_0_PDS2014/' :)
(: let $define := 'define2-0-0_DS.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
)
(: An TSPARMCD=STRAIN must be present :)
let $tsparmcdvalue := $tsdatasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$tsparmcdoid and @Value='STRAIN']]
where not($tsparmcdvalue)
return <error rule="SE2217" dataset="TS" rulelastupdate="2020-06-25">No TSPARMCD=STRAIN record found is TS</error>
]]>
</rulexquery>
</sdsrule>

<sdsrule id="SE2218" last-update="2020-06-25" originator="FDA" standard="SEND">
<ruledescription>'Study Start Date' (STSTDTC) record should be populated in Trial Summary (TS) domain record must be populated in Trial Summary (TS) domain</ruledescription>
<igversion>3.0</igversion>
<igversion>3.1</igversion>
<domain>TS</domain>
<rulexquery><![CDATA[
(: Rule SE2218 - 'Study Start Date' (STSTDTC) record should be populated in Trial Summary (TS) domain record must be populated in Trial Summary (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 := 'SEND_3_0_PDS2014/' :)
(: let $define := 'define2-0-0_DS.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
)
(: An TSPARMCD=STSTDTC must be present :)
let $tsparmcdvalue := $tsdatasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$tsparmcdoid and @Value='STSTDTC']]
where not($tsparmcdvalue)
return <error rule="SE2218" dataset="TS" rulelastupdate="2020-06-25">No TSPARMCD=STSTDTC record found is TS</error>
]]>
</rulexquery>
</sdsrule>

<sdsrule id="SE2219" last-update="2020-06-25" originator="FDA" standard="SEND">
<ruledescription>'Study Title' (STITLE) record should be populated in Trial Summary (TS) domain record must be populated in Trial Summary (TS) domain</ruledescription>
<igversion>3.0</igversion>
<igversion>3.1</igversion>
<domain>TS</domain>
<rulexquery><![CDATA[
(: Rule SE2219 - 'Study Title' (STITLE) record should be populated in Trial Summary (TS) domain record must be populated in Trial Summary (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 := 'SEND_3_0_PDS2014/' :)
(: let $define := 'define2-0-0_DS.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
)
(: An TSPARMCD=STITLE must be present :)
let $tsparmcdvalue := $tsdatasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$tsparmcdoid and @Value='STITLE']]
where not($tsparmcdvalue)
return <error rule="SE2219" dataset="TS" rulelastupdate="2020-06-25">No TSPARMCD=STITLE record found is TS</error>
]]>
</rulexquery>
</sdsrule>

<sdsrule id="SE2220" last-update="2020-06-25" originator="FDA" standard="SEND">
<ruledescription>'Study Type' (SSTYP) record should be populated in Trial Summary (TS) domain record must be populated in Trial Summary (TS) domain</ruledescription>
<igversion>3.0</igversion>
<igversion>3.1</igversion>
<domain>TS</domain>
<rulexquery><![CDATA[
(: Rule SE2220 - 'Study Type' (SSTYP) record should be populated in Trial Summary (TS) domain record must be populated in Trial Summary (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 := 'SEND_3_0_PDS2014/' :)
(: let $define := 'define2-0-0_DS.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
)
(: An TSPARMCD=SSTYP must be present :)
let $tsparmcdvalue := $tsdatasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$tsparmcdoid and @Value='SSTYP']]
where not($tsparmcdvalue)
return <error rule="SE2220" dataset="TS" rulelastupdate="2020-06-25">No TSPARMCD=SSTYP record found is TS</error>
]]>
</rulexquery>
</sdsrule>

<sdsrule id="SE2221" last-update="2020-06-25" originator="FDA" standard="SEND">
<ruledescription>'Investigational Therapy or Treatment' (TRT) record should be populated in Trial Summary (TS) domain record must be populated in Trial Summary (TS) domain</ruledescription>
<igversion>3.0</igversion>
<igversion>3.1</igversion>
<domain>TS</domain>
<rulexquery><![CDATA[
(: Rule SE2221 - 'Investigational Therapy or Treatment' (TRT) record should be populated in Trial Summary (TS) domain record must be populated in Trial Summary (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 := 'SEND_3_0_PDS2014/' :)
(: let $define := 'define2-0-0_DS.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
)
(: An TSPARMCD=TRT must be present :)
let $tsparmcdvalue := $tsdatasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$tsparmcdoid and @Value='TRT']]
where not($tsparmcdvalue)
return <error rule="SE2221" dataset="TS" rulelastupdate="2020-06-25">No TSPARMCD=TRT record found is TS</error>
]]>
</rulexquery>
</sdsrule>

<sdsrule id="SE2222" last-update="2020-06-25" originator="FDA" standard="SEND">
<ruledescription>'Test Facility Country' (TFCNTRY) record should be populated in Trial Summary (TS) domain record must be populated in Trial Summary (TS) domain</ruledescription>
<igversion>3.0</igversion>
<igversion>3.1</igversion>
<domain>TS</domain>
<rulexquery><![CDATA[
(: Rule SE2222 - 'Test Facility Country' (TFCNTRY) record should be populated in Trial Summary (TS) domain record must be populated in Trial Summary (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 := 'SEND_3_0_PDS2014/' :)
(: let $define := 'define2-0-0_DS.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
)
(: An TSPARMCD=TFCNTRY must be present :)
let $tsparmcdvalue := $tsdatasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$tsparmcdoid and @Value='TFCNTRY']]
where not($tsparmcdvalue)
return <error rule="SE2222" dataset="TS" rulelastupdate="2020-06-25">No TSPARMCD=TFCNTRY record found is TS</error>
]]>
</rulexquery>
</sdsrule>

<sdsrule id="SE2223" last-update="2020-06-25" originator="FDA" standard="SEND">
<ruledescription>'Test Facility Location' (TSTFLOC) record should be populated in Trial Summary (TS) domain record must be populated in Trial Summary (TS) domain</ruledescription>
<igversion>3.0</igversion>
<igversion>3.1</igversion>
<domain>TS</domain>
<rulexquery><![CDATA[
(: Rule SE2223 - 'Test Facility Location' (TSTFLOC) record should be populated in Trial Summary (TS) domain record must be populated in Trial Summary (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 := 'SEND_3_0_PDS2014/' :)
(: let $define := 'define2-0-0_DS.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
)
(: An TSPARMCD=TSTFLOC must be present :)
let $tsparmcdvalue := $tsdatasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$tsparmcdoid and @Value='TSTFLOC']]
where not($tsparmcdvalue)
return <error rule="SE2223" dataset="TS" rulelastupdate="2020-06-25">No TSPARMCD=TSTFLOC record found is TS</error>
]]>
</rulexquery>
</sdsrule>


<sdsrule id="SE2224" last-update="2020-06-25" originator="FDA" standard="SEND">
<ruledescription>'Test Facility Name' (TSTFNAM) record should be populated in Trial Summary (TS) domain record must be populated in Trial Summary (TS) domain</ruledescription>
<igversion>3.0</igversion>
<igversion>3.1</igversion>
<domain>TS</domain>
<rulexquery><![CDATA[
(: Rule SE2224 - 'Test Facility Name' (TSTFNAM) record should be populated in Trial Summary (TS) domain record must be populated in Trial Summary (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 := 'SEND_3_0_PDS2014/' :)
(: let $define := 'define2-0-0_DS.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
)
(: An TSPARMCD=TSTFNAM must be present :)
let $tsparmcdvalue := $tsdatasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$tsparmcdoid and @Value='TSTFNAM']]
where not($tsparmcdvalue)
return <error rule="SE2224" dataset="TS" rulelastupdate="2020-06-25">No TSPARMCD=TSTFNAM record found is TS</error>
]]>
</rulexquery>
</sdsrule>

<sdsrule id="SE2225" last-update="2020-06-25" originator="FDA" standard="SEND">
<ruledescription>'Time to Terminal Sacrifice' (TRMSAC) record should be populated in Trial Summary (TS) domain record must be populated in Trial Summary (TS) domain</ruledescription>
<igversion>3.0</igversion>
<igversion>3.1</igversion>
<domain>TS</domain>
<rulexquery><![CDATA[
(: Rule SE2225 - 'Time to Terminal Sacrifice' (TRMSAC) record should be populated in Trial Summary (TS) domain record must be populated in Trial Summary (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 := 'SEND_3_0_PDS2014/' :)
(: let $define := 'define2-0-0_DS.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
)
(: An TSPARMCD=TRMSAC must be present :)
let $tsparmcdvalue := $tsdatasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$tsparmcdoid and @Value='TRMSAC']]
where not($tsparmcdvalue)
return <error rule="SE2225" dataset="TS" rulelastupdate="2020-06-25">No TSPARMCD=TRMSAC record found is TS</error>
]]>
</rulexquery>
</sdsrule>

<sdsrule id="SE2226" last-update="2020-06-25" originator="FDA" standard="SEND">
<ruledescription>'Primary Treatment CAS Registry Number' (TRTCAS) record should be populated in Trial Summary (TS) domain record must be populated in Trial Summary (TS) domain</ruledescription>
<igversion>3.0</igversion>
<igversion>3.1</igversion>
<domain>TS</domain>
<rulexquery><![CDATA[
(: Rule SE2226 - 'Primary Treatment CAS Registry Number' (TRTCAS) record should be populated in Trial Summary (TS) domain record must be populated in Trial Summary (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 := 'SEND_3_0_PDS2014/' :)
(: let $define := 'define2-0-0_DS.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
)
(: An TSPARMCD=TRTCAS must be present :)
let $tsparmcdvalue := $tsdatasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$tsparmcdoid and @Value='TRTCAS']]
where not($tsparmcdvalue)
return <error rule="SE2226" dataset="TS" rulelastupdate="2020-06-25">No TSPARMCD=TRTCAS record found is TS</error>
]]>
</rulexquery>
</sdsrule>

<sdsrule id="SE2227" last-update="2020-06-25" originator="FDA" standard="SEND">
<ruledescription>'Treatment Vehicle' (TRTV) record should be populated in Trial Summary (TS) domain record must be populated in Trial Summary (TS) domain</ruledescription>
<igversion>3.0</igversion>
<igversion>3.1</igversion>
<domain>TS</domain>
<rulexquery><![CDATA[
(: Rule SE2227 - 'Treatment Vehicle' (TRTV) record should be populated in Trial Summary (TS) domain record must be populated in Trial Summary (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 := 'SEND_3_0_PDS2014/' :)
(: let $define := 'define2-0-0_DS.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
)
(: An TSPARMCD=TRTV must be present :)
let $tsparmcdvalue := $tsdatasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$tsparmcdoid and @Value='TRTV']]
where not($tsparmcdvalue)
return <error rule="SE2227" dataset="TS" rulelastupdate="2020-06-25">No TSPARMCD=TRTV record found is TS</error>
]]>
</rulexquery>
</sdsrule>


<!-- Rule SD0026: "" is not implemented as it is "Bullshit" and cannot be implemented without the LOINC code -->

<sdsrule id="SD0044" last-update="2019-08-15" originator="FDA" standard="SEND">
<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.0</igversion>
<igversion>3.1</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 $datasetdoc := (
		if($datasetname) then doc(concat($base,$datasetname))
		else ()
	)
    (: 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 $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 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="2019-08-15" recordnumber="{data($recnum)}">Missing value for {data($vamtuname)}, when {data($vamtname)} is populated</warning>	
]]></rulexquery>
</sdsrule>

<sdsrule id="SD0006" last-update="2020-06-25" originator="FDA" standard="SEND">
<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.0</igversion>
<igversion>3.1</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 many 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 := 'SEND_3_0_PDS2014/'  :)
(: let $define := 'define2-0-0_DS.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 := (
    	for $a in $definedoc//odm:ItemGrouDef[@Domain=$domain]
        	where $a/def:leaf/@xlink:href
            return concat($base,$a/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 := doc(concat($base,$define))//odm:ItemDef[@Name=$blflname]/@OID
    let $usubjidoid := (
        for $a in doc(concat($base,$define))//odm:ItemDef[@Name='USUBJID']/@OID 
        where $a = doc(concat($base,$define))//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-06-25">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-06-25" originator="FDA" standard="SEND" 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
Single dataset implementation
:)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare 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 $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 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($datasetdoc//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-06-25">No baseline result in Dataset {data($domain)} for subject {data($subject)} </warning>	
]]></rulexquery>
</sdsrule>

<sdsrule id="SD1044" last-update="2019-08-15" originator="FDA" standard="SEND">
<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.0</igversion>
<igversion>3.1</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="2019-08-15">No baseline variable {data($blflname)} in custom Findings dataset {data(name)}</warning>	 
]]>
</rulexquery>
</sdsrule>


<sdsrule id="SD1021-SD" last-update="2019-08-15" originator="FDA" standard="SEND" 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.0</igversion>
<igversion>3.1</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="2019-08-15">Unexpected character value in non-numeric variable value {data($varname)}. Value '{data($value)}' was found</warning>	 
]]>
</rulexquery>
</sdsrule>

<sdsrule id="SD1083" last-update="2019-08-15" originator="FDA" standard="SEND">
<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.0</igversion>
<igversion>3.1</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="2019-08-15">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>

<!-- Rule SD1082: "Variable length is too long for actual data" is not implemented as it only applies to SAS-XPT format -->

<sdsrule id="SD1020" last-update="2019-08-15" originator="FDA" standard="SEND" isrejectioncriterion="Yes">
<ruledescription>DM dataset must be included</ruledescription>
<ruledetaileddescription>Demographics (DM) dataset must be included in every submission</ruledetaileddescription>
<igversion>3.0</igversion>
<igversion>3.1</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 $dmdatasetname := (
	if($defineversion='2.1') then doc(concat($base,$define))//odm:ItemGroupDef[@Name='DM']/def21:leaf/@xlink:href
	else doc(concat($base,$define))//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="2019-08-15" dataset="DM">Document {data($dmdatasetname)} could not be found in collection {data($base)}</error>	
]]></rulexquery>
</sdsrule>


<sdsrule id="SD1115" last-update="2019-08-15" originator="FDA" standard="SEND" isrejectioncriterion="Yes">
<ruledescription>TS dataset must be included</ruledescription>
<ruledetaileddescription>Trial Summary (TS) dataset must be included in every submission</ruledetaileddescription>
<igversion>3.0</igversion>
<igversion>3.1</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 $tsdatasetname := (
	if($defineversion='2.1') then doc(concat($base,$define))//odm:ItemGroupDef[@Name='TS']/def21:leaf/@xlink:href
	else doc(concat($base,$define))//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="2015-02-09" dataset="TS">Document {data($tsdatasetname)} could not be found in collection {data($base)}</error>
]]></rulexquery>
</sdsrule>

<!-- Rule SD1117: "The structure of Findings class domains should be one record 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".
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.

This rule must be replaced by a rule that is based on the keys as defined in the define.xml
-->

<sdsrule id="SD1071-eXist" last-update="2019-08-16" originator="FDA" standard="SEND">
<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.0</igversion>
<igversion>3.1</igversion>
<domain>ALL</domain>
<rulexquery><![CDATA[
(: Rule SD1071 - Dataset is greater than 5 GB in size :)
(: Implementation ONLY in compbination with eXist :)
(: 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, and does not work with BaseX :)
(: 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 namespace xmldb="http://exist-db.org/xquery/xmldb";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external; 
declare variable $define external; 
declare variable $defineversion external;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
(: TODO: xmldb:get-child-resources only works for eXist-DB :)
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="2016-03-25">Size of file {$child} is larger than 1GB - file size found = {$size} MB </warning>
]]></rulexquery>
</sdsrule>

<sdsrule id="SD1071-BaseX" last-update="2020-07-01" originator="FDA" standard="SEND">
<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.0</igversion>
<igversion>3.1</igversion>
<domain>ALL</domain>
<rulexquery><![CDATA[
(: Rule SD1071 - Dataset is greater than 5 GB in size :)
(: Implementation ONLY in compbination with BaseX :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external; 
declare variable $define external;
declare variable $defineversion external;
(: In the example, we use the path to the file. 
This may look differently when the file is in a native XML database :)
(: let $base := 'C:\CDISC_Standards\CDISC_SEND\PDS2014_SEND_Data_Set\' :)
(: let $define := 'define2-0-0_DS.xml' :)
let $definedoc := doc(concat($base,$define))
(: Iterate over all the dataset definitions :)
for $dataset in $definedoc//odm:ItemGroupDef	
	let $name := $dataset/@Name
    (: Get the location of the file :)
	let $datasetloc := (
		if($defineversion='2.1') then $dataset/def21:leaf/@xlink:href
		else $dataset/def:leaf/@xlink:href
	)
    (: BaseX-specific: get the file size :)
    let $filesize := file:size(concat($base,$datasetloc)) div (1024*1024)
    where $filesize > 5*1024
    return <warning rule="SD1071" dataset="{$name}" rulelastupdate="2020-07-01">Size of file {data($datasetloc)} is larger than 5GB - file size found = {$filesize} MB </warning>
]]>
</rulexquery>
</sdsrule>


<sdsrule id="SD1131" last-update="2019-08-16" originator="FDA" standard="SEND">
<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.0</igversion>
<igversion>3.1</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="2019-08-16" 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="2019-08-16" originator="FDA" standard="SEND" 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.0</igversion>
<igversion>3.1</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 $dataset/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="2019-08-16" recordnumber="{data($recnum)}">Missing {data($strescname)}, when {data($blflname)} value={data($blflvalue)} is provided in dataset {data($datasetname)}</warning>	
]]></rulexquery>
</sdsrule>

<sdsrule id="SE0056" last-update="2020-06-05" originator="FDA" standard="SEND" requiresDomainOrDataset="Yes" webservice="Uses CDISC Library RESTful Web Service">
<ruledescription>Variables described in SEND IG as Required must be included in the dataset</ruledescription>
<igversion>3.0</igversion>
<igversion>3.1</igversion>
<domain>ALL</domain>
<rulexquery><![CDATA[	
(: Rule SE0056: Variables described in SEND IG as Required must be included in the dataset :)
(: The CDISC Library web service is used to retrieve the required variables for each domain/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;
declare variable $username external;
declare variable $password external;
(: let $base := 'SEND_3_0_PDS2014/' :)
(: let $define := 'define2-0-0_DS.xml' :)
(: CDISC Library username and password :)
(: let $username := 'xxxx' :)
(: let $password := 'yyy' :)
(: let $datasetname := 'ALL' :)
let $definedoc := doc(concat($base,$define))
(: CDISC Library base :)
let $cdisclibrarybase := 'https://library.cdisc.org/api/mdr/'
(: EITHER provide $datasetname=:'ALL', meaning: validate for all datasets referenced from the define.xml OR:
$datasetname:='XX' where XX is a specific dataset, MEANING validate for a single dataset or domain only :)
(:  get the definitions for the domains (ItemGroupDefs in define.xml) :)
let $datasets := (
    if($datasetname and $datasetname != 'ALL') then $definedoc//odm:ItemGroupDef[@Domain=$datasetname or starts-with(@Name,$datasetname)]
    else doc(concat($base,$define))//odm:ItemGroupDef
)
(: get the SDTM version - uses define.xml 2.0 
TODO: using define.xml 2.1 :)
let $sendigversion := (
	if($defineversion='2.1') then $definedoc//odm:MetaDataVersion[1]/def21:standards/def21:standard[@Type='IG' and @Name='SDTMIG']/@Version
	else $definedoc//odm:MetaDataVersion[1]/@def:StandardVersion
)
(: we need to translate the SDTM-IG version in what the CDISC library understands :)
let $sendigversion := translate($sendigversion,'.','-')
(: iterate over all datasets mentioned in the define.xml :)
for $itemgroup in $datasets
    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 $defclass := (
		if($defineversion = '2.1') then $itemgroup/def21:Class/@Name
		else $itemgroup/@def:Class
	)
    let $domain := (
    	if ($itemgroup/@domain) then $itemgroup/@domain
		else substring($itemgroup/@Name,1,2)
    )
    (: Unfortunately, the way of writing the class name (casing)
    has not always been consistent within CDISC :)
    let $class := (
    	if(upper-case($defclass)= 'FINDINGS') then 'Findings'
		else if (upper-case($defclass)= 'EVENTS') then 'Events'
        else if (upper-case($defclass)= 'INTERVENTIONS') then 'Interventions'
        else if (starts-with(upper-case($defclass),'TRIAL')) then 'TrialDesign'
        else if (ends-with(upper-case($defclass),'ABOUT')) then 'FindingsAbout'
        else if (starts-with(upper-case($defclass),'SPECIAL')) then 'SpecialPurpose'
        else if (starts-with(upper-case($defclass),'RELAT')) then 'Relationship'
        else 'GeneralObservations'
    )
    let $domain := ( 
    	if($itemgroup/@Domain) then $itemgroup/@Domain
		else $dsname
    )
    let $cdisclibraryquerysdtmig := concat($cdisclibrarybase,'sendig/',$sendigversion,'/classes/',$class)
    (: now run the CDISC Library query
        as it requires basic authenticatio, 
        we need to use the EXPath 'http-client' extension :)
    let $cdisclibrarysdtmigresponse := http:send-request(<http:request method='get' username='{$username}' password='{$password}' auth-method='Basic'/>, $cdisclibraryquerysdtmig)
    (: make an inventory of the variables that are required using the web service for this domain and SDS version :)
    let $requiredvars := $cdisclibrarysdtmigresponse/json/datasets/*[name=$domain]/datasetVariables/*[core='Req']/name/text()
    (: iterate over the required variables for this dataset,
    and then check whether the variable is present :)    
    let $datasetpath := concat($base,$dataset)
    let $datasetdoc := doc($datasetpath)  
    (: Iterate over the required variables :)
    for $requiredvar in $requiredvars 
    	(: get the OID, if defined for this dataset :)
    	let $requiredoid := (
        	for $a in $itemgroup/odm:ItemRef/@ItemOID
        	where $requiredvar = $definedoc//odm:ItemDef[@OID=$a]/@Name
        	return $a
        )
        (: Required variable is not defined for this dataset :)
    	where not($requiredoid)
        return <error rule="SE0056" rulelastupdate="2020-06-25" dataset="{data($dsname)}" variable="{data($requiredvar)}">SENDIG Required variable '{data($requiredvar)}' is not present in dataset '{data($dsname)}'</error>
]]>
</rulexquery>
</sdsrule>

<sdsrule id="SE0057" last-update="2020-06-05" originator="FDA" standard="SEND" requiresDomainOrDataset="Yes" webservice="Uses CDISC Library RESTful Web Service">
<ruledescription>Variables described in SEND IG as Expected must be included in the dataset</ruledescription>
<igversion>3.0</igversion>
<igversion>3.1</igversion>
<domain>ALL</domain>
<rulexquery><![CDATA[	
(: Rule SE0057: Variables described in SEND IG as Expeced must be included in the dataset :)
(: The CDISC Library web service is used to retrieve the required variables for each domain/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;
declare variable $username external;
declare variable $password external;
(: let $base := 'SEND_3_0_PDS2014/' :)
(: let $define := 'define2-0-0_DS.xml' :)
(: let $datasetname := 'LB' :)
(: let $username := 'xxxx' :)
(: let $password := 'yyyy' :)
let $cdisclibrarybase := 'https://library.cdisc.org/api/mdr/'
let $definedoc := doc(concat($base,$define))
(: EITHER provide $datasetname=:'ALL', meaning: validate for all datasets referenced from the define.xml OR:
$datasetname:='XX' where XX is a specific dataset, MEANING validate for a single dataset or domain only :)
(:  get the definitions for the domains (ItemGroupDefs in define.xml) :)
let $datasets := (
    if($datasetname != 'ALL') then $definedoc//odm:ItemGroupDef[@Domain=$datasetname or starts-with(@Name,$datasetname)]
    else $definedoc//odm:ItemGroupDef
)
(: the define.xml document itself :)
let $definedoc := $definedoc
(: get the SDTM version - uses define.xml 2.0 
TODO: using define.xml 2.1 :)
let $sendigversion := $definedoc//odm:MetaDataVersion[1]/@def:StandardVersion
(: we need to translate the SDTM-IG version in what the CDISC library understands :)
let $sendigversion := translate($sendigversion,'.','-')
(: iterate over all datasets mentioned in the define.xml :)
for $itemgroup in $datasets
    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 $defclass := (
		if($defineversion = '2.1') then $itemgroup/def21:Class/@Name
		else $itemgroup/@def:Class
	)
    let $domain := (
    	if ($itemgroup/@domain) then $itemgroup/@domain
		else substring($itemgroup/@Name,1,2)
    )
    (: Unfortunately, the way of writing the class name (casing)
    has not always been consistent within CDISC :)
    let $class := (
    	if(upper-case($defclass)= 'FINDINGS') then 'Findings'
		else if (upper-case($defclass)= 'EVENTS') then 'Events'
        else if (upper-case($defclass)= 'INTERVENTIONS') then 'Interventions'
        else if (starts-with(upper-case($defclass),'TRIAL')) then 'TrialDesign'
        else if (ends-with(upper-case($defclass),'ABOUT')) then 'FindingsAbout'
        else if (starts-with(upper-case($defclass),'SPECIAL')) then 'SpecialPurpose'
        else if (starts-with(upper-case($defclass),'RELAT')) then 'Relationship'
        else 'GeneralObservations'
    )
    let $domain := ( 
    	if($itemgroup/@Domain) then $itemgroup/@Domain
		else $dsname
    )
    let $cdisclibraryquerysdtmig := concat($cdisclibrarybase,'sendig/',$sendigversion,'/classes/',$class)
    (: now run the CDISC Library query
        as it requires basic authenticatio, 
        we need to use the EXPath 'http-client' extension :)
    let $cdisclibrarysdtmigresponse := http:send-request(<http:request method='get' username='{$username}' password='{$password}' auth-method='Basic'/>, $cdisclibraryquerysdtmig)
    (: make an inventory of the variables that are required using the web service for this domain and SDS version :)
    let $expectedvars := $cdisclibrarysdtmigresponse/json/datasets/*[name=$domain]/datasetVariables/*[core='Exp']/name/text()
    (: iterate over the expected variables for this dataset,
    and then check whether the variable is present :)    
    let $datasetpath := concat($base,$dataset)
    let $datasetdoc := doc($datasetpath)  
    (: Iterate over the expected variables :)
    for $expectedvar in $expectedvars 
    	(: get the OID, if defined for this dataset :)
    	let $expectedvaroid := (
        	for $a in $itemgroup/odm:ItemRef/@ItemOID
        	where $expectedvar = $definedoc//odm:ItemDef[@OID=$a]/@Name
        	return $a
        )
        (: Expected variable is not defined for this dataset :)
    	where not($expectedvaroid)
        return <error rule="SE0057" rulelastupdate="2020-06-25" dataset="{data($dsname)}" variable="{data($expectedvar)}">SENDIG Expected variable '{data($expectedvar)}' is not present in dataset '{data($dsname)}'</error>
]]>
</rulexquery>
</sdsrule>

<sdsrule id="SD1320-SD" last-update="2019-08-16" originator="FDA" standard="SEND" 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.0</igversion>
<igversion>3.1</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 $datasetdoc := (
		if($dsname) then doc(concat($base,$dsname))
		else ()
	)
    (: 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 $datasetdoc//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="2019-08-16" 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="2019-08-16" originator="FDA" standard="SEND">
<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.0</igversion>
<igversion>3.1</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 $datasetlocation := concat($base,$datasetname)
    (: 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 doc($datasetlocation)//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="2019-08-16" 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="2019-08-16" originator="FDA" standard="SEND" 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.0</igversion>
<igversion>3.1</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="2019-08-16" 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="SD1212" last-update="2019-08-16" originator="FDA" standard="SEND">
<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.0</igversion>
<igversion>3.1</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="2019-08-16" 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="2019-08-16" originator="FDA" standard="SEND" 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.0</igversion>
<igversion>3.1</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' :)
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 ()
	)
    (: 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="2019-08-16" 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="2019-08-16" originator="FDA" standard="SEND">
<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.0</igversion>
<igversion>3.1</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="2019-08-16" 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="2019-08-16" originator="FDA" standard="SEND" 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.0</igversion>
<igversion>3.1</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="2019-08-16" 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="2019-08-16" originator="FDA" standard="SEND">
<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.0</igversion>
<igversion>3.1</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 $dsdatasetdoc := (
	if($dsdatasetname) then doc(concat($base,$dsdatasetname))
	else ()
)
(: 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 $exdatasetdoc := (
	if($exdatasetname) then doc(concat($base,$exdatasetname))
	else ()
)
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 $exdatasetdoc//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($dsdatasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$dsusubjoid and @Value=$exusubjidvalue]]/odm:ItemData[@ItemOID=$dsstdtcoid]/@Value) :)
    let $dssdtcvalues := $dsdatasetdoc//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="2019-08-16" 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="2019-08-16" originator="FDA" standard="SEND">
<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.0</igversion>
<igversion>3.1</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="2019-08-16" recordnumber="{data($recnum)}">{data($stdyname)}={data($stdyvalue)} is after {data($endyname)}={data($endyvalue)}</error>
]]></rulexquery>
</sdsrule>

<sdsrule id="SD0012-SD" last-update="2019-08-16" originator="FDA" standard="SEND" 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.0</igversion>
<igversion>3.1</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="2019-08-16" recordnumber="{data($recnum)}">{data($stdyname)}={data($stdyvalue)} is after {data($endyname)}={data($endyvalue)}</error>
]]></rulexquery>
</sdsrule>

<sdsrule id="SD0013" last-update="2019-08-16" originator="FDA" standard="SEND">
<igversion>3.0</igversion>
<igversion>3.1</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="2019-08-16" 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="2019-08-16" originator="FDA" standard="SEND" requiresDomainOrDataset="Yes">
<igversion>3.0</igversion>
<igversion>3.1</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 $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 --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)
            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="2019-08-16" 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="2019-08-16" originator="FDA" standard="SEND">
<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.0</igversion>
<igversion>3.1</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="2019-08-16" 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="2019-09-22" originator="FDA" standard="SEND" 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.0</igversion>
<igversion>3.1</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 $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="2019-09-22" 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="2019-08-16" originator="FDA" standard="SEND">
<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.0</igversion>
<igversion>3.1</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 := $definedoc//odm:ItemGroupDef[@Name='DM']
let $dmdatasetname := (
	if($defineversion='2.1') then $dmdataset/def21:leaf/@xlink:href
	else $dmdataset/def:leaf/@xlink:href
)
let $dmdatasetdoc := (
	if($dmdatasetname) then doc(concat($base,$dmdatasetname))
	else ()
)
(: 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 $dmdatasetdoc//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="2019-08-16" 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="2019-08-16" originator="FDA" standard="SEND">
<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.0</igversion>
<igversion>3.1</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="2019-08-16">Missing {data($stdyname)} variable in dataset {data($datasetname)}, when {data($stdtcname)} variable is present</error>	
]]></rulexquery>
</sdsrule>

<sdsrule id="SD1091" last-update="2019-08-16" originator="FDA" standard="SEND">
<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.0</igversion>
<igversion>3.1</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="2019-08-16">Missing {data($endyname)} variable in dataset {data($datasetname)}, when {data($endtcname)} variable is present</error>
]]></rulexquery>
</sdsrule>

<sdsrule id="SD1206" last-update="2020-06-25" originator="FDA" standard="SEND">
<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</igversion>
<domain>EX</domain>
<rulexquery><![CDATA[
(: Rule SD1206: EXSTDTC date is after RFXENDTC - 
only applies to SENDIG v.3.1, not to 3.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 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 := (
	if($dmlocation) then doc(concat($base,$dmlocation))
	else ()
)
(: 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-06-25" recordnumber="{data($recnum)}">EXSTDTC={data($exstdtcvalue)} is after RFXENDTC={data($rfxendtcvalue)} in dataset {data($name)}</error>	
]]>
</rulexquery>
</sdsrule>

<sdsrule id="SD1207" last-update="2020-06-25" originator="FDA" standard="SEND">
<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</igversion>
<domain>EX</domain>
<rulexquery><![CDATA[
(: Rule SD1207: EXENDTC date is after RFXENDTC - 
Only applies to SENDIG 3.1 and not to 3.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 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 := (
	if($dmlocation) then doc(concat($base,$dmlocation))
	else ()
)
(: 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
	)
	let $datasetdoc := (
		if($datasetlocation) then doc(concat($base,$datasetlocation))
		else ()
	)
    (: 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 $datasetdoc//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-06-25" recordnumber="{data($recnum)}">EXENDTC={data($exendtcvalue)} is after RFXENDTC={data($rfxendtcvalue)} in dataset {data($name)}</error>	
]]>
</rulexquery>
</sdsrule>

<sdsrule id="SD1208" last-update="2020-06-25" originator="FDA" standard="SEND">
<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</igversion>
<domain>DM</domain>
<rulexquery><![CDATA[
(: Rule SD1208: RFXSTDTC date is after RFXENDTC (DM) - 
Only applies to SENDIG 3.1, not to 3.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 and the OID of DM.RFXENDTC (Date/Time of Last Study Treatment)  :)
let $dmdef := doc(concat($base,$define))//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-06-25" recordnumber="{data($recnum)}">RFXSTDTC={data($rfxstdtcvalue)} is after RFXENDTC={data($rfxendtcvalue)} in dataset DM</error>	
]]>
</rulexquery>
</sdsrule>

<sdsrule id="SD1063" last-update="2019-08-17" originator="FDA" standard="SEND">
<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.0</igversion>
<igversion>3.1</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' :)
(: iterate over all the files or documents in the directory or collection :)
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(doc(concat($base,$define))//odm:ItemGroupDef/def:leaf[@xlink:href=$filename])
    return <warning rule="SD1063" rulelastupdate="2019-08-17">Dataset {data($filename)} is not present in define file {data($define)} </warning>    
]]></rulexquery>
</sdsrule>

<sdsrule id="SD0061" last-update="2019-08-17" originator="FDA" standard="SEND">
<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.0</igversion>
<igversion>3.1</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))
for $filename in $definedoc//odm:ItemGroupDef/def:leaf/@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="2019-08-17">Dataset {data($filename)} referenced in {$define} but dataset is missing in submission</warning>
]]></rulexquery>
</sdsrule>

<sdsrule id="SD0054" last-update="2019-08-17" originator="FDA" standard="SEND">
<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.0</igversion>
<igversion>3.1</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="2019-08-17">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="2019-08-17" originator="FDA" standard="SEND">
<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.0</igversion>
<igversion>3.1</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 := doc(concat($base,$datasetlocation))
    (: 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="2019-08-17">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="2019-08-17" originator="FDA" standard="SEND" 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.0</igversion>
<igversion>3.1</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 $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 :)
let $definedoc := doc(concat($base,$define))
(:  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 $dataset/def21:leaf/@xlink:href
		else $dataset/def:leaf/@xlink:href
	)
	let $datasetdoc := (
		if($datasetlocation) then doc(concat($base,$datasetlocation))
		else ()
	)
    for $record in $datasetdoc//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="2019-08-17" 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="2019-08-17" originator="FDA" standard="SEND" 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.0</igversion>
<igversion>3.1</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="2019-08-17">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="2019-08-17" originator="FDA" requiresDomainOrDataset="Yes" standard="SEND">
<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.0</igversion>
<igversion>3.1</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="2019-08-17">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="2019-08-17" originator="FDA" requiresDomainOrDataset="Yes" standard="SEND" 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.0</igversion>
<igversion>3.1</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 := (
		if($datasetlocation) then doc(concat($base,$datasetlocation))
		else ()
	)
    (: 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 := (
        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 := $definedoc//odm:ItemDef[@OID=$valuelistvar]/def:ValueListRef/@ValueListOID
        let $valuelist := $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 := $valuelistitemref/def:WhereClauseRef/@WhereClauseOID
            (: and the corresponding WhereClauseDef :)
            let $whereclausedef := $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 := $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="2019-08-17">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="2019-08-17" originator="FDA" requiresDomainOrDataset="Yes" standard="SEND">
<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.0</igversion>
<igversion>3.1</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 := (
		if($datasetlocation) then doc(concat($base,$datasetlocation))
		else ()
	)
    (: 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 := (
        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 := $definedoc//odm:ItemDef[@OID=$valuelistvar]/def:ValueListRef/@ValueListOID
        let $valuelist := $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 := $valuelistitemref/def:WhereClauseRef/@WhereClauseOID
            (: and the corresponding WhereClauseDef :)
            let $whereclausedef := $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 := $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="2019-08-17">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="2019-08-17" originator="FDA" requiresDomainOrDataset="Yes" standard="SEND">
<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.0</igversion>
<igversion>3.1</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 := (
		if($datasetlocation) then doc(concat($base,$datasetlocation))
		else ()
	)
    (: 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 := (
        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 := $definedoc//odm:ItemDef[@OID=$valuelistvar]/def:ValueListRef/@ValueListOID
        let $valuelist := $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 := $valuelistitemref/def:WhereClauseRef/@WhereClauseOID
            (: and the corresponding WhereClauseDef :)
            let $whereclausedef := $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 := $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="2019-08-17">Variable {data($valuelistvarname)} valuelist value '{data($itemvalue)}' is compatible with its data type '{data($datatype)}'</error>
]]>
</rulexquery>
</sdsrule>

<sdsrule id="SD1231-SD" last-update="2019-08-17" originator="FDA" requiresDomainOrDataset="Yes" standard="SEND">
<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.0</igversion>
<igversion>3.1</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 := (
		if($datasetlocation) then doc(concat($base,$datasetlocation))
		else ()
	)
    (: 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 := (
        for $a in $definedoc//odm:ItemDef[def:ValueListRef/@ValueListOID]/@OID 
        where $a = $itemgroupdef/odm:ItemRef/@ItemOID 
        return $a 
    )
    (: 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 := $definedoc//odm:ItemDef[@OID=$valuelistvar]/def:ValueListRef/@ValueListOID
        let $valuelist := $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 := $valuelistitemref/def:WhereClauseRef/@WhereClauseOID
            (: and the corresponding WhereClauseDef :)
            let $whereclausedef := $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 := $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="2019-08-17">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="2019-08-17" originator="FDA" standard="SEND">
<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.0</igversion>
<igversion>3.1</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 := (
		if($datasetlocation) then doc(concat($base,$datasetlocation))
		else ()
	)
    (: 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="2019-08-16" 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="2019-08-17" originator="FDA" standard="SEND" 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.0</igversion>
<igversion>3.1</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 := (
		if($datasetlocation) then doc(concat($base,$datasetlocation))
		else ()
	)
    (: 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="2019-08-16" recordnumber="{data($recnum)}">{data($tptrefname)} is not populated although {data($rftdtcname)} is populated</warning>	
]]>
</rulexquery>
</sdsrule>

<sdsrule id="SD0024" last-update="2019-08-17" originator="FDA" standard="SEND">
<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.0</igversion>
<igversion>3.1</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="2019-08-17" 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="2019-08-17" originator="FDA" standard="SEND" 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.0</igversion>
<igversion>3.1</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="2019-08-17" recordnumber="{data($recnum)}">Missing value for {data($dtcname)}, when {data($endtcname)} is provided, value={data($endtcvalue)}</warning>	
]]></rulexquery>
</sdsrule>

<sdsrule id="SD1209" last-update="2026-06-25" originator="FDA" standard="SEND" 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</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. 
Only applies to SENDIG 3.1, not to 3.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 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-06-25" recordnumber="{data($recnum)}" variable="RFXENDTC">RFXENDTC is not populated although RFXSTDTC is populated</warning>	
]]>
</rulexquery>
</sdsrule>

<!-- 2019-10-15 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="2019-010-15" originator="FDA" standard="SEND">
<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.0</igversion>
<igversion>3.1</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 $datasetdoc := (
		if($datasetname) then doc(concat($base,$datasetname))
		else ()
	)
	(: 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 $datasetdoc[$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="2019-10-15" recordnumber="{data($recnum)}">Invalid (non-ISO8601 duration) value='{data($value)}'</error>
]]>
</rulexquery>
</sdsrule>

<sdsrule id="SD0028" last-update="2019-08-18" originator="FDA" standard="SEND">
<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.0</igversion>
<igversion>3.1</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="2019-08-18" 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="2019-08-18" originator="FDA" standard="SEND" 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.0</igversion>
<igversion>3.1</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="2019-08-18" 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="2019-05-18" originator="FDA" standard="SEND">
<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.0</igversion>
<igversion>3.1</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' :)
(: iterate over all datasets that have a --DOSE variable - Limit to @def:Class=INTERVENTIONS ? :)
let $definedoc := doc(concat($base,$define))
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 ()
	)
    (: 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 $datasetdoc//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="2019-05-18" 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="2019-05-18" originator="FDA" standard="SEND" 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.0</igversion>
<igversion>3.1</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 $datasetdoc := (
		if($dsname) then doc(concat($base,$dsname))
		else ()
	)
    (: 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 $datasetdoc//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="2019-05-18" recordnumber="{data($recnum)}">Negative value for {data($dosename)} in dataset {data($datasetname)}: {data($dosevalue)} was found</error>
]]></rulexquery>
</sdsrule>

<!-- Remark Rule SD0015: 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="2019-08-18" originator="FDA" standard="SEND">
<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.0</igversion>
<igversion>3.1</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 @Name='TE' 
		or upper-case(./def21:Class/@Name)='INTERVENTIONS' or upper-case(./def21:Class/@Name)='EVENTS' or upper-case(./def21:Class/@Name)='FINDINGS'
		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="2019-08-18" recordnumber="{data($recnum)}">Negative value for {data($durname)} in dataset {data($datasetname)}: {data($durvalue)} was found</error>	
]]></rulexquery>
</sdsrule>

<!-- Rule SD0015: 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="2019-08-18" originator="FDA" standard="SEND" 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.0</igversion>
<igversion>3.1</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="2019-08-18" recordnumber="{data($recnum)}">Negative value for {data($durname)} in dataset {data($datasetname)}: {data($durvalue)} was found</error>
]]></rulexquery>
</sdsrule>

<sdsrule id="SD0084" last-update="2019-08-18" originator="FDA" standard="SEND">
<ruledescription>The value for AGE may not be negative</ruledescription>
<ruledetaileddescription>The value of Age (AGE) cannot be less than 0</ruledetaileddescription>
<igversion>3.0</igversion>
<igversion>3.1</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 $dmdatasetdoc := (
		if($dmdatasetname) then doc(concat($base,$dmdatasetname))
		else ()
	)
    (: 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 $dmdatasetdoc//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="2015-02-10" 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="2019-08-18" originator="FDA" standard="SEND" 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.0</igversion>
<igversion>3.1</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 := (
		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
        (: 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="2019-08-18" 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="2019-08-18" originator="FDA" standard="SEND" 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.0</igversion>
<igversion>3.1</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 $datasetname := 'LB' :)
let $definedoc := doc(concat($base,$define))
(: 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 $datasetdoc := (
		if($dsname) then doc(concat($base,$dsname))
		else ()
	)
    (: 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 $datasetdoc//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="2019-08-18" 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="2019-08-18" originator="FDA" standard="SEND">
<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.0</igversion>
<igversion>3.1</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="2019-08-21" 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="2019-08-18" originator="FDA" standard="SEND" 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.0</igversion>
<igversion>3.1</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="2019-08-21" 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="2019-08-19" originator="FDA" standard="SEND">
<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.0</igversion>
<igversion>3.1</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)='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 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="2019-08-19" 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="2019-08-19" originator="FDA" standard="SEND" 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.0</igversion>
<igversion>3.1</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)='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 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="2019-08-19" 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="2019-08-19" originator="FDA" standard="SEND">
<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.0</igversion>
<igversion>3.1</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 @Domain='SE' 
		or upper-case(./def21:Class/@Name)='INTERVENTIONS' or upper-case(./def21:Class/@Name)='EVENTS' or upper-case(./def21:Class/@Name)='FINDINGS'
		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 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 := doc(concat($base,$define))//odm:ItemDef[@OID=$enrtptoid]/@Name
    (: iterate over all records that do have a --ENDTC, --ENRF or --ENRTPT data point :)
    for $record in $datasetdoc//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="2019-08-19" 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="2019-08-19" originator="FDA" standard="SEND" 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.0</igversion>
<igversion>3.1</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 @Domain='SE' 
		or upper-case(./def21:Class/@Name)='INTERVENTIONS' or upper-case(./def21:Class/@Name)='EVENTS' or upper-case(./def21:Class/@Name)='FINDINGS'
		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 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 $datasetdoc//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="2019-08-19" 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="2019-08-19" originator="FDA" standard="SEND">
<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.0</igversion>
<igversion>3.1</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="2019-08-19" 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="2019-08-19" originator="FDA" standard="SEND" 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.0</igversion>
<igversion>3.1</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="2019-08-19" 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="2019-08-19" originator="FDA" standard="SEND">
<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.0</igversion>
<igversion>3.1</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="2019-08-19" 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="2019-08-19" originator="FDA" standard="SEND" 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.0</igversion>
<igversion>3.1</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="2019-08-19" recordnumber="{data($recnum)}">Missing value for {data($entptname)}, when {data($enrtptname)} is populated, value = {data($enrtptvalue)}</warning>
]]></rulexquery>
</sdsrule>


<!-- Rule SE2316 is only applicable to SEND -->
<sdsrule id="SE2316" last-update="2020-06-25" originator="FDA" standard="SEND">
<ruledescription>'Control Type' (TCNTRL) record must be populated in Trial Sets (TX) domain</ruledescription>
<ruledetaileddescription>There must be a record with TXPARMCD = TCNTRL</ruledetaileddescription>
<igversion>3.0</igversion>
<igversion>3.1</igversion>
<domain>TS</domain>
<rulexquery><![CDATA[		
(: Rule SE2316: 'Control Type' (TCNTRL) record must be populated in Trial Sets (TX) 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 := 'SEND_3_0_PDS2014/' :)
(: let $define := 'define2-0-0_DS.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 $tsdoc := (
	if($tsdataset) then doc(concat($base,$tsdatasetname))
	else ()
)
(: get the OID of the TSPARMCD variable :)
let $tsparmcdoid := (
    for $a in doc(concat($base,$define))//odm:ItemDef[@Name='TSPARMCD']/@OID 
    where $a = doc(concat($base,$define))//odm:ItemGroupDef[@Name='TS']/odm:ItemRef/@ItemOID
    return $a
)
(: Check whether there is TSPARMCD=TCNTRL record :)
let $tsparmcdvalue := $tsdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$tsparmcdoid and @Value='TCNTRL']]
where not($tsparmcdvalue)
return <error rule="SE2316" dataset="TS" variable="TSPARMCD" rulelastupdate="2020-06-25">No PARAMCD=TCNTRL record was found</error>
]]>
</rulexquery>
</sdsrule>


<sdsrule id="SE2300" last-update="2020-06-25" originator="FDA" standard="SEND">
<ruledescription>'Arm Code' (ARMCD) records must be populated in Trial Sets (TX) domain</ruledescription>
<ruledetaileddescription>There must be a record with TXPARMCD = TCNTRL</ruledetaileddescription>
<igversion>3.0</igversion>
<igversion>3.1</igversion>
<domain>TS</domain>
<rulexquery><![CDATA[		
(: Rule SE2300: 'Arm Code' (ARMCD) records must be populated in Trial Sets (TX) 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 := 'SEND_3_0_PDS2014/' :)
(: let $define := 'define2-0-0_DS.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 $tsdoc := (
	if($tsdataset) then doc(concat($base,$tsdatasetname))
	else ()
)
(: get the OID of the TSPARMCD variable :)
let $tsparmcdoid := (
    for $a in doc(concat($base,$define))//odm:ItemDef[@Name='TSPARMCD']/@OID 
    where $a = doc(concat($base,$define))//odm:ItemGroupDef[@Name='TS']/odm:ItemRef/@ItemOID
    return $a
)
(: Check whether there is TSPARMCD=ARMCD record :)
let $tsparmcdvalue := $tsdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$tsparmcdoid and @Value='ARMCD']]
where not($tsparmcdvalue)
return <error rule="SE2300" dataset="TS" variable="TSPARMCD" rulelastupdate="2020-06-25">No PARAMCD=ARMCD record was found</error>
]]>
</rulexquery>
</sdsrule>


<sdsrule id="SE2301" last-update="2020-06-25" originator="FDA" standard="SEND">
<ruledescription>'Sponsor-Defined Group Code' (SPGRPCD) records must be populated in Trial Sets (TX) domain</ruledescription>
<ruledetaileddescription>There must be a record with TXPARMCD = TCNTRL</ruledetaileddescription>
<igversion>3.0</igversion>
<igversion>3.1</igversion>
<domain>TS</domain>
<rulexquery><![CDATA[		
(: Rule SE2301: 'Sponsor-Defined Group Code' (SPGRPCD) records must be populated in Trial Sets (TX) 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 := 'SEND_3_0_PDS2014/' :)
(: let $define := 'define2-0-0_DS.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 $tsdoc := (
	if($tsdataset) then doc(concat($base,$tsdatasetname))
	else ()
)
(: get the OID of the TSPARMCD variable :)
let $tsparmcdoid := (
    for $a in doc(concat($base,$define))//odm:ItemDef[@Name='TSPARMCD']/@OID 
    where $a = doc(concat($base,$define))//odm:ItemGroupDef[@Name='TS']/odm:ItemRef/@ItemOID
    return $a
)
(: Check whether there is TSPARMCD=SPGRPCD record :)
let $tsparmcdvalue := $tsdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$tsparmcdoid and @Value='SPGRPCD']]
where not($tsparmcdvalue)
return <error rule="SE2301" dataset="TS" variable="TSPARMCD" rulelastupdate="2020-06-25">No PARAMCD=SPGRPCD record was found</error>
]]>
</rulexquery>
</sdsrule>

<sdsrule id="SE2302" last-update="2020-06-25" originator="FDA" standard="SEND">
<ruledescription>'Group Label' (GRPLBL) records must be populated in Trial Sets (TX) domain</ruledescription>
<ruledetaileddescription>There must be a record with TXPARMCD = TCNTRL</ruledetaileddescription>
<igversion>3.0</igversion>
<igversion>3.1</igversion>
<domain>TS</domain>
<rulexquery><![CDATA[		
(: Rule SE2302: 'Group Label' (GRPLBL) records must be populated in Trial Sets (TX) 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 := 'SEND_3_0_PDS2014/' :)
(: let $define := 'define2-0-0_DS.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 $tsdoc := (
	if($tsdataset) then doc(concat($base,$tsdatasetname))
	else ()
)
(: get the OID of the TSPARMCD variable :)
let $tsparmcdoid := (
    for $a in doc(concat($base,$define))//odm:ItemDef[@Name='TSPARMCD']/@OID 
    where $a = doc(concat($base,$define))//odm:ItemGroupDef[@Name='TS']/odm:ItemRef/@ItemOID
    return $a
)
(: Check whether there is TSPARMCD=GRPLBL record :)
let $tsparmcdvalue := $tsdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$tsparmcdoid and @Value='GRPLBL']]
where not($tsparmcdvalue)
return <error rule="SE2302" dataset="TS" variable="TSPARMCD" rulelastupdate="2020-06-25">No PARAMCD=GRPLBL record was found</error>
]]>
</rulexquery>
</sdsrule>

<sdsrule id="SE2303" last-update="2020-06-25" originator="FDA" standard="SEND">
<ruledescription>'Dose Level' (TRTDOS) records must be populated in Trial Sets (TX) domain</ruledescription>
<ruledetaileddescription>There must be a record with TXPARMCD = TCNTRL</ruledetaileddescription>
<igversion>3.0</igversion>
<igversion>3.1</igversion>
<domain>TS</domain>
<rulexquery><![CDATA[		
(: Rule SE2303: 'Dose Level' (TRTDOS) records must be populated in Trial Sets (TX) 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 := 'SEND_3_0_PDS2014/' :)
(: let $define := 'define2-0-0_DS.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 $tsdoc := (
	if($tsdataset) then doc(concat($base,$tsdatasetname))
	else ()
)
(: get the OID of the TSPARMCD variable :)
let $tsparmcdoid := (
    for $a in doc(concat($base,$define))//odm:ItemDef[@Name='TSPARMCD']/@OID 
    where $a = doc(concat($base,$define))//odm:ItemGroupDef[@Name='TS']/odm:ItemRef/@ItemOID
    return $a
)
(: Check whether there is TSPARMCD=TRTDOS record :)
let $tsparmcdvalue := $tsdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$tsparmcdoid and @Value='TRTDOS']]
where not($tsparmcdvalue)
return <error rule="SE2303" dataset="TS" variable="TSPARMCD" rulelastupdate="2020-06-25">No PARAMCD=TRTDOS record was found</error>
]]>
</rulexquery>
</sdsrule>

<sdsrule id="SE2304" last-update="2020-06-25" originator="FDA" standard="SEND">
<ruledescription>'Dose Units' (TRTDOSU) records must be populated in Trial Sets (TX) domain</ruledescription>
<ruledetaileddescription>There must be a record with TXPARMCD = TCNTRL</ruledetaileddescription>
<igversion>3.0</igversion>
<igversion>3.1</igversion>
<domain>TS</domain>
<rulexquery><![CDATA[		
(: Rule SE2304: 'Dose Units' (TRTDOSU) records must be populated in Trial Sets (TX) 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 := 'SEND_3_0_PDS2014/' :)
(: let $define := 'define2-0-0_DS.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 $tsdoc := (
	if($tsdataset) then doc(concat($base,$tsdatasetname))
	else ()
)
(: get the OID of the TSPARMCD variable :)
let $tsparmcdoid := (
    for $a in doc(concat($base,$define))//odm:ItemDef[@Name='TSPARMCD']/@OID 
    where $a = doc(concat($base,$define))//odm:ItemGroupDef[@Name='TS']/odm:ItemRef/@ItemOID
    return $a
)
(: Check whether there is TSPARMCD=TRTDOSU record :)
let $tsparmcdvalue := $tsdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$tsparmcdoid and @Value='TRTDOSU']]
where not($tsparmcdvalue)
return <error rule="SE2304" dataset="TS" variable="TSPARMCD" rulelastupdate="2020-06-25">No PARAMCD=TRTDOSU record was found</error>
]]>
</rulexquery>
</sdsrule>

<!-- Rule SE2305: TODO TODO TODO: the rule as described in the FDA Excel is a mess ...
See Mail from Gitte Frausing - the real expert ...  -->
<!--
<sdsrule id="SE2305" last-update="2020-06-25" originator="FDA" standard="SEND">
<ruledescription>At least one of these variables or sets of variables should be populated: (DM.RFSTDTC and -DTC) or -DY or DSSTDY</ruledescription>
<ruledetaileddescription>Required timing variables for identification of the day on which group summaries (Group Means and Incidences) are calculated</ruledetaileddescription>
<igversion>3.0</igversion>
<igversion>3.1</igversion>
<domain>OM</domain>
<domain>MA</domain>
<domain>MI</domain>
<domain>TF</domain>
<rulexquery><![CDATA[
(: Rule SE2305: At least one of these variables or sets of variables should be populated: (DM.RFSTDTC and -DTC) or -DY or DSSTDY.  
Required timing variables for identification of the day on which group summaries (Group Means and Incidences) are calculated :)
(: I interprete this "rule" as that when there is a -DTC, there must also be a DM.RFSTDTC, 
and when there is a -DY, there must be a DS.DSSTDY, 
all of course for the specific subject :)
(: Best to do it to group per subject,
so that the number of lookups to DM and DS remains limited :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/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 := 'SEND_3_0_PDS2014/' :)
(: let $define := 'define2-0-0_DS.xml' :)
(: the define.xml document itself :)
let $definedoc := doc(concat($base,$define))
(: First get the DM and DS dataset, and get the OID of USUBJID, 
	and of DM.RFSTDTC and DS.DSSTDY :)
let $dmusubjidoid := (
	for $a in $definedoc//odm:ItemDef[@Name='USUBJID']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name='DM']/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
)
let $rfstdtcoid := (
	for $a in $definedoc//odm:ItemDef[@Name='RFSTDTC']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name='DM']/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
)
(: Get the DM and DS documents :)
let $dmloc := (
	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 $dmdoc := doc(concat($base,$dmloc))
let $dsloc := (
	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 $dsdoc := (
	if($dsloc) then doc(concat($base,$dsloc))
    else ()  (: case DS is not present - should not occur :)
)
(: Iterate over the OM, MA, MI, TF domains/datasets :)
for $itemgroupdef in $definedoc//odm:ItemGroupDef[@Domain='OM' or starts-with(@Name,'OM') 
		or @Domain='MA' or starts-with(@Name,'MA')
        or @Domain='MI' or starts-with(@Name,'MI')
        or @Domain='TF' or starts-with(@Name,'TF')]
    let $itemgroupoid := $itemgroupdef/@OID
let $dataset := (
	if($defineversion='2.1') then $itemgroupdef/def21:leaf/@xlink:href
	else $itemgroupdef/def:leaf/@xlink:href
)
    let $name := $itemgroupdef/@Name
    (: get the dataset location and the document itself :)
let $datasetname := (
	if($defineversion='2.1') then $itemgroupdef/def21:leaf/@xlink:href
	else $itemgroupdef/def:leaf/@xlink:href
)
    let $doc := doc(concat($base,$datasetname))
    (: we need the OIDs of -DY and -DTC :)
    let $dyoid := (
    	for $a in $definedoc//odm:ItemDef[ends-with(@Name,'DY') and string-length(@Name)=4]/@OID 
        where $a = $itemgroupdef/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 = $itemgroupdef/odm:ItemRef/@ItemOID
        return $a
    )
    let $dtcname := $definedoc//odm:ItemDef[@OID=$dtcoid]/@Name
    (: we also need the OID of USUBJID :)
    let $usubjidoid := (
    	for $a in $definedoc//odm:ItemDef[@Name='USUBJID']/@OID 
        where $a = $itemgroupdef/odm:ItemRef/@ItemOID
        return $a
    )
    (: group all records in the dataset by USUBJID, 
    this will limit the number of lookups in DM and DS :)
    let $groupedrecords := (
    	for $record in $doc//odm:ItemGroupData
            group by 
            $a := $record/odm:ItemData[@ItemOID=$usubjidoid]/@Value
            return element group {  
                $record
            }
    )
    (: Iterate over the groups - each group has the same values for USUBJID :)
    for $group in $groupedrecords
    	(: each group has a number of ItemGroupData, 
        but we can retrieve the USUBJID from the first record :)
        let $usubjidvalue := $group/odm:ItemGroupData[1]/odm:ItemData[@ItemOID=$usubjidoid]/@Value
        (: look up the value of DM.RFSTDTC and of DS.DSSTDY :)
        let $rfstdtcvalue := $dmdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$dmusubjidoid and @Value=$usubjidvalue]]/odm:ItemData[@ItemOID=$rfstdtcoid]/@Value
        let $dsstdyvalue := $dsdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$dsusubjidoid and @Value=$usubjidvalue]]/odm:ItemData[@ItemOID=$dsstdyoid]/@Value
        (: now iterate over all the records in the group,
        and get the values of -DTC and/of -DY :)
        for $record in $group/odm:ItemGroupData
        	let $recnum := $record/@data:ItemGroupDataSeq
            let $dyvalue := $record/odm:ItemData[@ItemOID=$dyoid]/@Value
            let $dtcvalue := $record/odm:ItemData[@ItemOID=$dtcoid]/@Value
          (: when there is a -DTC, there must also be a DM.RFSTDTC, 
          and when there is a -DY, there must be a DS.DSSTDY  :)
          return
          	if($dyvalue and not($dsstdyvalue)) then
            	<error rule="SE2305" rulelastupdate="2020-06-25" dataset="{data($name)}" recordnumber="{data($recnum)}">Missing DS.DSSTDY for variable {data($dyname)}</error>
            else if($dtcvalue and not($rfstdtcvalue)) then 
            	<error rule="SE2305" rulelastupdate="2020-06-25" dataset="{data($name)}" recordnumber="{data($recnum)}">Missing DM.RFSTDTC for variable {data($dtcname)}</error>
]]>
</rulexquery>
</sdsrule> -->

<!-- Rule SE2306: Something is rotten here... there are no -VISITDY variables, there is only VISITDY. 
And the P21 website speaks about -NOMDY !!! -->
<!--
<sdsrule id="SE2306" last-update="2020-06-25" originator="FDA" standard="SEND">
<ruledescription>At least one of the these variables or sets of variables should be populated: (DM.RFSTDTC and -DTC) or -STDY, or -VISITDY</ruledescription>
<ruledetaileddescription>Required timing variables for identification of the day on which group summaries (Group Means and Incidences) are calculated</ruledetaileddescription>
<igversion>3.0</igversion>
<igversion>3.1</igversion>
<domain>BW</domain>
<domain>CL</domain>
<domain>EG</domain>
<domain>LB</domain>
<domain>PM</domain>
<domain>VS</domain>
<rulexquery><![CDATA[
(: Rule SE2306: At least one of the these variables or sets of variables should be populated: (DM.RFSTDTC and -DTC) or -STDY, or -VISITDY.  
Required timing variables for identification of the day on which group summaries (Group Means and Incidences) are calculated :)
(: Best to do it to group per subject,
so that the number of lookups to DM remains limited :)
(: TODO: P21 website however speaks about -NOMDY !!! :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/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 := 'SEND_3_0_PDS2014/' 
let $define := 'define2-0-0_DS.xml' 
(: the define.xml document itself :)
let $definedoc := doc(concat($base,$define))
(: First get the DM dataset, and get the OID of USUBJID, 
	and of DM.RFSTDTC :)
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
)
(: Get the DM document :)
let $dmloc := (
	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 $dmdoc := doc(concat($base,$dmloc))
(: Iterate over the BW, CL, EG, LB, PM, VS domains/datasets :)
for $itemgroupdef in $definedoc//odm:ItemGroupDef[@Domain='OM' or starts-with(@Name,'OM') 
		or @Domain='BW' or starts-with(@Name,'BW')
        or @Domain='CL' or starts-with(@Name,'CL')
        or @Domain='EG' or starts-with(@Name,'EG')
        or @Domain='LB' or starts-with(@Name,'LB')
        or @Domain='PM' or starts-with(@Name,'PM')
        or @Domain='VS' or starts-with(@Name,'VS') ]
    let $itemgroupoid := $itemgroupdef/@OID
let $dataset := (
	if($defineversion='2.1') then $itemgroupdef/def21:leaf/@xlink:href
	else $itemgroupdef/def:leaf/@xlink:href
)
    let $name := $itemgroupdef/@Name
    (: get the dataset location and the document itself :)
let $datasetname := (
	if($defineversion='2.1') then $itemgroupdef/def21:leaf/@xlink:href
	else $itemgroupdef/def:leaf/@xlink:href
)
    let $doc := doc(concat($base,$datasetname))
    (: we need the OIDs of -STDY and -DTC and VISITDY :)
    (: TODO TODO TODO: it is unclear whether the rule is about -DY or -STDY :)
    let $stdyoid := (
    	for $a in $definedoc//odm:ItemDef[ends-with(@Name,'STDY') and string-length(@Name)=6]/@OID 
        where $a = $itemgroupdef/odm:ItemRef/@ItemOID
        return $a
    )
    let $stdyname := $definedoc//odm:ItemDef[@OID=$stdyoid]/@Name
    let $dtcoid := (
    	for $a in $definedoc//odm:ItemDef[ends-with(@Name,'DTC') and string-length(@Name)=5]/@OID 
        where $a = $itemgroupdef/odm:ItemRef/@ItemOID
        return $a
    )
    let $dtcname := $definedoc//odm:ItemDef[@OID=$dtcoid]/@Name
    let $visitdyoid := (
    	for $a in $definedoc//odm:ItemDef[@Name='VISITDY']/@OID 
        where $a = $itemgroupdef/odm:ItemRef/@ItemOID
        return $a
    )
    (: we also need the OID of USUBJID :)
    let $usubjidoid := (
    	for $a in $definedoc//odm:ItemDef[@Name='USUBJID']/@OID 
        where $a = $itemgroupdef/odm:ItemRef/@ItemOID
        return $a
    )
    (: group all records in the dataset by USUBJID, 
    this will limit the number of lookups in DM and DS :)
    let $groupedrecords := (
    	for $record in $doc//odm:ItemGroupData
            group by 
            $a := $record/odm:ItemData[@ItemOID=$usubjidoid]/@Value
            return element group {  
                $record
            }
    )
    (: Iterate over the groups - each group has the same values for USUBJID :)
    for $group in $groupedrecords
    	(: each group has a number of ItemGroupData, 
        but we can retrieve the USUBJID from the first record :)
        let $usubjidvalue := $group/odm:ItemGroupData[1]/odm:ItemData[@ItemOID=$usubjidoid]/@Value
        (: look up the value of DM.RFSTDTC and of DS.DSSTDY :)
        let $rfstdtcvalue := $dmdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$dmusubjidoid and @Value=$usubjidvalue]]/odm:ItemData[@ItemOID=$rfstdtcoid]/@Value
        (: now iterate over all the records in the group,
        and get the values of -DTC and/of -STDY and/of VISITNUM :)
        for $record in $group/odm:ItemGroupData
        	let $recnum := $record/@data:ItemGroupDataSeq
            (: TODO TODO TODO: it is unclear whether the rule is about -DY or -STDY :)
            let $stdyvalue := $record/odm:ItemData[@ItemOID=$stdyoid]/@Value
			
            let $dtcvalue := $record/odm:ItemData[@ItemOID=$dtcoid]/@Value
          (: when there is a -DTC, there must also be a DM.RFSTDTC, 
          and when there is a -DY, there must be a DS.DSSTDY  :)
          return
		  (:  TODO - see mail Jitte
          	if($dyvalue and not($stdyvalue)) then
            	<error rule="SE2305" rulelastupdate="2020-06-25" dataset="{data($name)}" recordnumber="{data($recnum)}">Missing DS.DSSTDY for variable {data($stdyname)}</error>
            else if($dtcvalue and not($rfstdtcvalue)) then 
			    <error rule="SE2305" rulelastupdate="2020-06-25" dataset="{data($name)}" recordnumber="{data($recnum)}">Missing RFSTDTC for variable {data($stdyname)}</error>
		:)
]]>
</rulexquery>
</sdsrule> -->

<!-- Rules SE2306, SE2307 and SE2308: TODO TODO: do later,
as there are so many discrepancies between the FDA Excel, the P21 website, the P21 reports (rules tabs) and slide 13 of the June 2020 webinar -->

<sdsrule id="SE2311" last-update="2020-06-26" originator="FDA" standard="SEND">
<ruledescription>Set Code (SETCD) values must match entries in the Trial Sets (TX) dataset</ruledescription>
<igversion>3.0</igversion>
<igversion>3.1</igversion>
<domain>DM</domain>
<rulexquery><![CDATA[
(: Rule SE2311: Set Code (SETCD) values should match entries in the Trial Sets (TX) 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 := 'SEND_3_0_PC201708/' :)
(: let $define := 'define.xml' :)
(: the define.xml document itself :)
let $definedoc := doc(concat($base,$define))
(: First get the DM dataset, and get the OID of SETCD :)
let $dmsetcdoid := (
	for $a in $definedoc//odm:ItemDef[@Name='SETCD']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name='DM']/odm:ItemRef/@ItemOID
        return $a
)
(: Get the DM document :)
let $dmloc := (
	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 $dmdoc := doc(concat($base,$dmloc))
(: we need the OID of SETCD in TX :)
let $txsetcdoid := (
	for $a in $definedoc//odm:ItemDef[@Name='SETCD']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name='TX']/odm:ItemRef/@ItemOID
        return $a
)
(: Get the TX document :)
let $txloc := (
	if($defineversion='2.1') then $definedoc//odm:ItemGroupDef[@Name='TX']/def21:leaf/@xlink:href
	else $definedoc//odm:ItemGroupDef[@Name='TX']/def:leaf/@xlink:href
)
let $txdoc := (
	if($txloc) then doc(concat($base,$txloc))
    else () (: TX is not present :)
)
(: get all the unique SETCD values in TX :)
let $txsetcdvalues := distinct-values($txdoc//odm:ItemGroupData/odm:ItemData[@ItemOID=$txsetcdoid]/@Value)
(: Iterate over all the DM records that have SETCD populated :)
for $record in $dmdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$dmsetcdoid]]
	let $recnum := $record/@data:ItemGroupDataSeq
    (: get the value of SETCD :)
    let $dmsetcdvalue := $record/odm:ItemData[@ItemOID=$dmsetcdoid]/@Value
    (: value must be one of the TX.SETCD values :)
    where not(functx:is-value-in-sequence($dmsetcdvalue,$txsetcdvalues))
    return <error rule="SE2311" dataset="DM" variable="SETCD" recordnumber="{data($recnum)}" rulelastupdate="2020-06-26">DM.SETCD value '{data($dmsetcdvalue)}' cannot be found in TX</error>
]]>
</rulexquery>
</sdsrule>

<sdsrule id="SE2317" last-update="2020-06-26" originator="FDA" standard="SEND">
<ruledescription>The value of Parameter Code (TXPARMCD) variable must be unique for each Trial Set (SETCD) within the Trial Sets (TX) domain</ruledescription>
<igversion>3.0</igversion>
<igversion>3.1</igversion>
<domain>TX</domain>
<rulexquery><![CDATA[
(: Rule SE2317: The value of Parameter Code (TXPARMCD) variable must be unique for each Trial Set (SETCD) within the Trial Sets (TX) 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 := 'SEND_3_0_PC201708/' :)
(: let $define := 'define.xml' :)
(: the define.xml document itself :)
let $definedoc := doc(concat($base,$define))
(: we need the OID of SETCD and TXPARMCD in TX :)
let $setcdoid := (
	for $a in $definedoc//odm:ItemDef[@Name='SETCD']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name='TX']/odm:ItemRef/@ItemOID
        return $a
)
let $txparmcdoid := (
	for $a in $definedoc//odm:ItemDef[@Name='TXPARMCD']/@OID 
        where $a = $definedoc//odm:ItemGroupDef[@Name='TX']/odm:ItemRef/@ItemOID
        return $a
)
(: Get the TX document :)
let $txloc := (
	if($defineversion='2.1') then $definedoc//odm:ItemGroupDef[@Name='TX']/def21:leaf/@xlink:href
	else $definedoc//odm:ItemGroupDef[@Name='TX']/def:leaf/@xlink:href
)
let $txdoc := (
	if($txloc) then doc(concat($base,$txloc))
    else () (: TX is not present :)
)
(: Group all records in TX by the value of SETCD and TXPARMCD - 
there may be only 1 record in each group :)
let $groupedrecords := (
	for $record in $txdoc//odm:ItemGroupData
            group by 
            $a := $record/odm:ItemData[@ItemOID=$setcdoid]/@Value,
            $b := $record/odm:ItemData[@ItemOID=$txparmcdoid]/@Value
            return element group {  
                $record
            }
)
(: In each group, all the records have the same value for SETCD and TXPARMCD. 
Count the number of records in each group, 
keep the record numbers for reporting :)
for $group in $groupedrecords
	let $txparmcdvalue := $group/odm:ItemGroupData[1]/odm:ItemData[@ItemOID=$txparmcdoid]/@Value
	let $setcdvalue := $group/odm:ItemGroupData[1]/odm:ItemData[@ItemOID=$setcdoid]/@Value
    let $count := count($group/odm:ItemGroupData)
    let $recnums := $group/odm:ItemGroupData/@data:ItemGroupDataSeq
    (: there may be only 1 record per group :)
    where $count > 1
    return <error rule="SE2317" dataset="TX" variable="TXPARMCD" recordnumber="{data($recnums[last()])}" rulelastupdate="2020-06-26">Duplicate value '{data($txparmcdvalue)}' for records {data($recnums)} for SETCD='{data($setcdvalue)}' in TX</error>
]]>
</rulexquery>
</sdsrule>

<!-- Rule SE1074: "Variables designed only for SDTM human studies must be not included in the SEND dataset".
How do we know this? Can this be done by using the CDISC Library? E.g. by looking into the SDTM Model?
Is there an indication of this there?
E.g. for FETUSID CDISC-Library description contains "Not to be used with human clinical trials". Can we rely on this?
-->

<!-- Rule SE0055: "'Variable Data Types in the dataset should match the variable data types described in SEND IG and SDTM Model".
Remark however that Dataset-XML/Define-XML has more granular datatypes than what is mentioned in the SEND-IG.
Do this using the CDISC-Library by matching XPT datatypes to Define-XML datatypes -->
<sdsrule id="SE0055" last-update="2020-06-26" originator="FDA" standard="SEND" webservice="Uses CDISC Library RESTful Web Service">
<ruledescription>Variable Data Types in the dataset should match the variable data types described in SEND IG and SDTM Model</ruledescription>
<igversion>3.0</igversion>
<igversion>3.1</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;
(: CDISC Library username and password :)
declare variable $username external;
declare variable $password external;
(: let $base := 'SEND_3_0_PC201708/'  :)
(: let $define := 'define.xml' :)
(: let $base := 'SEND_3_0_PDS2014/' :)
(: let $define := 'define2-0-0_DS.xml' :)
(: Uses the CDISC library, e.g. query
https://library.cdisc.org/api/mdr/sdtm/1-7/classes/GeneralObservations/variables/--NOMDY
:)
(: CDISC Library base :)
let $cdisclibrarybase := 'https://library.cdisc.org/api/mdr/'
(: the define.xml document :)
let $definedoc := doc(concat($base,$define))
(: get the SEND-IG version, we need to match it to the SDTM (model) version :)
let $sendigversion := $definedoc//odm:MetaDataVersion/@def:StandardVersion
let $sdtmversion := (
	if($sendigversion = '3.0') then '1-2'
	else if($sendigversion = '3.1') then '1-5'
    else 'unknown'
)
(: Replace dot by dash for SEND-IG version for use in CDISC Library :)
let $sendigversion := translate($sendigversion,'.','-')
(: Order all dataset definitions per def:Class,
so that we need only 1 CDISC-Library call per class :)
let $groupeditemgroupdefs := (
	if($defineversion = '2.1') then 
		for $itemgroup in $definedoc//odm:ItemGroupDef
				group by 
				$a := $itemgroup/def21:Class/@Name
				return element group {  
					$itemgroup
				}
	else 
		for $itemgroup in $definedoc//odm:ItemGroupDef
				group by 
				$a := $itemgroup/@def:Class
				return element group {  
					$itemgroup
				}
)
(: iterate over the groups - 
in each group, all dataset definitions (ItemGroupDef) have the same value for def:Class :)
for $group in $groupeditemgroupdefs
	let $defclass := (
		if($defineversion='2.1') then $group/odm:ItemGroupDef[1]/def21:Class/@Name
		else $group/odm:ItemGroupDef[1]/@def:Class
	)
    (: Unfortunately, the way of writing the class name (casing)
      has not always been consistent within CDISC :)
    let $class := (
        if(upper-case($defclass)= 'FINDINGS') then 'Findings'
        else if (upper-case($defclass)= 'EVENTS') then 'Events'
        else if (upper-case($defclass)= 'INTERVENTIONS') then 'Interventions'
        else if (starts-with(upper-case($defclass),'TRIAL')) then 'TrialDesign'
        else if (ends-with(upper-case($defclass),'ABOUT')) then 'FindingsAbout'
        else if (starts-with(upper-case($defclass),'SPECIAL')) then 'SpecialPurpose'
        else if (starts-with(upper-case($defclass),'RELAT')) then 'Relationship'
        else 'GeneralObservations'
    )
    (: CDISC Library query to the SDTM MODEL :)
    (: let $cdisclibraryquerysdtmmodel := concat($cdisclibrarybase,'sdtm/',$sdtmversion,'/classes/',$class)	:)
    (: now run the CDISC Library query
       as it requires basic authentication, 
       we need to use the EXPath 'http-client' extension :)
    (: let $cdisclibraryresponsesdtmmodel := http:send-request(<http:request method='get' username='{$username}' password='{$password}' auth-method='Basic'/>, $cdisclibraryquerysdtmmodel):)
    (: CDISC Library query to the SEND-IG :)
    let $cdisclibraryquerysendig := concat($cdisclibrarybase,'sendig/',$sendigversion,'/classes/',$class)	
    let $cdisclibraryresponsesendig := http:send-request(<http:request method='get' username='{$username}' password='{$password}' auth-method='Basic'/>, $cdisclibraryquerysendig)
  	(: Iterate over all dataset definitions :)
  	for $itemgroupdef in $group/odm:ItemGroupDef
      (: get name, domain and class :)
      let $name := $itemgroupdef/@Name
      let $domainname := $itemgroupdef/@Domain
      (: iterate over all variables within the domain :)
      for $itemrefoid in $itemgroupdef/odm:ItemRef/@ItemOID
          let $varname := $definedoc//odm:ItemDef[@OID=$itemrefoid]/@Name
          (: we need to take generic variable names into account, 
              e.g. LBDTC goes into --DTC :)
          let $varnamegen := (
              if(starts-with($varname,$domainname)) 
                  then concat('--',substring($varname,3))
              else $varname
          )
          let $definedatatype := $definedoc//odm:ItemDef[@OID=$itemrefoid]/@DataType
          (: look up the name in the CDISC Library response and get the XPT simple datatype :)
          let $simpledatatype := ($cdisclibraryresponsesendig/json//*/simpleDatatype[../name=$varname or ../name=$varnamegen])[1]
          (: When the simple datatype is 'Num', we require Define-XML datatype 'integer', 'float' or 'double' :)
          (: When the simple datatype is 'Char', we require Define-XML datatype NOT to be 'integer', 'float' or 'double' :)
          where ($simpledatatype = 'Num' and not($definedatatype='integer' or $definedatatype='float' or $definedatatype='double'))
              or ($simpledatatype = 'Char' and ($definedatatype='integer' or $definedatatype='float' or $definedatatype='double')) 
          return <error rule="SE0055" dataset="{data($name)}" variable="{data($varname)}" rulelastupdate="2020-06-26">Dataset datatype '{data($definedatatype)}' does not correspond to expected SEND-IG datatype '{data($simpledatatype)}'</error> 
]]>
</rulexquery>
</sdsrule>

<sdsrule id="SE0063" last-update="2020-06-26" originator="FDA" standard="SEND" webservice="Uses CDISC Library RESTful Web Service">
<ruledescription>Variable Label in the dataset should match the variable label described in SEND IG</ruledescription>
<igversion>3.0</igversion>
<igversion>3.1</igversion>
<domain>ALL</domain>
<rulexquery><![CDATA[
(: Rule SE0063: Variable Label in the dataset should match the variable label described in SEND IG :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink"; 
(: "declare variable ... external" allows to pass $base and $define from an external programm :)
declare variable $base external;
declare variable $define external; 
declare variable $defineversion external;
(: CDISC Library username and password :)
declare variable $username external;
declare variable $password external;
(: let $base := 'SEND_3_0_PC201708/'  :)
(: let $define := 'define.xml' :)
(: let $base := 'SEND_3_0_PDS2014/' :)
(: let $define := 'define2-0-0_DS.xml' :)
(: Uses the CDISC library, e.g. query
https://library.cdisc.org/api/mdr/sdtm/1-7/classes/GeneralObservations/variables/--NOMDY
:)
(: CDISC Library base :)
let $cdisclibrarybase := 'https://library.cdisc.org/api/mdr/'
(: the define.xml document :)
let $definedoc := doc(concat($base,$define))
(: get the SEND-IG version, we need to match it to the SDTM (model) version :)
let $sendigversion := $definedoc//odm:MetaDataVersion/@def:StandardVersion
let $sdtmversion := (
	if($sendigversion = '3.0') then '1-2'
	else if($sendigversion = '3.1') then '1-5'
    else 'unknown'
)
(: Replace dot by dash for SEND-IG version for use in CDISC Library :)
let $sendigversion := translate($sendigversion,'.','-')
(: we already do a call to the CDISC Library to get "general" and "timing" variables. 
The latter may always be added, even when not mentioned in the SEND-IG :)
(: e.g.: https://library.cdisc.org/api/mdr/sdtm/1-2/classes/GeneralObservations :)
let $cdisclibrarygeneralobservationsquery := concat($cdisclibrarybase,'sdtm/',$sdtmversion,'/classes/GeneralObservations')
let $cdisclibrarygeneralobservationsresponse := http:send-request(<http:request method='get' username='{$username}' password='{$password}' auth-method='Basic'/>, $cdisclibrarygeneralobservationsquery)
(: Order all dataset definitions per def:Class,
so that we need only 1 CDISC-Library call per class :)
let $groupeditemgroupdefs := (
	if($defineversion = '2.1') then
		for $itemgroup in $definedoc//odm:ItemGroupDef
				group by 
				$a := $itemgroup/def21:Class/@Name
				return element group {  
					$itemgroup
				}
	else 
		for $itemgroup in $definedoc//odm:ItemGroupDef
            group by 
            $a := $itemgroup/@def:Class
            return element group {  
                $itemgroup
            }
)
(: iterate over the groups - 
in each group, all dataset definitions (ItemGroupDef) have the same value for def:Class :)
for $group in $groupeditemgroupdefs
	let $defclass := (
		if($defineversion = '2.1') then $group/odm:ItemGroupDef[1]/def21:Class/@Name
		else $group/odm:ItemGroupDef[1]/@def:Class
	)
    (: Unfortunately, the way of writing the class name (casing)
      has not always been consistent within CDISC :)
    let $class := (
        if(upper-case($defclass)= 'FINDINGS') then 'Findings'
        else if (upper-case($defclass)= 'EVENTS') then 'Events'
        else if (upper-case($defclass)= 'INTERVENTIONS') then 'Interventions'
        else if (starts-with(upper-case($defclass),'TRIAL')) then 'TrialDesign'
        else if (ends-with(upper-case($defclass),'ABOUT')) then 'FindingsAbout'
        else if (starts-with(upper-case($defclass),'SPECIAL')) then 'SpecialPurpose'
        else if (starts-with(upper-case($defclass),'RELAT')) then 'Relationship'
        else 'GeneralObservations'
    )
    (: CDISC Library query to the SDTM MODEL :)
    (: let $cdisclibraryquerysdtmmodel := concat($cdisclibrarybase,'sdtm/',$sdtmversion,'/classes/',$class)	:)
    (: now run the CDISC Library query
       as it requires basic authentication, 
       we need to use the EXPath 'http-client' extension :)
    (: let $cdisclibraryresponsesdtmmodel := http:send-request(<http:request method='get' username='{$username}' password='{$password}' auth-method='Basic'/>, $cdisclibraryquerysdtmmodel):)
    (: CDISC Library query to the SEND-IG :)
    let $cdisclibraryquerysendig := concat($cdisclibrarybase,'sendig/',$sendigversion,'/classes/',$class)	
    let $cdisclibraryresponsesendig := http:send-request(<http:request method='get' username='{$username}' password='{$password}' auth-method='Basic'/>, $cdisclibraryquerysendig)
  	(: Iterate over all dataset definitions :)
  	for $itemgroupdef in $group/odm:ItemGroupDef
      (: get name, domain and class :)
      let $name := $itemgroupdef/@Name
      let $domainname := $itemgroupdef/@Domain
      (: iterate over all variables within the domain :)
      for $itemrefoid in $itemgroupdef/odm:ItemRef/@ItemOID
          let $varname := $definedoc//odm:ItemDef[@OID=$itemrefoid]/@Name
          (: we need to take generic variable names into account, 
              e.g. LBDTC goes into --DTC :)
          let $varnamegen := (
              if(starts-with($varname,$domainname)) 
                  then concat('--',substring($varname,3))
              else $varname
          )
          let $varlabel := ( 
          		if($definedoc//odm:ItemDef[@OID=$itemrefoid]/odm:Description/odm:TranslatedText[1]) then 
          			$definedoc//odm:ItemDef[@OID=$itemrefoid]/odm:Description/odm:TranslatedText[1]
                else $definedoc//odm:ItemDef[@OID=$itemrefoid]/@def:Label
          )
          (: look up the name in the CDISC Library response and get the expected variable label :)
          let $expectedvarlabel := ( 
          		(: first look into the response from the SEND-IG :)
          		if($cdisclibraryresponsesendig/json//*/label[../name=$varname or ../name=$varnamegen]) then 
          			($cdisclibraryresponsesendig/json//*/label[../name=$varname or ../name=$varnamegen])[1] 
            	(: If not found, look in the SDTM model 'GeneralObservations' :)
            	else ($cdisclibrarygeneralobservationsresponse/json//*/label[../name=$varname or ../name=$varnamegen])[1]   
          )
          (: give an error when real and expected label differ :)
          where not($varlabel = $expectedvarlabel)
          return <error rule="SE0063" dataset="{data($name)}" variable="{data($varname)}" rulelastupdate="2020-06-26">Label '{data($varlabel)}' does not correspond to expected SEND-IG label '{data($expectedvarlabel)}'</error> 
]]>
</rulexquery>
</sdsrule>

<sdsrule id="SD2019" last-update="2019-08-19" originator="FDA" standard="SEND">
<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.0</igversion>
<igversion>3.1</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="2019-08-19" recordnumber="{data($recnum)}">Invalid value for AGETXT. '{data($agetxtvalue)}' was found</warning>
]]></rulexquery>
</sdsrule>

<sdsrule id="SE2312" last-update="2020-06-25" originator="FDA" standard="SEND">
<ruledescription>Pool ID (POOLID) values should match entries in the Pool Definition (POOLDEF) dataset</ruledescription>
<igversion>3.0</igversion>
<igversion>3.1</igversion>
<domain>EX</domain>
<domain>CL</domain>
<domain>FW</domain>
<domain>LB</domain>
<domain>PC</domain>
<domain>PP</domain>
<domain>RELREC</domain>
<domain>SUPPQUAL</domain>
<rulexquery><![CDATA[
(: Rule SE2312: Pool ID (POOLID) values should match entries in the Pool Definition (POOLDEF) 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 := 'SEND_3_0_PC201708/'  :)
(: let $define := 'define.xml' :)
(: let $base := 'SEND_3_0_PDS2014/' :)
(: let $define := 'define2-0-0_DS.xml' :)
let $definedoc := doc(concat($base,$define))
(: get all unique POOLID values within POOLDEF :)
let $pooldef := $definedoc//odm:ItemGroupDef[@Name='POOLDEF']
let $pooldefdoc := (
	if($pooldef/def:leaf/@xlink:href) then doc(concat($base,$pooldef/def:leaf/@xlink:href))
    else ()  (: No POOLDEF dataset :)
)
let $poolidoid := (
	for $a in $definedoc//odm:ItemDef[@Name='POOLID']/@OID 
        where $a = $pooldef/odm:ItemRef/@ItemOID
        return $a
)
let $pooldefpoolids := distinct-values($pooldefdoc//odm:ItemGroupData/odm:ItemData[@ItemOID=$poolidoid]/@Value)
(: get all EX, CL, FW, LB, PC, PP, RELREC, SUPP-- datasets :)
for $itemgroupdef in $definedoc//odm:ItemGroupDef[@Domain='EX' or starts-with(@Name,'EX') 
      or @Domain='CL' or starts-with(@Name,'CL') 
      or @Domain='FW' or starts-with(@Name,'FW') 
      or @Domain='LB' or starts-with(@Name,'LB') 
      or @Domain='PC' or starts-with(@Name,'PC') 
      or @Domain='PP' or starts-with(@Name,'PP')
      or @Name='RELREC'
      or starts-with(@Name,'SUPP') ]
	let $name := $itemgroupdef/@Name
    (: get the OID of POOLID in the dataset definition :)
    let $dspoolidoid := (
    	for $a in $definedoc//odm:ItemDef[@Name='POOLID']/@OID 
        where $a = $itemgroupdef/odm:ItemRef/@ItemOID
        return $a
    )
    (: get the dataset location and document :)
	let $dsloc := (
		if($defineversion='2.1') then $itemgroupdef/def21:leaf/@xlink:href
		else $itemgroupdef/def:leaf/@xlink:href
	)
    let $dsdoc := (
		if($dsloc) then doc(concat($base,$dsloc))
		else ()
	)
    (: iterate over all records in the dataset, 
    and get the value of POOLID :)
    for $record in $dsdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$dspoolidoid]]
    	let $recnum := $record/@data:ItemGroupDataSeq
        let $poolidvalue := $record/odm:ItemData[@ItemOID=$dspoolidoid]/@Value
        (: Value must be one of POOLDEF.POOLID :)
    	where not(functx:is-value-in-sequence($poolidvalue,$pooldefpoolids))
        return <error rule="SE2312" dataset="{data($name)}" variable="POOLID" recordnumber="{data($recnum)}" rulelastupdate="2020-06-26">POOLID value '{data($poolidvalue)}' is not found as a POOLDEF.POOLID value</error> 
]]>
</rulexquery>
</sdsrule>



<!-- Rule SD0038 applies to: INTERVENTIONS, EVENTS, FINDINGS, DM, SE, SV -->
<sdsrule id="SD0038" last-update="2019-08-19" originator="FDA" standard="SEND">
<ruledescription>Study Day variables (*DY) value may not equal 0</ruledescription>
<ruledetaileddescription>Study Day variables (*DY) value should not equal 0</ruledetaileddescription>
<igversion>3.0</igversion>
<igversion>3.1</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="2019-08-19" 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="2019-08-19" originator="FDA" standard="SEND" 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.0</igversion>
<igversion>3.1</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(@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 $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="2019-08-19" 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="2019-08-19" originator="FDA" standard="SEND" 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.0</igversion>
<igversion>3.1</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 $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  
)
(: make a temporary construct with USUBJID-RFSTDTC pairs: this will speed up the lookups later :)
let $usubjidrfstdtcpairs := (
    for $record in doc($dmdatasetlocation)//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 2019-08-19: 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="2019-08-19" 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="2019-08-19" originator="FDA" standard="SEND" 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.</igversion>
<igversion>3.1</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 $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  
)
(: make a temporary construct with USUBJID-RFSTDTC pairs: this will speed up the lookups later :)
let $usubjidrfstdtcpairs := (
    for $record in doc($dmdatasetlocation)//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 2019-08-19: 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="2019-08-19" 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="2019-10-16" originator="FDA" standard="SEND">
<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.0</igversion>
<igversion>3.1</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 $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  
)
(: make a list of all USUBJID values for which RFSTDTC has a complete date part :)
let $usubjidcompleterfstdtc := (
    for $record in doc($dmdatasetlocation)//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="2019-09-16" 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="2019-10-16" originator="FDA" standard="SEND" 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.0</igversion>
<igversion>3.1</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 $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  
)
(: make a list of all USUBJID values for which RFSTDTC has a complete date part :)
let $usubjidcompleterfstdtc := (
    for $record in doc($dmdatasetlocation)//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="2019-09-16" 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 2019-08-19  -->
<!-- applies to INTERVENTIONS, EVENTS, FINDINGS, CO, SE, SV -->
<sdsrule id="SD1085" last-update="2019-08-19" originator="FDA" standard="SEND">
<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.0</igversion>
<igversion>3.1</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 $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  
)
(: make a list of all USUBJID values for which RFSTDTC is not a complete date :)
let $usubjidincompleterfstdtc := (
    for $record in doc($dmdatasetlocation)//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)='FINDINGS' or upper-case(./def21:Class/@Name)='FINDINGS' 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="2019-08-19" 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 2019-08-19  -->
<sdsrule id="SD1085-SD" last-update="2019-08-19" originator="FDA" standard="SEND" 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.0</igversion>
<igversion>3.1</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 $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  
)
(: make a list of all USUBJID values for which RFSTDTC is not a complete date :)
let $usubjidincompleterfstdtc := (
    for $record in doc($dmdatasetlocation)//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="2019-08-19" 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="2019-08-19" originator="FDA" standard="SEND">
<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.0</igversion>
<igversion>3.1</igversion>
<domain>INTERVENTIONS</domain>
<domain>EVENTS</domain>
<domain>FINDINGS</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="2019-08-19" 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="2019-08-19" originator="FDA" standard="SEND" 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>INTERVENTIONS</domain>
<domain>EVENTS</domain>
<domain>FINDINGS</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="2019-08-19" 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 2019-08-19  -->

<sdsrule id="SD1015" last-update="2019-08-19" originator="FDA" standard="SEND">
<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.0</igversion>
<igversion>3.1</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 := 'SEND_3_0_PDS2014/' :)
(: let $define := 'define2-0-0_DS.xml' :)
(: let $base := 'SEND_3_0_PC201708/' :)
(: let $define := 'define.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
)
(: and get the document itself :)
let $tadoc := (
	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 2019-08-19: make a list of all EPOCH values in TA :)
let $epochvalues := (
    for $epoch in $tadoc//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  (: ensure that TE-EPOCH has been defined :)
    (: 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 doc(concat($base,$define))//odm:ItemDef[@Name='EPOCH']/@OID 
        where $a = doc(concat($base,$define))//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 2019-08-19: compare with the list of EPOCH values in TA :)
        (: where $taepochoid and $datasetepochoid and not(functx:is-value-in-sequence($epochvalue, $epochvalues)) :)
        return <error rule="SD1015" dataset="{data($name)}" variable="EPOCH" rulelastupdate="2015-02-10" 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 2019-08-19  -->
<sdsrule id="SD1015-SD" last-update="2019-08-19" originator="FDA" standard="SEND" 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.0</igversion>
<igversion>3.1</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 := 'SEND_3_0_PDS2014/' :)
(: let $define := 'define2-0-0_DS.xml' :)
(: let $base := 'SEND_3_0_PC201708/' :)
(: let $define := 'define.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
)
(: and get the document itself :)
let $tadoc := (
	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 2019-08-19: make a list of all EPOCH values in TA :)
let $epochvalues := (
    for $epoch in $tadoc//odm:ItemGroupData/odm:ItemData[@ItemOID=$taepochoid]/@Value
    return $epoch
)
(: now iterate over all other datasets :)
let $datasets := $definedoc//odm:ItemGroupDef[@Name = $datasetname and not(@Name='TA')]
for $dataset in $datasets  (: ensure that TE-EPOCH has been defined :)
    (: get name and location :)
    let $name := $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 ()
	)
    (: and the OID of EPOCH :)
    let $datasetepochoid := (
        for $a in doc(concat($base,$define))//odm:ItemDef[@Name='EPOCH']/@OID 
        where $a = doc(concat($base,$define))//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 2019-08-19: compare with the list of EPOCH values in TA :)
        (: where $taepochoid and $datasetepochoid and not(functx:is-value-in-sequence($epochvalue, $epochvalues)) :)
        return <error rule="SD1015" dataset="{data($name)}" variable="EPOCH" rulelastupdate="2015-02-10" 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>


<sdsrule id="SD0058" last-update="2020-06-28" originator="FDA" standard="SEND" webservice="Uses CDISC Library RESTful Web Service">
<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.0</igversion>
<igversion>3.1</igversion>
<domain>ALL</domain>
<rulexquery><![CDATA[
(: Rule SD0058 - Variable appears in dataset, but is not in SDTM model :)
(: CDISC Library API Implementation :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/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;
(: CDISC Library username and password :)
declare variable $username external;
declare variable $password 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)
} ;
(: let $base := 'SEND_3_0_PC201708/'  :)
(: let $define := 'define.xml' :)
let $definedoc := doc(concat($base,$define))
(: Translate the SEND-IG version to an SDTM MODEL version :)
let $sendigversion := $definedoc//odm:MetaDataVersion/@def:StandardVersion
let $sdtmversion := (
	if($sendigversion = '3.0') then '1-2'
	else if($sendigversion = '3.1') then '1-5'
    else 'unknown'
)
(: CDISC-Library base :)
let $cdisclibrarybase := 'https://library.cdisc.org/api/mdr/'
(: In order to avoid too many calls to the CDISC Library, 
we already generate a list of all "General Observations" variables :)
(: e.g.: https://library.cdisc.org/api/mdr/sdtm/1-2/classes/GeneralObservations :)
let $cdisclibrarygeneralobservationsquery := concat($cdisclibrarybase,'sdtm/',$sdtmversion,'/classes/GeneralObservations')
let $cdisclibrarygeneralobservationsresponse := http:send-request(<http:request method='get' username='{$username}' password='{$password}' auth-method='Basic'/>, $cdisclibrarygeneralobservationsquery)
(: In order to minimize the number of calls to the CDISC Library,
we group all dataset definition by def:Class :)
(: Order all dataset definitions per def:Class,
so that we need only 1 CDISC-Library call per class :)
let $groupeditemgroupdefs := (
	if($defineversion = '2.1') then
		for $itemgroup in $definedoc//odm:ItemGroupDef
				group by 
				$a := $itemgroup/def21:Class/@Name
				return element group {  
					$itemgroup
				}
	else 
		for $itemgroup in $definedoc//odm:ItemGroupDef
				group by 
				$a := $itemgroup/@def:Class
				return element group {  
					$itemgroup
				}
)
(: iterate over the groups - 
in each group, all dataset definitions (ItemGroupDef) have the same value for def:Class :)
for $group in $groupeditemgroupdefs
	let $defclass := (
		if($defineversion = '2.1') then
			$group/odm:ItemGroupDef[1]/def21:Class/@Name
		else 
			$group/odm:ItemGroupDef[1]/@def:Class
	)
    (: Unfortunately, the way of writing the class name (casing)
      has not always been consistent within CDISC :)
    let $class := (
        if(upper-case($defclass)= 'FINDINGS') then 'Findings'
        else if (upper-case($defclass)= 'EVENTS') then 'Events'
        else if (upper-case($defclass)= 'INTERVENTIONS') then 'Interventions'
        else if (starts-with(upper-case($defclass),'TRIAL')) then 'TrialDesign'
        else if (ends-with(upper-case($defclass),'ABOUT')) then 'FindingsAbout'
        else if (starts-with(upper-case($defclass),'SPECIAL')) then 'SpecialPurpose'
        else if (starts-with(upper-case($defclass),'RELAT')) then 'Relationship'
        else 'GeneralObservations'
    )
    (: CDISC Library query to the SDTM MODEL :)
    let $cdisclibraryquerysdtmmodel := concat($cdisclibrarybase,'sdtm/',$sdtmversion,'/classes/',$class)
    (: now run the CDISC Library query
       as it requires basic authentication, 
       we need to use the EXPath 'http-client' extension :)
    let $cdisclibraryresponsesdtmmodel := http:send-request(<http:request method='get' username='{$username}' password='{$password}' auth-method='Basic'/>, $cdisclibraryquerysdtmmodel)
    (: iterate over the datasets in the group - each of them has the same def:Class value :)
    for $itemgroupdef in $group/odm:ItemGroupDef
    	(: get the name of the dataset and the domain :)
        let $name := $itemgroupdef/@Name
        let $domainname := $itemgroupdef/@Domain
        (: iterate over all variables within the dataset definition :)
        for $itemrefoid in $itemgroupdef/odm:ItemRef/@ItemOID
        	let $varname := $definedoc//odm:ItemDef[@OID=$itemrefoid]/@Name 
            (: we need to take generic variable names into account, 
              e.g. LBDTC goes into --DTC :)
            let $varnamegen := (
                if(starts-with($varname,$domainname)) 
                    then concat('--',substring($varname,3))
                else $varname
            )
            (: now look up the variable name in the SDTM model for this class :)
            let $cdisclibraryvar := (
            	if($class = 'Interventions' or $class='Events' or $class='Findings') then 
            		$cdisclibraryresponsesdtmmodel/json//classVariables/*[name=$varname or name=$varnamegen]
                else  $cdisclibraryresponsesdtmmodel/json//datasetVariables/*[name=$varname or name=$varnamegen]
            )(: and in the "General Observations Class" :)
            let $cdisclibrarygeneralvar := $cdisclibrarygeneralobservationsresponse/json//classVariables/*[name=$varname or name=$varnamegen]
            let $varnamereport := (
            	if(starts-with($varname,$domainname)) then $varnamegen
                else $varname
            )
            where not($cdisclibraryvar) and not($cdisclibrarygeneralvar)
            return <error rule="SE0058" dataset="{data($name)}" rulelastupdate="2020-06-26">Variable '{data($varnamereport)}' is not in SDTM Model version {data($sdtmversion)}</error>
]]></rulexquery>
</sdsrule>

<sdsrule id="SD0058-SD" last-update="2020-06-26" originator="FDA" standard="SEND" requiresDomainOrDataset="Yes" webservice="Uses CDISC Library RESTful Web Service">
<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.1</igversion>
<igversion>3.2</igversion>
<domain>ALL</domain>
<rulexquery><![CDATA[
(: Rule SD0058 - Variable appears in dataset, but is not in SDTM model :)
(: CDISC Library API Implementation :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/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;
(: CDISC Library username and password :)
declare variable $username external;
declare variable $password 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)
} ;
(: let $base := 'SEND_3_0_PC201708/'  :)
(: let $define := 'define.xml' :)
(: let $datasetname := 'MI' :)
let $definedoc := doc(concat($base,$define))
(: Translate the SEND-IG version to an SDTM MODEL version :)
let $sendigversion := $definedoc//odm:MetaDataVersion/@def:StandardVersion
let $sdtmversion := (
	if($sendigversion = '3.0') then '1-2'
	else if($sendigversion = '3.1') then '1-5'
    else 'unknown'
)
(: CDISC-Library base :)
let $cdisclibrarybase := 'https://library.cdisc.org/api/mdr/'
(: In order to avoid too many calls to the CDISC Library, 
we already generate a list of all "General Observations" variables :)
(: we already do a call to the CDISC Library to get "general" and "timing" variables. 
The latter may always be added, even when not mentioned in the SEND-IG :)
(: e.g.: https://library.cdisc.org/api/mdr/sdtm/1-2/classes/GeneralObservations :)
let $cdisclibrarygeneralobservationsquery := concat($cdisclibrarybase,'sdtm/',$sdtmversion,'/classes/GeneralObservations')
let $cdisclibrarygeneralobservationsresponse := http:send-request(<http:request method='get' username='{$username}' password='{$password}' auth-method='Basic'/>, $cdisclibrarygeneralobservationsquery)
(: In order to minimize the number of calls to the CDISC Library,
we group all dataset definition by def:Class :)
(: Order all dataset definitions per def:Class,
so that we need only 1 CDISC-Library call per class :)
for $itemgroupdef in $definedoc//odm:ItemGroupDef[@Name=$datasetname]
	let $defclass := (
		if($defineversion='2.1') then 
			$itemgroupdef/def21:Class/@Name
		else 
			$itemgroupdef/@def:Class
	)
    (: Unfortunately, the way of writing the class name (casing)
      has not always been consistent within CDISC :)
    let $class := (
        if(upper-case($defclass)= 'FINDINGS') then 'Findings'
        else if (upper-case($defclass)= 'EVENTS') then 'Events'
        else if (upper-case($defclass)= 'INTERVENTIONS') then 'Interventions'
        else if (starts-with(upper-case($defclass),'TRIAL')) then 'TrialDesign'
        else if (ends-with(upper-case($defclass),'ABOUT')) then 'FindingsAbout'
        else if (starts-with(upper-case($defclass),'SPECIAL')) then 'SpecialPurpose'
        else if (starts-with(upper-case($defclass),'RELAT')) then 'Relationship'
        else 'GeneralObservations'
    )
    (: CDISC Library query to the SDTM MODEL :)
    let $cdisclibraryquerysdtmmodel := concat($cdisclibrarybase,'sdtm/',$sdtmversion,'/classes/',$class)
    (: now run the CDISC Library query
       as it requires basic authentication, 
       we need to use the EXPath 'http-client' extension :)
    let $cdisclibraryresponsesdtmmodel := http:send-request(<http:request method='get' username='{$username}' password='{$password}' auth-method='Basic'/>, $cdisclibraryquerysdtmmodel)
    let $name := $itemgroupdef/@Name
    let $domainname := $itemgroupdef/@Domain
    (: iterate over all variables within the dataset definition :)
    	for $itemrefoid in $itemgroupdef/odm:ItemRef/@ItemOID
    let $varname := $definedoc//odm:ItemDef[@OID=$itemrefoid]/@Name 
    	(: we need to take generic variable names into account, 
    	e.g. LBDTC goes into --DTC :)
    	let $varnamegen := (
    		if(starts-with($varname,$domainname)) 
    			then concat('--',substring($varname,3))
    		else $varname
    	)
    (: now look up the variable name in the SDTM model for this class :)
    let $cdisclibraryvar := (
    	if($class = 'Interventions' or $class='Events' or $class='Findings') then 
    		$cdisclibraryresponsesdtmmodel/json//classVariables/*[name=$varname or name=$varnamegen]
    	else  $cdisclibraryresponsesdtmmodel/json//datasetVariables/*[name=$varname or name=$varnamegen]
    )(: and in the "General Observations Class" :)
    let $cdisclibrarygeneralvar := $cdisclibrarygeneralobservationsresponse/json//classVariables/*[name=$varname or name=$varnamegen]
    let $varnamereport := (
    	if(starts-with($varname,$domainname)) then $varnamegen
    	else $varname
    )
    where not($cdisclibraryvar) and not($cdisclibrarygeneralvar)
    return <error rule="SE0058" dataset="{data($name)}" rulelastupdate="2020-06-26">Variable '{data($varnamereport)}' is not in SDTM Model version {data($sdtmversion)}</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 -->


<!-- Rule SD1078: now using the CDISC Library (XML4Pharma WebServices do not have a service for SEND for this -->
<sdsrule id="SD1078" last-update="2020-06-27" originator="FDA" standard="SEND"  webservice="Uses CDISC Library 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.0</igversion>
<igversion>3.1</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. 
Implementation using the CDISC Library :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare 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 $username external;
declare variable $password external;
(: let $base := 'SEND_3_0_PDS2014/' :)
(: let $define := 'define2-0-0_DS.xml' :)
let $definedoc := doc(concat($base,$define))
(: CDISC Library base :)
let $cdisclibrarybase := 'https://library.cdisc.org/api/mdr/'
(: get the SEND-IG version :)
let $sendigversion := $definedoc//odm:MetaDataVersion/@def:StandardVersion
(: Translate the IG version to one the CDISC library understands: 
replace dot by dash :)
let $sendigversion := translate($sendigversion,'.','-') 
(: We will group the datasets by def:Class, 
this to minimize the number of calls to the CDISC Library :)
let $groupeditemgroupdefs := (
	if($defineversion = '2.1') then
		for $itemgroup in $definedoc//odm:ItemGroupDef
				group by 
				$a := $itemgroup/def21:Class/@Name
				return element group {  
					$itemgroup
				}
	else 
		for $itemgroup in $definedoc//odm:ItemGroupDef
			group by 
			$a := $itemgroup/@def:Class
			return element group {  
				$itemgroup
			}
)
(: iterate over all the groups - 
all datasets within the same group have the same value for @def:Class :)
(: iterate over the groups - 
in each group, all dataset definitions (ItemGroupDef) have the same value for def:Class :)
for $group in $groupeditemgroupdefs
	let $defclass := (
		if($defineversion='2.1') then $group/odm:ItemGroupDef[1]/def21:Class/@Name
		else $group/odm:ItemGroupDef[1]/@def:Class
	)
    (: Unfortunately, the way of writing the class name (casing)
      has not always been consistent within CDISC :)
    let $class := (
        if(upper-case($defclass)= 'FINDINGS') then 'Findings'
        else if (upper-case($defclass)= 'EVENTS') then 'Events'
        else if (upper-case($defclass)= 'INTERVENTIONS') then 'Interventions'
        else if (starts-with(upper-case($defclass),'TRIAL')) then 'TrialDesign'
        else if (ends-with(upper-case($defclass),'ABOUT')) then 'FindingsAbout'
        else if (starts-with(upper-case($defclass),'SPECIAL')) then 'SpecialPurpose'
        else if (starts-with(upper-case($defclass),'RELAT')) then 'Relationship'
        else 'GeneralObservations'
    )
    (: Make the CDISC Library call to get all variable information per domain for this class  :)
    let $cdisclibraryquerysendig := concat($cdisclibrarybase,'sendig/',$sendigversion,'/classes/',$class)	
    let $cdisclibraryresponsesendig := http:send-request(<http:request method='get' username='{$username}' password='{$password}' auth-method='Basic'/>, $cdisclibraryquerysendig)
    (: Iterate over all dataset definitions :)
  	for $itemgroupdef in $group/odm:ItemGroupDef
      (: get name, domain and class :)
      let $name := $itemgroupdef/@Name
      let $domainname := (
      	if($itemgroupdef/@Domain) then $itemgroupdef/@Domain
        else if (starts-with($name,'SUPP')) then 'SUPPQUAL'
        else if ($name='POOLDEF') then 'POOLDEF'
        else if ($name='RELREC') then 'RELREC'
        else substring($name,1,2)
      )
      (: get the location of the dataset and the document itself :)
		let $dsloc := (
			if($defineversion='2.1') then $itemgroupdef/def21:leaf/@xlink:href
			else $itemgroupdef/def:leaf/@xlink:href
		)
      let $doc := (
      	if($dsloc) then doc(concat($base,$dsloc))
        else () (: no location defined :)
      )
      (: Iterate over all the variables in the dataset definition :)
      for $itemoid in $itemgroupdef/odm:ItemRef/@ItemOID
        let $varname := $definedoc//odm:ItemDef[@OID=$itemoid]/@Name
        (: Get the "core" property - can be "Req", "Exp" or "Perm" :)
        let $core := ($cdisclibraryresponsesendig/json/datasets/*[name=$domainname]//*[./name=$varname]/core/text())
        (: return <test>{data($domainname)} - {data($varname)} - {$core}</test>  :)
        (: in case a variable is not explicitely mentioned in the SEND-IG), 
        it is always permissible 'Perm' :)
        let $core := (
        	if(not($core)) then 'Perm'
        	else $core
        ) 
        (: count the number of records in the dataset for the "Perm" variables :)
        (: if the variable is NOT 'Perm' we set the count artficially to 1
        so that the system does not need to go over all the records, which saves time :)
        let $count := (
        	if(not($core='Perm')) then 1
        	else count($doc//odm:ItemGroupData/odm:ItemData[@ItemOID=$itemoid and @Value])
        )
        where $core='Perm' and $count=0
        return <warning rule="SD1078" dataset="{data($name)}" variable="{data($varname)}" rulelastupdate="2020-06-27">Permissible variable {data($varname)} with missing value for all records</warning>
]]></rulexquery>
</sdsrule>

<!-- TODO: currently uses the XML4Pharma RESTful web service - move to CDISC-Library RESTful web service.
This also as service CDISCCoreFromSDTMVariable has not been implemented for SEND -->
<sdsrule id="SD1078-SD" last-update="2020-06-26" originator="FDA" standard="SEND" webservice="Uses CDISC Library 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.0</igversion>
<igversion>3.1</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. 
Implementation using the CDISC Library :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/v1.3";
declare namespace data="http://www.cdisc.org/ns/Dataset-XML/v1.0";
declare namespace xlink="http://www.w3.org/1999/xlink";
(: "declare 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 variable $username external;
declare variable $password external;
(: let $base := 'SEND_3_0_PDS2014/' :)
(: let $define := 'define2-0-0_DS.xml' :)
(: let $datasetname := 'DM' :)
let $definedoc := doc(concat($base,$define))
(: CDISC Library base :)
let $cdisclibrarybase := 'https://library.cdisc.org/api/mdr/'
(: get the SEND-IG version :)
let $sendigversion := $definedoc//odm:MetaDataVersion/@def:StandardVersion
(: Translate the IG version to one the CDISC library understands: 
replace dot by dash :)
let $sendigversion := translate($sendigversion,'.','-') 
(: iterate over the provided dataset(s) :)
for $itemgroupdef in $definedoc//odm:ItemGroupDef[@Name=$datasetname]
	let $defclass := (
		if($defineversion='2.1') then $itemgroupdef/def21:Class/@Name
		else $itemgroupdef/@def:Class
	)
    (: Unfortunately, the way of writing the class name (casing)
      has not always been consistent within CDISC :)
    let $class := (
        if(upper-case($defclass)= 'FINDINGS') then 'Findings'
        else if (upper-case($defclass)= 'EVENTS') then 'Events'
        else if (upper-case($defclass)= 'INTERVENTIONS') then 'Interventions'
        else if (starts-with(upper-case($defclass),'TRIAL')) then 'TrialDesign'
        else if (ends-with(upper-case($defclass),'ABOUT')) then 'FindingsAbout'
        else if (starts-with(upper-case($defclass),'SPECIAL')) then 'SpecialPurpose'
        else if (starts-with(upper-case($defclass),'RELAT')) then 'Relationship'
        else 'GeneralObservations'
    )
    (: Make the CDISC Library call to get all variable information per domain for this class  :)
    let $cdisclibraryquerysendig := concat($cdisclibrarybase,'sendig/',$sendigversion,'/classes/',$class)	
    let $cdisclibraryresponsesendig := http:send-request(<http:request method='get' username='{$username}' password='{$password}' auth-method='Basic'/>, $cdisclibraryquerysendig)
      (: get name, domain :)
      let $name := $itemgroupdef/@Name
      let $domainname := (
      	if($itemgroupdef/@Domain) then $itemgroupdef/@Domain
        else if (starts-with($name,'SUPP')) then 'SUPPQUAL'
        else if ($name='POOLDEF') then 'POOLDEF'
        else if ($name='RELREC') then 'RELREC'
        else substring($name,1,2)
      )
      (: get the location of the dataset and the document itself :)
		let $dsloc := (
			if($defineversion='2.1') then $itemgroupdef/def21:leaf/@xlink:href
			else $itemgroupdef/def:leaf/@xlink:href
		)
      let $doc := (
      	if($dsloc) then doc(concat($base,$dsloc))
        else () (: no location defined :)
      )
      (: Iterate over all the variables in the dataset definition :)
      for $itemoid in $itemgroupdef/odm:ItemRef/@ItemOID
        let $varname := $definedoc//odm:ItemDef[@OID=$itemoid]/@Name
        (: Get the "core" property - can be "Req", "Exp" or "Perm" :)
        let $core := ($cdisclibraryresponsesendig/json/datasets/*[name=$domainname]//*[./name=$varname]/core/text())
        (: return <test>{data($domainname)} - {data($varname)} - {$core}</test>  :)
        (: in case a variable is not explicitely mentioned in the SEND-IG), 
        it is always permissible 'Perm' :)
        let $core := (
        	if(not($core)) then 'Perm'
        	else $core
        ) 
        (: count the number of records in the dataset for the "Perm" variables :)
        (: if the variable is NOT 'Perm' we set the count artficially to 1
        so that the system does not need to go over all the records, which saves time :)
        let $count := (
        	if(not($core='Perm')) then 1
        	else count($doc//odm:ItemGroupData/odm:ItemData[@ItemOID=$itemoid and @Value])
        )
        where $core='Perm' and $count=0
        return <warning rule="SD1078" dataset="{data($name)}" variable="{data($varname)}" rulelastupdate="2020-06-27">Permissible variable {data($varname)} with missing value for all records</warning>
]]></rulexquery>
</sdsrule>


<!-- SD0057 is not applicable to SEND -->

<!-- Rule SD1095 applies to INTERVENTIONS, FINDINGS, EVENTS -->
<sdsrule id="SD1095" last-update="2019-08-19" originator="FDA" standard="SEND">
<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.0</igversion>
<igversion>3.1</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)='EVENTS' or upper-case(./def21:Class/@Name)='FINDINGS']
    (: 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="2019-08-19">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="2019-08-19" originator="FDA" standard="SEND">
<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.0</igversion>
<igversion>3.1</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="2019-08-19">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". 
We can implement this once using Define-XML 2.1, as we need to know the version of the CT -->

<sdsrule id="CT2003" last-update="2020-06-26" originator="FDA" standard="SEND">
<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.0</igversion>
<igversion>3.1</igversion>
<domain>ALL</domain>
<rulexquery><![CDATA[
(: Rule CT2003 : 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="SD2003" dataset="{data($name)}" variable="{$testcdname}" recordnumber="{$recnum}" rulelastupdate="2020-06-26">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-06-26" originator="FDA" standard="SEND" 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.0</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="CT2003" dataset="{data($name)}" variable="{$testcdname}" recordnumber="{$recnum}" rulelastupdate="2020-06-26">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 is not applicable to SEND -->

<sdsrule id="SD1041" last-update="2019-08-20" originator="FDA" standard="SEND">
<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.0</igversion>
<igversion>3.1</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="2019-08-20">{data($scatname)} value '{data($catvalue)}' is identical to {data($catname)} value '{data($scatvalue)}'</error>		
]]>
</rulexquery>
</sdsrule>

<sdsrule id="SD1041-SD" last-update="2019-08-20" originator="FDA" standard="SEND" 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.0</igversion>
<igversion>3.1</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="2019-08-20">{data($scatname)} value '{data($catvalue)}' is identical to {data($catname)} value '{data($scatvalue)}'</error>		
]]>
</rulexquery>
</sdsrule>


<!-- 2020-06-26: uses CDISC Library API -->
<sdsrule id="SD0002-SD" last-update="2019-08-20" originator="FDA" standard="SEND" requiresDomainOrDataset="Yes" webservice="Uses CDISC Library 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.0</igversion>
<igversion>3.1</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 :) 
(: Implementation using the CDISC Library - single 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;
declare variable $username external;
declare variable $password external;
(: let $base := 'SEND_3_0_PDS2014/' :)
(: let $define := 'define2-0-0_DS.xml' :)
(: let $datasetname := 'LB' :)
let $definedoc := doc(concat($base,$define))
(: CDISC Library base :)
let $cdisclibrarybase := 'https://library.cdisc.org/api/mdr/'
(: get the SEND-IG version :)
let $sendigversion := $definedoc//odm:MetaDataVersion/@def:StandardVersion
(: Translate the IG version to one the CDISC library understands: 
replace dot by dash :)
let $sendigversion := translate($sendigversion,'.','-') 
(: iterate over the provided dataset(s) :)
for $itemgroupdef in $definedoc//odm:ItemGroupDef[@Name=$datasetname]
	let $defclass := (
		if($defineversion='2.1') then $itemgroupdef/def21:Class/@Name
		else $itemgroupdef/@def:Class
	)
    (: Unfortunately, the way of writing the class name (casing)
      has not always been consistent within CDISC :)
    let $class := (
        if(upper-case($defclass)= 'FINDINGS') then 'Findings'
        else if (upper-case($defclass)= 'EVENTS') then 'Events'
        else if (upper-case($defclass)= 'INTERVENTIONS') then 'Interventions'
        else if (starts-with(upper-case($defclass),'TRIAL')) then 'TrialDesign'
        else if (ends-with(upper-case($defclass),'ABOUT')) then 'FindingsAbout'
        else if (starts-with(upper-case($defclass),'SPECIAL')) then 'SpecialPurpose'
        else if (starts-with(upper-case($defclass),'RELAT')) then 'Relationship'
        else 'GeneralObservations'
    )
    (: Make the CDISC Library call to get all variable information per domain for this class  :)
    let $cdisclibraryquerysendig := concat($cdisclibrarybase,'sendig/',$sendigversion,'/classes/',$class)	
    let $cdisclibraryresponsesendig := http:send-request(<http:request method='get' username='{$username}' password='{$password}' auth-method='Basic'/>, $cdisclibraryquerysendig)
      (: get name, domain :)
      let $name := $itemgroupdef/@Name
      let $domainname := (
      	if($itemgroupdef/@Domain) then $itemgroupdef/@Domain
        else if (starts-with($name,'SUPP')) then 'SUPPQUAL'
        else if ($name='POOLDEF') then 'POOLDEF'
        else if ($name='RELREC') then 'RELREC'
        else substring($name,1,2)
      )
      (: get the location of the dataset and the document itself :)
		let $dsloc := (
			if($defineversion='2.1') then $itemgroupdef/def21:leaf/@xlink:href
			else $itemgroupdef/def:leaf/@xlink:href
		)
      let $doc := (
      	if($dsloc) then doc(concat($base,$dsloc))
        else () (: no location defined :)
      )
      (: Iterate over all the variables in the dataset definition :)
      for $itemoid in $itemgroupdef/odm:ItemRef/@ItemOID
        let $varname := $definedoc//odm:ItemDef[@OID=$itemoid]/@Name
        (: Get the "core" property - can be "Req", "Exp" or "Perm" :)
        let $core := ($cdisclibraryresponsesendig/json/datasets/*[name=$domainname]//*[./name=$varname]/core/text())
        (: return <test>{data($domainname)} - {data($varname)} - {$core}</test>  :)
        (: in case a variable is not explicitely mentioned in the SEND-IG), 
        it is always permissible 'Perm' :)
        let $core := (
        	if(not($core)) then 'Perm'
        	else $core
        ) 
        (: count the number of records in the dataset for the "Perm" variables :)
        (: if the variable is NOT 'Perm' we set the count artficially to 1
        so that the system does not need to go over all the records, which saves time :)
        (: get the records that have a null value and that are required :)
        for $record in $doc//odm:ItemGroupData[not(odm:ItemData[@ItemOID=$itemoid])][$core='Req']
        	let $recnum := $record/@data:ItemGroupDataSeq
            return <error rule="SD0002" dataset="{data($name)}" variable="{data($varname)}" recordnumber="{data($recnum)}" rulelastupdate="2020-06-27">Required variable {data($varname)} has a missing value</error>
]]>
</rulexquery>
</sdsrule>

<sdsrule id="SD0005" last-update="2019-08-20" originator="FDA" standard="SEND" 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.0</igversion>
<igversion>3.1</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 $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
(: now iterate over all datasets, but not the Trial design datasets and not for SUPPxx datasets :)
for $datasetdoc in doc($datasets)
   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="2019-08-20" 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="SD0005A-SD" last-update="2019-08-20" originator="FDA" standard="SEND" requiresDomainOrDataset="Yes">
<ruledescription>Values for --SEQ variable may not be duplicate within USUBJID</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.0</igversion>
<igversion>3.1</igversion>
<domain>ALL</domain>
<rulexquery><![CDATA[
(: SD0005A implements the rule for USUBJID, SD0005B implements the rule for POOLID :)
(: 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) variable 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 $datasets := concat($base,$dsname) (: result is a set of datasets - but in this case only 1 :)
(: 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
(: now iterate over all datasets, but not the Trial design datasets and not for SUPPxx datasets :)
for $datasetdoc in doc($datasets)
   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="2019-08-20" 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="SD0005B-SD" last-update="2019-08-20" originator="FDA" standard="SEND" requiresDomainOrDataset="Yes">
<ruledescription>Values for --SEQ variable may not be duplicate within 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.0</igversion>
<igversion>3.1</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 Pool Identifier (POOLID) variable value when they are present in the domain :)
(: SD0005A implements the rule for USUBJID, SD0005B implements the rule for POOLID :)
(: 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 := 'SEND_3_0_PDS2014/' :)
(: let $define := 'define2-0-0_DS.xml' :)
(: let $datasetname := 'FW' :)
let $definedoc := doc(concat($base,$define))
(: Iterate over the provided dataset(s) :)
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 $datasets := concat($base,$dsname) (: result is a set of datasets - but in this case only 1 :)
  (: get the OID of --SEQ and POOLID :)
  let $seqoid := 
      for $a in $definedoc//odm:ItemDef[ends-with(@Name,'SEQ') and string-length(@Name)=5]/@OID 
      where $a = $definedoc//odm:ItemGroupDef[@Name=$itemgroupname]/odm:ItemRef/@ItemOID
      return $a 
  let $poolidoid := 
      for $a in $definedoc//odm:ItemDef[@Name='POOLID']/@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
  (: now iterate over all datasets, but not the Trial design datasets and not for SUPPxx datasets :)
  for $datasetdoc in doc($datasets)
     let $orderedrecords := (
      for $record in $datasetdoc//odm:ItemGroupData[odm:ItemData[@ItemOID=$poolidoid] and odm:ItemData[@ItemOID=$seqoid]]
          group by 
          $b := $record/odm:ItemData[@ItemOID=$poolidoid]/@Value,
          $c := $record/odm:ItemData[@ItemOID=$seqoid]/@Value
          return element group {  
              $record
          }
      )	
      for $group in $orderedrecords
          (: each group has the same values for POOLID and --SEQ :)
          let $poolid := $group/odm:ItemGroupData[1]/odm:ItemData[@ItemOID=$poolidoid]/@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 POOLID and --SEQ :)
          where count($group/odm:ItemGroupData) > 1
              return <error rule="SD0005" dataset="{data($itemgroupname)}" variable="{data($seqname)}" rulelastupdate="2019-08-20" recordnumber="{data($recnum)}">The record with POOLID = {data($poolid)} and {data($seqname)} is not unique in the dataset {data($itemgroupname)}. The following records have the same combination of POOLID and {data($seqname)}: records number {data($recnums)}</error>
]]></rulexquery>
</sdsrule>

<!-- 2020-06-27 -->


<sdsrule id="SD0064" last-update="2019-08-20" originator="FDA" standard="SEND">
<ruledescription>Subject must be present in DM domain</ruledescription>
<ruledetaileddescription>All Subjects (USUBJID) must be present in Demographics (DM) domain</ruledetaileddescription>
<igversion>3.0</igversion>
<igversion>3.1</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' :)
let $definedoc := doc(concat($base,$define))
(: 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' :)
(: 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 $dataset := (
		if($defineversion='2.1') then $itemgroupdef/def21:leaf/@xlink:href
		else $itemgroupdef/def:leaf/@xlink:href
	)
    let $datasetname := $itemgroupdef/@Name
    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="2019-08-20" 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>

<sdsrule id="SD0064-SD" last-update="2019-08-20" originator="FDA" requiresDomainOrDataset="Yes" standard="SEND">
<ruledescription>Subject must be present in DM domain</ruledescription>
<ruledetaileddescription>All Subjects (USUBJID) must be present in Demographics (DM) domain</ruledetaileddescription>
<igversion>3.0</igversion>
<igversion>3.1</igversion>
<domain>ALL</domain>
<rulexquery><![CDATA[
(: Rule SD0064 FAST ALTERNATIVE - All Subjects (USUBJID) must be present in Demographics (DM) domain
Single domain/dataset implementation :)
xquery version "3.0";
declare namespace def = "http://www.cdisc.org/ns/def/v2.0";
declare namespace def21 = "http://www.cdisc.org/ns/def/v2.1";
declare namespace odm="http://www.cdisc.org/ns/odm/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 $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[@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="2019-05-20" 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>

<!-- rule SD1023 does not apply to SEND -->

<!-- rule SD1018 does not apply to SEND -->

<!-- rule SD0065 does not apply to SEND -->

<!-- rule SD0051 does not apply to SEND -->

<!-- rule SD0052 does not apply to SEND -->

<!-- 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 -->

<!-- rule SD1062 does not apply to SEND -->

<!-- rule SD0009 does not apply to SEND -->

<!-- rule SD1143 does not apply to SEND -->

<!-- rule SD1291 does not apply to SEND -->

<!-- Rule SD1101: Was FFAC142 but now limited to INTERVENTIONS, EVENTS, FINDINGS, SE -->
<sdsrule id="SD1101" last-update="2019-08-21" originator="FDA" standard="SEND">
<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.0</igversion>
<igversion>3.1</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 $datasetname := (
		if($defineversion='2.1') then $dataset/def21:leaf/@xlink:href
		else $dataset/def:leaf/@xlink:href
	)
    (: 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($name,'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="2019-08-21">Missing {data($entptname)} variable, when {data($enrtptname)} variable is present</error>	
]]></rulexquery>
</sdsrule>

<!-- rule SD1287, SD1288, SD1289, SD1293, SD1294 do not apply to SEND -->

<sdsrule id="SD1102" last-update="2019-08-21" originator="FDA" standard="SEND">
<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.0</igversion>
<igversion>3.1</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
	let $datasetname := (
		if($defineversion='2.1') then $dataset/def21:leaf/@xlink:href
		else $dataset/def:leaf/@xlink:href
	)
    (: 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="2019-08-21">Missing {data($enrtptname)}  variable, when {data($entptname)} variable is present</error>
]]></rulexquery>
</sdsrule>

<!-- Rule SD1292 does not apply to SEND -->

<!-- Rule SD2238 does not apply to SEND -->

<sdsrule originator="FDA" id="SD1104" standard="SEND" last-update="2019-08-21">
	<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.0</igversion>
	<igversion>3.1</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="2019-08-21">{data($strtptname)} is not present although {data($sttptname)} is present</error>			
	]]></rulexquery>
</sdsrule>

<sdsrule originator="FDA" id="SD1103" standard="SEND" last-update="2019-08-21">
	<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.0</igversion>
	<igversion>3.1</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 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 $strtptoid and not($sttptoid)
    return <error rule="SD1103" variable="{data($sttptname)}" dataset="{$name}" rulelastupdate="2019-08-21">{data($sttptname)} is not present although {data($strtptname)} is present</error>			
]]>
</rulexquery>
</sdsrule>	

<!-- Rules SD1313, SD1314, SD1315, SD1316, SD1319, SD1317, SD1318 are not applicable to SEND -->

<sdsrule originator="FDA" id="SD1039" standard="SEND" last-update="2019-08-22">
	<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.0</igversion>
	<igversion>3.1</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="2019-08-22">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="SEND" last-update="2019-08-22" 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.0</igversion>
	<igversion>3.1</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="2019-08-22">The value of {data($catname)} is redundant. It is equal to one of DOMAIN, --TERM, --TESTCD, --TRT, --BODSYS</warning>
	]]>
	</rulexquery>
</sdsrule>

<!-- Rules SD1145, SD1144 are not applicable to SEND -->

<!-- Rule SD0042 is not applicable to SEND -->

<!-- Rules SD1241, SD1242, SD1243, SD1280, SD0041, SD1281, SD1245 are not applicable to SEND  -->

<!-- SD0032 applies to INTERVENTIONS, EVENTS, FINDINGS, CO -->
<sdsrule originator="FDA" id="SD0032" standard="SEND" last-update="2019-08-23">
	<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.0</igversion>
	<igversion>3.1</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 doc(concat($base,$define))//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="2019-08-23" 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="SEND" last-update="2019-08-23" 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.0</igversion>
	<igversion>3.1</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 doc(concat($base,$define))//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="2019-08-23" 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>

<!-- Rules SD1244, SD1246, SD1282 are not applicable to SEND -->

<!-- SD0023: was rule FDAC175 -->
<sdsrule id="SD0023" last-update="2019-08-23" originator="FDA" standard="SEND">
<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.0</igversion>
<igversion>3.1</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)='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 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="2019-08-23" 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="2019-08-23" originator="FDA" standard="SEND" 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.0</igversion>
<igversion>3.1</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)='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 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="2019-08-23" 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>

<!-- Rules SD1283, SD1247, SD1248, SD1249, SD1251 are not applicable to SEND -->
	
<sdsrule originator="FDA" id="SD1047" standard="SEND" last-update="2019-08-29">
	<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.0</igversion>
	<igversion>3.1</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="2019-08-29" recordnumber="{data($recnum)}">Both {data($dosename)} and {data($dostxtname)} are populated</error>			
		]]>
	</rulexquery>
</sdsrule>

<sdsrule originator="FDA" id="SD1047-SD" standard="SEND" last-update="2019-08-29" 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.0</igversion>
	<igversion>3.1</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="2019-08-29" recordnumber="{data($recnum)}">Both {data($dosename)} and {data($dostxtname)} are populated</error>			
		]]>
	</rulexquery>
</sdsrule>

<sdsrule originator="FDA" id="SD0035" standard="SEND" last-update="2019-08-29">
	<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.0</igversion>
	<igversion>3.1</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 doc(concat($base,$define))//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 := doc(concat($base,$define))//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="2019-08-29" 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="SEND" last-update="2019-08-29" 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.0</igversion>
	<igversion>3.1</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 doc(concat($base,$define))//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 := doc(concat($base,$define))//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="2019-08-29" recordnumber="{data($recnum)}">{data($dosuname)} is not populated although one of {data($dosename)}, {data($dostxtname)} or {data($dostotname)} is populated</error>				
		]]>
	</rulexquery>
</sdsrule>


<!-- Rules SD1284, SD1285 are not applicable to SEND -->

<!-- Rule SD2003 is not applicable to SEND -->

<sdsrule originator="FDA" id="SD0071-A" standard="SEND" last-update="2019-08-29">
	<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.0</igversion>
	<igversion>3.1</igversion>
	<domain>DM</domain>
	<synonym context="CDISC">CG0128</synonym>
	<rulexquery><![CDATA[
(: Rule SD0071 - When ARMCD not in TA.ARMCD then ARMCD in ('SCRNFAIL', 'NOTASSGN') :)
(: This rule has been split in two implementation parts: SD0071A for screen failures,
SD0071B for withdrawn 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 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 $datasetdoc := (
		if ($datasetname) then doc(concat($base,$datasetname))
		else ()
	)
    (: 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 $datasetdoc//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="2019-08-29" 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="SEND" last-update="2019-08-29">
	<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.0</igversion>
	<igversion>3.1</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' :)
(: This rule has been split in two implementation parts: SD0071A for screen failures,
SD0071B for withdrawn 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 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 $datasetdoc := (
		if($datasetname) then doc(concat($base,$datasetname))
		else ()
	)
    (: 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 $datasetdoc//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="2019-08-29" 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>

<!-- Rules SD2001, SD2002 are not applicable to SEND -->

<sdsrule originator="FDA" id="SD0066" standard="SEND" last-update="2019-08-29">
	<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.0</igversion>
	<igversion>3.1</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 $datasetdoc := (
		if($datasetname) then doc(concat($base,$datasetname))
		else ()
	)
    (: 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 $datasetdoc//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="2019-08-29" 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>

<!-- Rules SD0011, SD2236, SD0053, SD1252, SD1253, SD1254, SD1255, SD1256 are not applicable to SEND -->
	
<sdsrule originator="FDA" id="SD0088" standard="SEND" last-update="2019-08-29">
	<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.0</igversion>
	<igversion>3.1</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="2019-08-29" recordnumber="{data($recnum)}">RFENDTC=null for ACTARM='{data($actarmvalue)}'. RFENDTC is required to be populated for all randomized subjects.</error>			
		]]>
	</rulexquery>
</sdsrule>

<!-- Rules SD1240 and SD1258 are not applicable to SEND -->

<sdsrule originator="FDA" id="SD0087" standard="SEND" last-update="2019-08-29">
	<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.0</igversion>
	<igversion>3.1</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="2019-08-29" recordnumber="{data($recnum)}">RFSTDTC expected to be not null for ACTARM='{data($actarmvalue)}'</error>			
		]]>
	</rulexquery>
</sdsrule>

<sdsrule originator="FDA" id="SD1259" standard="SEND" last-update="2019-08-29">
	<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.0</igversion>
	<igversion>3.1</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="2019-08-29" recordnumber="{data($recnum)}">SETCD='{data($setcdvalue)}' has more than 8 characters</error>				
		]]>
	</rulexquery>
</sdsrule>

<sdsrule id="SD1001" last-update="2019-08-29" originator="FDA" standard="SEND">
<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.0</igversion>
<igversion>3.1</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 $datasetdoc := (
	if($datasetname) then doc(concat($base,$datasetname) )
	else ()
)
(: iterate over all records in the DM dataset :)
for $record in $datasetdoc//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($datasetdoc//odm:ItemGroupData[@data:ItemGroupDataSeq > $recnum][odm:ItemData[@ItemOID=$subjidoid][@Value=$subjidvalue]])
    where $count > 0
    return <error rule="SD1001" dataset="DM" variable="SUBJID" rulelastupdate="2019-08-29" recordnumber="{data($recnum)}">Duplicate value of SUBJID={data($subjidvalue)} - {data($count)} in dataset {data($datasetname)}</error>	
]]></rulexquery>
</sdsrule>

<sdsrule id="SD0083" last-update="2019-08-29" originator="FDA" standard="SEND">
<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.0</igversion>
<igversion>3.1</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 $dataset := concat($base,$datasetname) 
(: iterate over all records in the DM dataset :)
for $record in doc($dataset)//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(doc($dataset)//odm:ItemGroupData[@data:ItemGroupDataSeq > $recnum][odm:ItemData[@ItemOID=$usubjidoid][@Value=$usubjidvalue]])
    where $count > 0
    return <error rule="SD0083" dataset="DM" variable="SUBJID" rulelastupdate="2019-08-29" recordnumber="{data($recnum)}">Duplicate value of SUBJID={data($usubjidvalue)} - {data($count)} in dataset {data($datasetname)}</error>	
]]></rulexquery>
</sdsrule>

<sdsrule originator="FDA" id="SD1010" standard="SEND" last-update="2019-08-29">
	<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.0</igversion>
	<igversion>3.1</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="2019-08-29" recordnumber="{data($recnum)}">Invalid value for ELEMENT={data($element)}, null was expected for ETCD='UNPLAN'</error>			
		]]>
	</rulexquery>
</sdsrule>

<sdsrule originator="FDA" id="SD1004" standard="SEND" last-update="2019-08-29">
	<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.0</igversion>
	<igversion>3.1</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="2019-08-29" 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="SEND" last-update="2019-08-29">
	<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.0</igversion>
	<igversion>3.1</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 := doc(concat($base,$define))//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="2019-08-27" 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="SEND" last-update="2019-08-29">
	<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.0</igversion>
	<igversion>3.1</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="2019-08-29" 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="SEND" last-update="2019-08-29">
	<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.0</igversion>
	<igversion>3.1</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="2019-08-29" 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: TODO: 
only following identifier timing variables are allowed: COGRPID, COREFID, COSPID, VISIT, VISITNUM, VISITDY, TAETORD, CODY, COTPT, COTPTNUM, COELTM, COTPTREF, CORFTDTC. 
Use CDISC Library to check what the type of the variable is -->

<!-- Rules SD1261, SD1262 are not applicable to SEND -->

<!-- Rules SD1045, SD1046, SD0085, SD1016, SD0068, SD1298 are not applicable to SEND -->

<sdsrule id="SD1061" last-update="2019-08-29" originator="FDA" standard="SEND">
<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.0</igversion>
<igversion>3.1</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 := (
	if($mbdatasetname) then concat($base,$mbdatasetname)
	else ()
)
(: 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="2019-08-29">Document {data($mbdatasetname)} for domain MB could not be found in collection {data($base)} although document {data($msdatasetname)} is present</warning>
]]></rulexquery>
</sdsrule>

<!-- Rules SD1263, SD1264 are applicable to SEND -->
   
<sdsrule id="SD1066" last-update="2019-08-29" originator="FDA" standard="SEND">
<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.0</igversion>
<igversion>3.1</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="2019-08-29" 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="2019-08-29" originator="FDA" standard="SEND">
<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.0</igversion>
<igversion>3.1</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="2019-08-29" 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>

<!-- Rule SD1265 is not applicable to SEND -->

<sdsrule id="SD1051" last-update="2019-08-29" originator="FDA" standard="SEND">
<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.0</igversion>
<igversion>3.1</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="2019-08-29" 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="2019-08-29" originator="FDA" standard="SEND">
<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.0</igversion>
<igversion>3.1</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="2019-08-29" recordnumber="{data($recnum)}">Missing IDVAR value, when RDOMAIN value is provided, RDOMAIN={data($rdomainvalue)}</error>	
]]></rulexquery>
</sdsrule>


<sdsrule id="SD1013" last-update="2019-08-29" originator="FDA" standard="SEND">
<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.0</igversion>
<igversion>3.1</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="2019-08-29" recordnumber="{data($recnum)}">TAETORD is populated, value={data($taetordvalue)} with ETCD='UNPLAN' in dataset SE</warning>
]]></rulexquery>
</sdsrule>

<!-- Rule SD1266 is not applicable to SEND -->

<sdsrule originator="FDA" id="SD0092" standard="SEND" last-update="2019-09-07">
	<ruledescription>When ETCD='UNPLAN', SEUPDES may not be null</ruledescription>
	<igversion>3.0</igversion>
	<igversion>3.1</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="2019-09-07" recordnumber="{data($recnum)}">Missing value for SEUPDES, when ETCD='UNPLAN'</error>			
		]]>
	</rulexquery>
</sdsrule>

<!-- Rules SD1017 and SD1019 are not applicable to SEND -->

<!-- Rule SD1299 is not applicable to SEND -->

<sdsrule id="SD1088" last-update="2019-08-29" originator="FDA" standard="SEND">
<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.0</igversion>
<igversion>3.1</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 $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 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 := 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="2019-08-29" 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="2019-08-29" originator="FDA" standard="SEND" 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.0</igversion>
<igversion>3.1</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="2019-08-29" 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="2019-08-29" originator="FDA" standard="SEND" 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.0</igversion>
<igversion>3.1</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="2019-08-29" 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="2019-08-29" originator="FDA" standard="SEND" 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.0</igversion>
<igversion>3.1</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 $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 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 := 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="2019-08-29" 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="2019-08-29" originator="FDA" standard="SEND">
<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.0</igversion>
<igversion>3.1</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 $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 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 := 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 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="2019-08-29" 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="2019-08-29" originator="FDA" standard="SEND" 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.0</igversion>
<igversion>3.1</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 $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 :)
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 := 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 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="2019-08-29" 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="2019-08-29" originator="FDA" standard="SEND">
<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.0</igversion>
<igversion>3.1</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 $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 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 := 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 $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="2019-08-29" 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="2019-08-29" originator="FDA" standard="SEND" 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.0</igversion>
<igversion>3.1</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 $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 :)
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 := 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 $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="2019-08-29" 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="2019-08-29" originator="FDA" standard="SEND" 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.0</igversion>
<igversion>3.1</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 $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' 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 := 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 $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="2019-08-29" 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="2019-08-29" originator="FDA" standard="SEND" 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.0</igversion>
<igversion>3.1</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 $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 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 := 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 $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="2019-08-29" 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="2019-08-29" originator="FDA" standard="SEND">
<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.0</igversion>
<igversion>3.1</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 $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 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 := 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 $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="2015-02-11" 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="2019-08-29" originator="FDA" standard="SEND" requiresDomainOrDataset="Yes">
<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.0</igversion>
<igversion>3.1</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 $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 :)
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 := 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 $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="2015-02-11" 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>

<!-- Rule SD1030 is only applicable to SENDIG 3.1, not to 3.0 -->
<sdsrule id="SD1030" last-update="2019-09-16" originator="FDA" standard="SEND">
<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</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 $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 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 := doc($dmdatasetlocation)//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="2019-09-16" recordnumber="{data($recnum)}">Value for {data($strfname)} is populated(value={data($strfvalue)}), when RFSTDTC is NULL in dataset {data($datasetname)}</warning>
]]></rulexquery>
</sdsrule>

<!-- Rule SD1030 is only applicable to SENDIG 3.1, not to 3.0 -->
<sdsrule id="SD1030-SD" last-update="2019-09-16" originator="FDA" standard="SEND" 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</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 $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 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 := doc($dmdatasetlocation)//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="2019-09-16" recordnumber="{data($recnum)}">Value for {data($strfname)} is populated(value={data($strfvalue)}), when RFSTDTC is NULL in dataset {data($datasetname)}</warning>	
]]></rulexquery>
</sdsrule>

<!-- 2019-09-16: rule SD1031: End Relative to Reference Period (-ENRF) should not be populated for subjects with missing value for Reference End Date/Time (RFENDTC) -->
<!-- Rule SD1031 is only applicable to SENDIG 3.1, not to 3.0 -->
<sdsrule id="SD1031" last-update="2019-09-16" originator="FDA" standard="SEND">
<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</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 $dmdatasetlocation := 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 := doc($dmdatasetlocation)//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="2019-10-16" recordnumber="{data($recnum)}">Value for {data($enrfname)} is populated(value={data($enrfvalue)}), when RFENDTC is NULL in dataset {data($name)}</warning>	
]]>
</rulexquery>
</sdsrule>

<!-- 2019-09-16: rule SD1031: End Relative to Reference Period (-ENRF) should not be populated for subjects with missing value for Reference End Date/Time (RFENDTC) -->
<!-- Rule SD1031 is only applicable to SENDIG 3.1, not to 3.0 -->
<sdsrule id="SD1031-SD" last-update="2019-09-16" originator="FDA" standard="SEND" 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</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 $dmdatasetlocation := 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 $datasetlocation := concat($base,$dsname)
    (: 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 doc($datasetlocation)//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 := doc($dmdatasetlocation)//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="2019-10-16" 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="2019-08-29" originator="FDA" standard="SEND">
<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.0</igversion>
<igversion>3.1</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 using 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 $doc := (
	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 $doc//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="2019-08-29" 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="2019-08-29" originator="FDA" standard="SEND" 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.0</igversion>
<igversion>3.1</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 $doc := (
	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 $doc//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="2019-08-29" 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="2019-08-29" originator="FDA" standard="SEND" 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.0</igversion>
<igversion>3.1</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="2019-08-29">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="2019-08-29" originator="FDA" standard="SEND" 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.0</igversion>
<igversion>3.1</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="2019-08-29">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="2019-08-29" originator="FDA" standard="SEND" 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.0</igversion>
<igversion>3.1</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) 
        return <warning rule="SD1127" dataset="{data($name)}" variable="{data($tptnumname)}" rulelastupdate="2019-08-29">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="2019-08-29" originator="FDA" standard="SEND" 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.0</igversion>
<igversion>3.1</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) 
        return <warning rule="SD1127" dataset="{data($name)}" variable="{data($tptnumname)}" rulelastupdate="2019-08-29">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 is not applicable to SEND: 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).
This rule is NONSENSE anyway -->

<sdsrule id="SD1125" last-update="2020-07-01" originator="FDA" standard="SEND" 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.0</igversion>
<igversion>3.1</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 := 'SEND_3_0_PDS2014/'  :)
(: let $define := 'define2-0-0_DS.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 :)
        (: taking care of the case that -ELTM is not populated :)
        where $uniqueeltmvaluescount > 0 and not($uniqueeltmvaluescount = 1) 
        return <warning rule="SD1125" dataset="{data($name)}" variable="{data(eltmname)}" rulelastupdate="2020-07-01">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-07-01" originator="FDA" standard="SEND" 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.0</igversion>
<igversion>3.1</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 := 'SEND_3_0_PDS2014/' :)
(: let $define := 'define2-0-0_DS.xml' :)
(: let $datasetname := 'PC' :)
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 $datasetlocalname := (
		if($defineversion='2.1') then $dataset/def21:leaf/@xlink:href
		else $dataset/def:leaf/@xlink:href
	)
    let $datasetdoc:= (
		if($datasetlocalname) then doc(concat($base,$datasetlocalname))
		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 :)
        (: taking care of the case that -ELTM is not populated :)
        where $uniqueeltmvaluescount > 0 and not($uniqueeltmvaluescount = 1) 
        return <warning rule="SD1125" dataset="{data($name)}" variable="{data(eltmname)}" rulelastupdate="2020-07-01">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="2019-08-29" originator="FDA" standard="SEND">
<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.0</igversion>
<igversion>3.1</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="2019-08-29" 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="2019-08-29" originator="FDA" standard="SEND">
<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.0</igversion>
<igversion>3.1</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="2019-08-29" 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>

<!-- Rules SD1267 and SD1287 are not applicable to SEND -->

<sdsrule id="SD0020" last-update="2019-08-29" originator="FDA" standard="SEND">
<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.0</igversion>
<igversion>3.1</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="2019-08-29">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="2019-08-29" originator="FDA" standard="SEND">
<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.0</igversion>
<igversion>3.1</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="2019-08-29">The value for TSPARM in dataset {data($datasetname)} has more than 40 characters: '{data($testvalue)}' was found</error>
]]></rulexquery>
</sdsrule>

<!-- Rule SD2018 is only applicable to SENDIG 3.1 not to 3.0 -->
<sdsrule id="SD2018" last-update="2019-08-29" originator="FDA" standard="SEND">
<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</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="2019-08-29" recordnumber="{data($recnum)}">Missing TSVALNF value</error>	
]]></rulexquery>
</sdsrule>

<!-- Rule SD2017 is only applicable to SENDIG 3.1 not to 3.0 -->
<sdsrule id="SD2017" last-update="2019-08-29" originator="FDA" standard="SEND">
<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</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="2019-08-29" recordnumber="{data($recnum)}">Missing TSVAL value</error>
]]></rulexquery>
</sdsrule>

<!-- Rules SD1278, SD2252, SD2255, SD2265, SD1268, SD1260 are not applicable to SEND -->

<sdsrule id="SD1038" last-update="2019-08-29" originator="FDA" standard="SEND">
<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.0</igversion>
<igversion>3.1</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 doc(concat($base,$define))//odm:ItemGroupDef[@Name='TS']/def21:leaf/@xlink:href
	else doc(concat($base,$define))//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="2019-08-29" 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>


<!-- Rules SD1295, SD1215, SD1223, SD1306, SD1307, SD1308, SD1309, SD1310, Sd1311, SD1312 are not applicable to SEND -->

<!-- Rules SD1269, SD1296 are not applicable to SEND -->

<!-- Rules SD2245, SD2246, SD2247, SD1148 are not applicable to SEND -->

<!-- Rules SD1226, SD2201, SD2202, SD2203, SD2204, SD2205, SD2206, SD2207, SD2209, SD2210 are not applicable to SEND -->

<!-- Rule SD2211 is identical to rule SD1307! -->
<!-- Rule SD2212, SD2213, SD2214, SD2215, SD2216, SD2217, SD2218, SD2219, 
SD2221, SD2222, SD2223, SD2224, SD2225, SD2226, SD2227, SD2228, SD222, SD2230,
SD2231, SD2232, SD2233, SD2234, SD2235 are not applicable to SEND -->

<!-- Rules SD1297 and SD0063 and SD0063A are not applicable to SEND -->

<sdsrule id="SD1069" last-update="2019-08-30" originator="FDA" standard="SEND">
<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.0</igversion>
<igversion>3.1</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="2019-08-30" 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="2019-08-30" originator="FDA" standard="SEND">
<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.0</igversion>
<igversion>3.1</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="2019-08-30" 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="2019-08-30" originator="FDA" standard="SEND">
<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.0</igversion>
<igversion>3.1</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="2019-08-30" 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="SEND" last-update="2019-08-30" 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.0</igversion>
	<igversion>3.1</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 := doc(concat($base,$datasetlocation))
    (: 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="2019-08-30">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="SEND" last-update="2019-08-30" 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.0</igversion>
	<igversion>3.1</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="2019-08-30">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="2019-08-30" originator="FDA" standard="SEND">
<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>
<igversion>3.0</igversion>
<igversion>3.1</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))
(: 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) :)
(: 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="2019-08-30" 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="2019-08-30" 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="2019-08-30" originator="FDA" standard="SEND" 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>
<igversion>3.0</igversion>
<igversion>3.1</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))
(: 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) :)
(: 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
		(: generate the message :)
		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="2019-08-30" 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="2019-08-30" 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>

<!-- Rule SD1270 is not applicable to SEND -->

<!-- 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 -->

<!-- Rule SD1271 is not applicable to SEND -->

<sdsrule id="SD0089" last-update="2019-08-30" originator="FDA" standard="SEND">
<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.0</igversion>
<igversion>3.1</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 doc(concat($base,$define))//odm:ItemDef[@Name='TEENRL']/@OID 
        where $a = doc(concat($base,$define))//odm:ItemGroupDef[@Name='TE']/odm:ItemRef/@ItemOID
        return $a
)
let $teduroid := (
    for $a in doc(concat($base,$define))//odm:ItemDef[@Name='TEDUR']/@OID 
        where $a = doc(concat($base,$define))//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="20189-08-30" recordnumber="{data($recnum)}">Missing values for TEENRL and TEDUR</warning>
]]></rulexquery>
</sdsrule>


<!-- 2020-07-01: Using the CDISC-Library)  -->
<sdsrule id="SD1079" last-update="2019-08-30" originator="FDA" standard="SEND" webservice="Uses CDISC Library RESTful Web Service">
<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.0</igversion>
<igversion>3.1</igversion>
<domain>ALL</domain>
<rulexquery><![CDATA[
(: Rule SD1079 - variable must be ordered in the domain as by CDISC Standards :)
(: The current version uses the CDISC Library and its 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;
(: CDISC Library API username and password :)
declare variable $username external;
declare variable $password 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)
} ;
(: let $base := 'SEND_3_0_PDS2014/' :)
(: let $define := 'define2-0-0_DS.xml' :)
(: get the define.xml :)
let $definedoc := doc(concat($base,$define))
(: Get the SEND-IG version, taking '3.0' as the default :)
let $sendigversion := (
	if($definedoc//odm:MetaDataVersion[@StandardName='SEND-IG']/@StandardVersion) then $definedoc//odm:MetaDataVersion[@StandardName='SEND-IG']/@StandardVersion
	else '3.1'
)
(: Translate the SDTM-IG version into the SDTM model version, with '1.4' as the default, :)
let $sdtmmodelversion := (
	if($sendigversion='3.1') then '1.5'
    else '1.2'
)
(: Replacing the dot by a dash as the CDISC Library uses the latter :)
let $sdtmmodelversioncdisclibrary := translate($sdtmmodelversion,'.','-')
(: Use the CDISC Library to get all Identifiers and Timing variables for all classes :)
let $cdisclibrarybase := concat('https://library.cdisc.org/api/mdr/sdtm/',$sdtmmodelversioncdisclibrary,'/classes/')
let $identifierstimingsquery := concat($cdisclibrarybase,'GeneralObservations')
let $identifierstimingsresponse := http:send-request(<http:request method='get' username='{$username}' password='{$password}' auth-method='Basic'/>, $identifierstimingsquery)
(: we need to split identifiers and timing variable :)
let $identifiers := $identifierstimingsresponse/json//classVariables/*[role='Identifier']/name/text()
let $timings := $identifierstimingsresponse/json//classVariables/*[role='Timing']/name/text()
(: Use the CDISC Library to get all Interventions variables :)
let $interventionsquery := concat($cdisclibrarybase,'Interventions')
let $interventionsresponse := http:send-request(<http:request method='get' username='{$username}' password='{$password}' auth-method='Basic'/>, $interventionsquery)
let $interventions := $interventionsresponse/json//classVariables/*/name/text()
(: Use the CDISC Library to get all Events variables :)
let $eventsquery := concat($cdisclibrarybase,'Events')
let $eventsresponse := http:send-request(<http:request method='get' username='{$username}' password='{$password}' auth-method='Basic'/>, $eventsquery)
let $events := $eventsresponse/json//classVariables/*/name/text()
(: Use the CDISC Library to get all Events variables :)
let $findingsquery := concat($cdisclibrarybase,'Findings')
let $findingsesponse := http:send-request(<http:request method='get' username='{$username}' password='{$password}' auth-method='Basic'/>, $findingsquery)
let $findings := $findingsesponse/json//classVariables/*/name/text()
(: Use the CDISC Library to get all FindingsAbout variables :)
let $findingsaboutquery := concat($cdisclibrarybase,'FindingsAbout')
let $findingsaboutresponse := http:send-request(<http:request method='get' username='{$username}' password='{$password}' auth-method='Basic'/>, $findingsaboutquery)
let $findingsabout := $findingsaboutresponse/json//classVariables/*/name/text()
(: Use the CDISC Library to get all SpecialPurpose variables :)
(: ATTENTION: these are organized per dataset! :)
let $specialpurposequery := concat($cdisclibrarybase,'SpecialPurpose')
let $specialpurposeresponse := http:send-request(<http:request method='get' username='{$username}' password='{$password}' auth-method='Basic'/>, $specialpurposequery)
let $specialpurpose := $specialpurposeresponse/json//datasets
(: Use the CDISC Library to get all TrialDesign variables :)
(: ATTENTION: these are organized per dataset! :)
let $trialdesignquery := concat($cdisclibrarybase,'TrialDesign')
let $trialdesignresponse := http:send-request(<http:request method='get' username='{$username}' password='{$password}' auth-method='Basic'/>, $trialdesignquery)
(: Remark that we do NOT go into the individual datasets YET :) 
let $trialdesign := $trialdesignresponse/json//datasets
(: Use the CDISC Library to get all TrialDesign variables :)
(: ATTENTION: these are organized per dataset! :)
let $relastionshipquery := concat($cdisclibrarybase,'Relationship')
let $relationshipresponse := http:send-request(<http:request method='get' username='{$username}' password='{$password}' auth-method='Basic'/>, $relastionshipquery)
let $relationship := $relationshipresponse/json//datasets
(: Use the CDISC Library to get all AssociatedPersons variables :)
let $associatedpersonsquery := concat($cdisclibrarybase,'AssociatedPersons')
let $associatedpersonsresponse := http:send-request(<http:request method='get' username='{$username}' password='{$password}' auth-method='Basic'/>, $associatedpersonsquery)
let $associatedpersonsclassVariables := $associatedpersonsresponse/json//classVariables/*/name/text()
let $associatedpersonsdatasetVariables := $associatedpersonsresponse/json//datasetVariables/*/name/text()
(: iterate over all dataset definitions, and get the class, name and domain :)
for $itemgroupdef in $definedoc//odm:ItemGroupDef
	let $defclass := (
		if($defineversion='2.1') then $itemgroupdef/def21:Class/@Name
		else $itemgroupdef/@def:Class
	)
    let $name := $itemgroupdef/@Name
    let $domain := (
    	if(starts-with($name,'SUPP')) then 'SUPPQUAL'
    	else if($itemgroupdef/@Domain) then $itemgroupdef/@Domain
        else if($itemgroupdef/@Name='RELREC') then 'RELREC'
        else substring($itemgroupdef/@Name,1,2)
    )
   (: get the variable name :)
    let $variables := (
    	for $varoid in $itemgroupdef/odm:ItemRef/@ItemOID 
        return $definedoc//odm:ItemDef[@OID=$varoid]/@Name
    )
    (: now comes the different part: check the order :)
    (: depending on the class, create an ordered array of the variables that are applicable :)
    (: Read the def:Class, if incorrect, set cdisclibraryclass to 'UNKNOWN' :)
    let $cdisclibraryclass := (
    	if(upper-case($defclass)='INTERVENTIONS') then 'Interventions'
        else if (upper-case($defclass)='EVENTS') then 'Events'
        else if (upper-case($defclass)='FINDINGS') then 'Findings'
        else if (upper-case($defclass)='FINDINGS ABOUT' or upper-case($defclass)='FINDINGSABOUT') then 'FindingsAbout'
        else if (upper-case($defclass)='SPECIAL PURPOSE' or upper-case($defclass)='SPECIALPURPOSE') then 'SpecialPurpose'
        else if (upper-case($defclass)='TRIALDESIGN' or upper-case($defclass)='TRIAL DESIGN') then 'TrialDesign'
        else if (upper-case($defclass)='RELATIONSHIP') then 'Relationship'
        else 'UNRECOGNIZED'
    )
    (: P.S. FINDINGS ABOUT is NOT in SEND :)
    let $orderedvariables := (
    	if($cdisclibraryclass = 'Interventions') then
         	functx:value-union(functx:value-union($identifiers,$interventions),$timings)
        else if($cdisclibraryclass = 'Events') then
         	functx:value-union(functx:value-union($identifiers,$events),$timings)
        else if($cdisclibraryclass = 'Findings') then    
        	functx:value-union(functx:value-union($identifiers,$findings),$timings)
        else if($cdisclibraryclass = 'TrialDesign') then
        	$trialdesign/*[./name=$domain]/datasetVariables/*/name
        else if(upper-case($defclass)='Relationship') then
        	$relationship/*[./name=$domain]/datasetVariables/*/name
        else if($cdisclibraryclass = 'SpecialPurpose') then
        	$specialpurpose/*[./name=$domain]/datasetVariables/*/name
        else ()
    )
    (: we need to replace '--' by the domain name :)
    let $orderedvars := local:replacedash($orderedvariables,$domain)
    (: check whether the variables from the define.xml is a subset of the list of domain variables  :)
    let $issubset := local:compare($orderedvars,$variables) 
    where not($issubset)
    return <error rule="CG0330" dataset="{data($name)}" class="{data($defclass)}" sendigversion="{data($sendigversion)}" rulelastupdate="2020-06-16">Variable order is not correct for class '{$cdisclibraryclass}': found = {data($variables)} - expected = {$orderedvars}</error>	
]]></rulexquery>
</sdsrule>


<!-- SD1040 applies to INTERVENTIONS, EVENTS, FINDINGS, TI -->
<sdsrule id="SD1040" last-update="2019-08-30" originator="FDA" standard="SEND">
<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.0</igversion>
<igversion>3.1</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="2019-08-30" 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="2019-08-30" originator="FDA" standard="SEND" 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.0</igversion>
<igversion>3.1</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="2019-08-30" recordnumber="{data($recnum)}">Redundancy in paired variables values for {data($scatname)} with value={data($scatvalue)}.</warning>
]]></rulexquery>
</sdsrule>

<!-- Rules SD1272, SD1273, SD1274, SD1275, SD1276, SD1277 are not applicable to SEND -->

<sdsrule id="SD0027" last-update="2019-08-30" originator="FDA" standard="SEND" 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.0</igversion>
<igversion>3.1</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="2019-08-30" 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="2019-08-30" originator="FDA" standard="SEND" 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.0</igversion>
<igversion>3.1</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="2019-08-30" 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="2019-08-30" originator="FDA" standard="SEND" 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.0</igversion>
<igversion>3.1</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="2019-08-30" 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="2019-08-30" originator="FDA" standard="SEND" 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.0</igversion>
<igversion>3.1</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="2019-08-30" 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>

<!-- Rule SD1137 is not applicable to SEND -->

<sdsrule originator="FDA" id="SD0016" standard="SEND" last-update="2019-08-30" 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.0</igversion>
	<igversion>3.1</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="2019-08-30">Value of {data($strescname)} may not be null when {data($drvflname)}='Y'</error>												
	]]>
	</rulexquery>
</sdsrule>

<sdsrule originator="FDA" id="SD0016-SD" standard="SEND" last-update="2019-08-30" 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.0</igversion>
	<igversion>3.1</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="2019-08-30">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. 
As it is about SDTM, the rule is not applicable to SEND anyway -->

<sdsrule id="SD0075" last-update="2019-08-30" originator="FDA" standard="SEND">
<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.0</igversion>
<igversion>3.1</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="2019-08-30">Variable '{data($idvarvalue)}' was not found in the parent dataset of the referenced domain RDOMAIN='{data($rdomain)}' </error>
]]></rulexquery>
</sdsrule>

<sdsrule id="SD0077" last-update="2019-08-31" originator="FDA" standard="SEND">
<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.0</igversion>
<igversion>3.1</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 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
    )
    (: 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="2019-08-31" 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="2019-08-31" originator="FDA" standard="SEND">
<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.0</igversion>
<igversion>3.1</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="2019-08-31">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="2019-08-31" originator="FDA" standard="SEND" 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.0</igversion>
<igversion>3.1</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="2019-08-31">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>

<!-- Rule SD1301 is not applicable to SEND -->
<!-- Rule SD2009 is not applicable to SEND -->

<sdsrule id="SD0036" last-update="2019-09-16" originator="FDA" standard="SEND">
<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.0</igversion>
<igversion>3.1</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="2019-09-16" 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>

<!-- 2019-09-16 SD0036  -->
<sdsrule id="SD0036-SD" last-update="2019-09-16" originator="FDA" standard="SEND" 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.0</igversion>
<igversion>3.1</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="2019-09-16" 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>

<!-- Rule SD1290 is not applicable to SEND -->

<sdsrule id="SD0017" last-update="2019-09-02" originator="FDA" standard="SEND">
<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.0</igversion>
<igversion>3.1</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 $dataset := concat($base,$datasetname)
    (: iterate over all the records where there is a --TEST variable :)
    for $record in doc($dataset)//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="2019-09-02">The value for {data($testvar)} has more than 40 characters: '{data($testvalue)}' was found</error>
]]></rulexquery>
</sdsrule>

<!-- Rule SD1109 is not applicable to SEND -->

<sdsrule id="SD0001" last-update="2019-09-02" originator="FDA" standard="SEND">
<ruledescription>Domain tables must have at least one record</ruledescription>
<ruledetaileddescription>Domain table should have at least one record</ruledetaileddescription>
<igversion>3.0</igversion>
<igversion>3.1</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;
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
let $definedoc := doc(concat($base,$define))
(: Find all datasets :)
for $itemgroup in $definedoc//odm:ItemGroupDef
    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="2019-09-02">{$numrecords} found in dataset: {data($datasetname)}</error>
]]></rulexquery>
</sdsrule>

<sdsrule id="SD0001-SD" last-update="2019-09-02" originator="FDA" standard="SEND" requiresDomainOrDataset="Yes">
<ruledescription>Domain tables must have at least one record</ruledescription>
<ruledetaileddescription>Domain table should have at least one record</ruledetaileddescription>
<igversion>3.0</igversion>
<igversion>3.1</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="2019-09-02">{$numrecords} found in dataset: {data($datasetname)}</error>
]]></rulexquery>
</sdsrule>


<sdsrule id="SD1005" last-update="2019-09-02" originator="FDA" standard="SEND">
<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.0</igversion>
<igversion>3.1</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 $dmdatasetpath := concat($base,$dmdatasetname)
let $dmstudyidunique := distinct-values(doc($dmdatasetpath)//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 $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="2019-09-02" 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>

<!-- Rule SD1060 is not applicable to SEND -->
<sdsrule id="SD1060" last-update="2019-09-02" originator="FDA" standard="SEND">
<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="2019-09-02" 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="SEND" last-update="2019-09-02" 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.0</igversion>
	<igversion>3.1</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 doc(concat($base,$define))//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="2019-09-02" 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="2019-09-03" originator="FDA" standard="SEND">
<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.0</igversion>
<igversion>3.1</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 $definedoc := doc(concat($base,$define))
(: let $base := '/db/fda_submissions/cdisc01/' :)
(: let $define := 'define2-0-0-example-sdtm.xml' :)
(: 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 $dsdataset := concat($base,$dsdatasetname)
(: 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(doc($dsdataset)//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="2019-09-03" 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="2019-09-03" originator="FDA" standard="SEND">
<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.0</igversion>
<igversion>3.1</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="2019-09-03">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="2019-09-03" originator="FDA" standard="SEND" 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.0</igversion>
<igversion>3.1</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 $dataset := concat($base,$dsname)
    (: get the OID of the "DOMAIN" variable :)
    let $domainoid := (
    for $a in $definedoc//odm:ItemDef[@Name='DOMAIN']/@OID 
		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(doc($dataset)//odm:ItemData[@ItemOID=$domainoid]/@Value)
    where count($distinctdomainvalues) > 1
    return <error rule="SD0004" dataset="{data($itemgroupdefname)}" variable="DOMAIN" rulelastupdate="2019-09-03">Inconsistent Domain Abbreviation (DOMAIN) variable value in dataset {data($datasetname)}. Following values were found: {data($distinctdomainvalues)}</error>		
]]></rulexquery>
</sdsrule>

<sdsrule id="SD0067" last-update="2019-09-03" originator="FDA" standard="SEND">
<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.0</igversion>
<igversion>3.1</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="2019-09-03" 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="2019-09-03" originator="FDA" standard="SEND">
<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.0</igversion>
<igversion>3.1</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 $tedatasetlocation := concat($base,$tedatasetname)
(: 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(doc($tedatasetlocation)//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="2019-09-03" 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>

<!-- Rule SD1053 is not applicable to SEND -->

<sdsrule id="SD1049" last-update="2019-09-03" originator="FDA" standard="SEND">
<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.0</igversion>
<igversion>3.1</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 $datasetlocation := concat($base,$datasetname)
    (: 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 doc($datasetlocation)//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="2019-09-03" 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="2019-09-03" originator="FDA" standard="SEND">
<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.0</igversion>
<igversion>3.1</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 $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 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="2019-09-03" 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="2019-09-03" originator="FDA" standard="SEND">
<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.0</igversion>
<igversion>3.1</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 $tadatasetlocation := concat($base,$tadatasetname)
(:  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((doc($tadatasetlocation))//odm:ItemGroupData/odm:ItemData[@ItemOID=$taetordoid][@Value=$taetord])
    return <error rule="SD1014" dataset="TA" variable="TAETORD" rulelastupdate="2019-09-03" 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="2019-09-03" originator="FDA" standard="SEND" 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.0</igversion>
<igversion>3.1</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/def21:leaf/@xlink:href
	else $tadataset/def:leaf/@xlink:href
)
let $tadatasetlocation := concat($base,$tadatasetname)
(:  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((doc($tadatasetlocation))//odm:ItemGroupData/odm:ItemData[@ItemOID=$taetordoid][@Value=$taetord])
    return <error rule="SD1014" dataset="TA" variable="TAETORD" rulelastupdate="2019-09-03" 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="2019-09-03" originator="FDA" standard="SEND">
<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.0</igversion>
<igversion>3.1</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="2019-09-03" recordnumber="{data($recnum)}">Invalid value for RELTYPE variable. Non-null value={data($relvalue)} was found though identifying variable is {data($idvarvalue)}</error>
]]></rulexquery>
</sdsrule>

<!-- Rule SD1042 is only applicable to SENDIG 3.1, not to 3.0 -->
<sdsrule id="SD1042-A" last-update="2019-09-03" originator="FDA" standard="SEND">
<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</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="2019-09-03" 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>

<!-- Rule SD1042 is only applicable to SENDIG 3.1, not to 3.0 -->
<sdsrule id="SD1042-A-SD" last-update="2019-09-03" originator="FDA" standard="SEND" 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</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="2019-09-03" 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>

<!-- Rule SD1042 is only applicable to SENDIG 3.1, not to 3.0 -->
<sdsrule id="SD1042-B" last-update="2019-09-03" originator="FDA" standard="SEND">
<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</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="2019-09-03" 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="2019-09-03" originator="FDA" standard="SEND" 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</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="2019-09-03" 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-07-01" originator="FDA" standard="SEND">
<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.0</igversion>
<igversion>3.1</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 doc(concat($base,$define))//odm:ItemDef[ends-with(@Name,'TRTV')]/@OID 
        where $a = doc(concat($base,$define))//odm:ItemGroupDef[@Name=$name]/odm:ItemRef/@ItemOID
        return $a
    )
    let $trtvname := $definedoc//odm:ItemDef[@OID=$trtvoid]/@Name
    let $vamtoid := (
        for $a in doc(concat($base,$define))//odm:ItemDef[ends-with(@Name,'VAMT')]/@OID 
        where $a = doc(concat($base,$define))//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-07-01" recordnumber="{data($recnum)}">Missing value for {data($trtvname)}, when {data($vamtname)} is not NULL</warning>
]]></rulexquery>
</sdsrule>


<sdsrule id="SD0043-SD" last-update="2020-07-01" originator="FDA" standard="SEND" 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.0</igversion>
<igversion>3.1</igversion>
<domain>INTERVENTIONS</domain>
<rulexquery><![CDATA[	
(: 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 $datasetname := 'EX' :)
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)='EVENTS']
    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-07-01" 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="2019-09-03" originator="FDA" standard="SEND">
<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.0</igversion>
<igversion>3.1</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="2019-09-03" 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="2019-09-03" originator="FDA" standard="SEND" 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.0</igversion>
<igversion>3.1</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="2019-09-03" 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="2019-09-03" originator="FDA" standard="SEND">
<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.0</igversion>
<igversion>3.1</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="2019-03-09" 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="2019-09-03" originator="FDA" standard="SEND" 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.0</igversion>
<igversion>3.1</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="2019-09-03" recordnumber="{data($recnum)}">Missing {data($strescname)}, when {data($rescatname)} value={data($rescatvalue)} is provided in dataset {data($datasetname)}</warning>	
]]></rulexquery>
</sdsrule>

<!-- Rule SD1036 is not applicable to SEND -->

<!-- Rule SD1098 applies to INTERVENTIONS, EVENTS, FINDINGS -->
<sdsrule id="SD1098" last-update="2020-07-01" originator="FDA" standard="SEND">
<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.0</igversion>
<igversion>3.1</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-07-01" 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-07-01" originator="FDA" standard="SEND" 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.0</igversion>
<igversion>3.1</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-07-01" 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="2019-09-03" originator="FDA" standard="SEND">
<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.0</igversion>
<igversion>3.1</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
	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 --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="2019-09-03">Missing {data($catname)} variable, when {data($scatname)} variable is present</warning>
]]></rulexquery>
</sdsrule>


<sdsrule originator="FDA" id="SD2022" standard="SEND" last-update="2019-09-03">
	<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.0</igversion>
	<igversion>3.1</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="2019-09-03" recordnumber="{data($recnum)}">AGEU=null, although AGE or AGETXT is populated</error>	
		]]>
	</rulexquery>
</sdsrule>

<sdsrule id="SD2021" last-update="2019-09-03" originator="FDA" standard="SEND">
<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.0</igversion>
<igversion>3.1</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="2019-09-03" recordnumber="{data($recnum)}">Missing values for both AGE and AGETXT, when AGEU is provided</error>
]]></rulexquery>
</sdsrule>

<!-- Rule SD2004 is not applicable to SEND -->
<!-- Rules SD2016, SD1217, SD1219, SD1221, SD2208, SD2250, SD2251, 
SD2241, SD2242, SD2256, SD2253, SD2254, SD2257, SD2267, SD2269, SD2258, SD2268, 
SD2260, SD2261, SD2263, SD2264, SD2243, SD1323, SD2244 are not applicabel to SEND -->

<!-- TODO: Rule SE1323: TSVAL for FCNTRY record must be a valid term from ISO 3166
see similar rule for COUNTRY in DM -->

<!-- Rules SD2249, SD2266, SD1279, SD0056  are not applicable to SEND -->

<!-- Rule SD1075: "Variables described in IG as not recommended for usage should be not included in the dataset".
This is a nonsense rule! It is only applicable to SEND-IG 3.0 and has been removed for 3.1 -->

<sdsrule id="SD0033" last-update="2019-09-05" originator="FDA" standard="SEND">
<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.0</igversion>
<igversion>3.1</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="2019-09-05" 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="2019-09-05" originator="FDA" standard="SEND" 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.0</igversion>
<igversion>3.1</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="2019-09-05" 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>

<!-- Rule SD1250 is not applicable to SEND -->
<sdsrule originator="FDA" id="SD1250" standard="SEND" last-update="2019-09-05">
	<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="2019-09-05" 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>
