RE: [xsl] find first occurrence of attribute value grouped by element

Subject: RE: [xsl] find first occurrence of attribute value grouped by element
From: "Andrew Welch" <awelch@xxxxxxxxxxxxxxx>
Date: Mon, 23 Sep 2002 17:05:29 +0100
Hi,

You could try the result-tree-fragment approach, which gives you an
easier time keeping track of whats going on.  So, the following code
produces an rtf like this:

   <test id="test1" Unit_id="000001" pft="yes"></test>
   <test id="test2" Unit_id="000001" pft="yes"></test>
   <test id="test1" Unit_id="000001" pft="no"></test>
   ....

You can then query into this using the node-set extension function,
locating the pft attribute (passed first time :) using the id's
provided.

To build this rtf, you need to create a variable as a top level element,
selecting all <test>'s within<testdata>

<xsl:variable name="tests">
  <xsl:for-each select="//testdata/test">
  ...
  </xsl:for-each>
</xsl:variable>

To makes things simple we then need to store the @id so we can compare
it to preceding-siblings, and populate the id attributes:

<xsl:variable name="tests">
  <xsl:for-each select="//testdata/test">
      <xsl:variable name="id" select="@id"/>
      <test id="{@id}" Unit_id="{parent::testdata/@Unit_id}">
       ...
      </test>
  </xsl:for-each>
</xsl:variable>

For the pft attribute, you are basically saying 'if @passed = T and
there are no preceding siblings with @id the same as the current @id
with @passed, then yes otherwise no',

so,

<xsl:attribute name="pft">
  <xsl:choose>
   <xsl:when test="@passed = 'T' and not(preceding-sibling::test[@id =
$id]/@passed = 'T')">yes</xsl:when>
   <xsl:otherwise>no</xsl:otherwise>
  </xsl:choose>
</xsl:attribute>

To put it all together:

<xsl:variable name="tests">
  <xsl:for-each select="//testdata/test">
      <xsl:variable name="id" select="@id"/>
      <test id="{@id}" Unit_id="{parent::testdata/@Unit_id}">
        <xsl:attribute name="pft">
        <xsl:choose>
          <xsl:when test="@passed = 'T' and
not(preceding-sibling::test[@id = $id]/@passed = 'T')">yes</xsl:when>
          <xsl:otherwise>no</xsl:otherwise>
        </xsl:choose>
        </xsl:attribute>
      </test>
  </xsl:for-each>
</xsl:variable>


If you need help querying into the rtf then repost,

cheers
andrew


