Page 1 of 1

Check Overlapping Time Periods with Schematron

Posted: Tue Mar 13, 2018 5:32 pm
by Sepp C
Hi,

I have a very similar problem to topic15574.html as I need to check for overlapping time periods with Schematron.

I have following additional constraints:
  • - Time Periods are grouped by an ID
  • - The overlap check only applies on a per-group basis. This means that between unrelated groups overlaps are allowed
  • - The End element is optional. Because of this I cannot use the solution from the other thread. Oxygen runs in a Stack-Overflow Exception because of the recursive call.
  • - When the end element is missing, it must be treated as '9999-12-31'
My schema looks like this, this would be a valid input file:

Code: Select all


<periods>
<period>
<id>1</id>
<start>2017-01-01</start>
</period>
<period>
<id>1</id>
<start>2000-01-01</start>
<end>2010-12-31</end>
</period>
<period>
<id>2</id>
<start>2000-01-01</start>
<end>2017-12-31</end>
</period>
</periods>
Thanks for any help

Re: Check Overlapping Time Periods with Schematron

Posted: Wed Mar 14, 2018 3:39 pm
by tavy
Hello,

To avoid the Stack-Overflow Exception you need to collect the start and end date from the 'start' and 'end' element instead of the attributes. Something like in the example below.
For the other changes that you want to make in the function, unfortunately I cannot help you. I suggest you to write on the XSL-List, or on Stackoverflow.

Code: Select all


<?xml version="1.0" encoding="UTF-8"?>
<sch:schema xmlns:sch="http://purl.oclc.org/dsdl/schematron" queryBinding="xslt2"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:sqf="http://www.schematron-quickfix.com/validator/process">

<sch:ns prefix="fct" uri="localFunctions"/>

<sch:pattern>
<sch:rule context="periods">
<sch:let name="overlapping" value="fct:checkOverlaping((),period)"/>
<sch:assert test="$overlapping=''">
Overlapping period: <sch:value-of select="$overlapping"/></sch:assert>
</sch:rule>
</sch:pattern>

<xsl:function name="fct:checkOverlaping" >
<xsl:param name="currentEndDate" as="xs:date?"/>
<xsl:param name="periods" as="node()*"/>
<xsl:choose>
<xsl:when test="not(exists($currentEndDate))">
<xsl:variable name="orderedPeriods" as="node()*">
<xsl:for-each select="$periods">
<xsl:sort select="start"/>
<xsl:copy-of select="."/>
</xsl:for-each>
</xsl:variable>
<xsl:value-of select="fct:checkOverlaping(xs:date($orderedPeriods[position()=1]/end),$orderedPeriods[position()>1])"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="
if (not(exists($periods))) then '' else
if ($currentEndDate > $periods[position()=1]/start)
then concat('End: ', $currentEndDate, ' Start: ', $periods[position()=1]/start) else
fct:checkOverlaping($periods[position()=1]/end,$periods[position()>1])"/>
</xsl:otherwise>
</xsl:choose>
</xsl:function>
</sch:schema>
Best Regards,
Octavian

Re: Check Overlapping Time Periods with Schematron

Posted: Wed Mar 14, 2018 4:42 pm
by Sepp C
Hi Octavian

I had changed the attibutes @ to elements already (of course). Anyway, found a solution. Had to interject my wished maxvalue directly in the calling of the function, like that:

Code: Select all


<?xml version="1.0" encoding="UTF-8"?>
<sch:schema xmlns:sch="http://purl.oclc.org/dsdl/schematron" queryBinding="xslt2"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:sqf="http://www.schematron-quickfix.com/validator/process">

<sch:ns prefix="fct" uri="localFunctions"/>

<sch:pattern>
<sch:rule context="periods">
<sch:let name="overlapping" value="fct:checkOverlaping((),period)"/>
<sch:assert test="$overlapping=''">
Overlapping period: <sch:value-of select="$overlapping"/></sch:assert>
</sch:rule>
</sch:pattern>

<xsl:function name="fct:checkOverlaping" >
<xsl:param name="currentEndDate" as="xs:date?"/>
<xsl:param name="periods" as="node()*"/>
<xsl:choose>
<xsl:when test="not(exists($currentEndDate))">
<xsl:variable name="orderedPeriods" as="node()*">
<xsl:for-each select="$periods">
<xsl:sort select="@start"/>
<xsl:copy-of select="."/>
</xsl:for-each>
</xsl:variable>
==> <xsl:value-of select="fct:checkOverlaping(if (exists($orderedPeriods[position() = 1]/validUntil)) then xs:date($orderedPeriods[position() = 1]/validUntil) else xs:date('9999-12-31'), $orderedPeriods[position() > 1])"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="
if (not(exists($periods))) then '' else
if ($currentEndDate > $periods[position()=1]/@start)
then concat('End: ', $currentEndDate, ' Start: ', $periods[position()=1]/@start) else
fct:checkOverlaping($periods[position()=1]/@end,$periods[position()>1])"/>
</xsl:otherwise>
</xsl:choose>
</xsl:function>
</sch:schema>
Thanks!