> 
> Hi,
> 
> This is my first posting to this list site. I have tried to search the
> archives
> and other sites to help with my problem, but have not found anything 
> which addresses it specifically.  Please excuse my newbieness.
> 
> 
> Problem:
> 
> Determine the first occurrence of a particular attribute 
> value grouped by
> element.
> 
> 
> Example data:
> 
> testdata.xml:
> 
> <?xml version="1.0" ?>
> <!-- simplified test data example 1-->
> <root>
> <testdata Unit_id="000001">
>  <test id="test1" passed='F' timestamp="2002-09-01 12:00:00"></test>
>  <test id="test2" passed='T' timestamp="2002-09-01 13:00:00"></test>
>  <test id="test1" passed='T' timestamp="2002-09-02 11:00:00"></test>
> </testdata>
> <testdata Unit_id="000002">
>  <test id="test1" passed='T' timestamp="2002-09-01 10:00:00"></test>
>  <test id="test2" passed='T' timestamp="2002-09-01 13:00:00"></test>
>  <test id="test3" passed='T' timestamp="2002-09-02 11:00:00"></test>
> </testdata>
> <testdata Unit_id="000003">
>  <test id="test1" passed='T' timestamp="2002-09-03 10:00:00"></test>
>  <test id="test3" passed='T' timestamp="2002-09-04 14:00:00"></test>
>  <test id="test2" passed='F' timestamp="2002-09-02 11:00:00"></test>
> </testdata>
> </root>
> 
> 
> Desired output:
> --------------------------------------------------------------
> --------------
> ----------------
> Test     Total Attempts   Total Pass  Total Fail   Pass on 
> First Attempt
> First Run Ratio
> --------------------------------------------------------------
> --------------
> ----------------
> test 1        4              3           1                   2
> 66.7%
> test 2        4              2           2                   1
> 25%
> test 3        2              2           0                   2
> 100%
> 
> 
> My XSLT attempt at producing the listed output:
> 
> testdata.xsl:
> 
> <?xml version="1.0" ?>
> <xsl:stylesheet version="1.0"
> xmlns:xsl="http://www.w3.org/1999/XSL/Transform";>
> <xsl:output method="html"/>
> <xsl:key name="keyTest" match="test" use="@id" />
> <xsl:key name="keyPassed" match="test[@passed='T']" use="@id" />
> <xsl:key name="keyFailed" match="test[@passed='F']" use="@id" />
> 
> <!--  The key that follows must be incorrect...  :(   -->
> <xsl:key name="keyFRP" match="test[@passed='T' and
> not(preceding-sibling::*[@passed='F'])]"  use="@id" />
> 
> <xsl:template match="/">
> 	<html>
> 	 <head>
> 	  <title>Summary Report</title>
> 	 </head>
> 	 <body>
> 	  <table border="1">
> 	   <tr>
> 	    <th>Test</th>
> 	    <th>Total Attempts</th>
> 	    <th>Total Pass</th>
> 	    <th>Total Fail</th>
> 	    <th>Pass on First Attempt</th>
> 	    <th>First Run Ratio</th>
> 	   </tr>
> 	   <testdata>
> 	    <xsl:call-template name="summary_list" />
>            </testdata>	
> 	  </table>			
> 	</body>
>    </html>
> </xsl:template>
> 
> <xsl:template match="//testdata//test[generate-id(.) =
> generate-id(key('keyTest', @id))]"  name="summary_list">      
>  <xsl:for-each select = "//testdata//test[generate-id(.) =
> generate-id(key('keyTest', @id))]" >
>     <!--<xsl:variable name="theTS" select="@id" />-->
>     <xsl:variable name="firstUnit" select="../@Unit_id" /> 
>   <tr>
> 
>    <!-- Display the Test Name!  -->
>    <td align="center"><b><xsl:value-of select = "@id" /></b></td>
> 					
>    <!-- Count all Tests!  -->
>    <td align="center"><xsl:value-of select = 
> "count(key('keyTest',@id))"
> /></td>
> 					
>    <!-- Count the Passes!  -->
>    <td align="center"><xsl:value-of select = 
> "count(key('keyPassed',@id))"
> /></td>      
> 	 
>    <!-- Count the Fails!  -->
>    <td align="center"><xsl:value-of select = 
> "count(key('keyFailed',@id))"
> /></td>      
> 
>    <!-- Try to Count the first pass! --> 
>    <td align="center"><xsl:value-of select = 
> "count(key('keyFRP',@id))"
> /></td>  
> 
>    <!-- Calculate First Run Ratio --> 
>    <xsl:choose>
>     <xsl:when test="count(key('keyFRP',@id))">
>      <td align="right">
>       <xsl:value-of  select = 
> "format-number(count(key('keyFRP',@id)) div
> count(key('keyTest',@id)),'###.###%')" />
>      </td> 
>     </xsl:when>
>     <xsl:otherwise>
>      <td align="right"><xsl:value-of  select = "'0%'" /></td> 
>     </xsl:otherwise>
>    </xsl:choose>
>  
>   </tr>
>  </xsl:for-each>		
> </xsl:template>
> </xsl:stylesheet>
> 
> This XSLT file seemed to work for the first data example, but 
> the second example listed below failed to generate the proper results.
> 
> <?xml version="1.0" ?>
> <!-- simplified test data example 2 -->
> <root>
> <testdata Unit_id="000001">
>  <test id="test1" passed='F' timestamp="2002-09-01 12:00:00"></test>
>  <test id="test2" passed='F' timestamp="2002-09-01 13:00:00"></test>
>  <test id="test1" passed='T' timestamp="2002-09-02 11:00:00"></test>
>  <test id="test2" passed='T' timestamp="2002-09-02 14:00:00"></test>
> </testdata>
> <testdata Unit_id="000002">
>  <test id="test1" passed='T' timestamp="2002-09-01 10:00:00"></test>
>  <test id="test2" passed='T' timestamp="2002-09-01 13:00:00"></test>
>  <test id="test3" passed='T' timestamp="2002-09-02 11:00:00"></test>
> </testdata>
> <testdata Unit_id="000003">
>  <test id="test1" passed='T' timestamp="2002-09-03 10:00:00"></test>
>  <test id="test3" passed='T' timestamp="2002-09-04 14:00:00"></test>
>  <test id="test2" passed='F' timestamp="2002-09-02 11:00:00"></test>
>  <test id="test4" passed='T' timestamp="2002-09-07 11:00:00"></test>
> </testdata>
> </root>
> 
> 
> It did seem to work for the first data set, but it failed to work 
> correctly for the second data set! 
> 
> I was thinking that if I counted the passes for a test for a 
> given unit_id 
> where a fail had not occurred, that this would give the 
> desired result. 
> 
> I know that the key that I have created for the first pass occurrence
> is not correct.  The 'test4' is not counted as a first run 
> pass for some
> reason.  I am not sure what the proper key should be. 
> 
> I had not used the timestamp data because I am assuming that 
> a fail will 
> not occur after a pass of a particular test, and that a test 
> would not 
> be taken after a pass had occurred.
> 
> 
> What would the proper approach be to solve this problem?
> 
> Any help will be greatly appreciated!
> Thanks,
> John Pallister
> jpallister@xxxxxxxxxxxx
> 
>  XSL-List info and archive:  http://www.mulberrytech.com/xsl/xsl-list
> 
> 
> 
> ---
> Incoming mail is certified Virus Free.
> Checked by AVG anti-virus system (http://www.grisoft.com).
> Version: 6.0.391 / Virus Database: 222 - Release Date: 19/09/2002
>  
> 

---
Outgoing mail is certified Virus Free.
Checked by AVG anti-virus system (http://www.grisoft.com).
Version: 6.0.391 / Virus Database: 222 - Release Date: 19/09/2002
 

 XSL-List info and archive:  http://www.mulberrytech.com/xsl/xsl-list


Current Thread