Background: I'm collecting usability statistics for a group of applications. Each count has the following attributes: date, application, major heading, minor heading, count. My intent is to pull this back as XML and render it in to an HTML table.
I have all this working and all the files are below. I would value any feedback.
A simple example of the data looks like this:
<counts-report>
<counts date="2004-10-07">
<application name="App A">
<major name="Major A">
<minor name="Minor A">1</minor>
<minor name="Minor C">7</minor>
</major>
</application>
</counts>
<counts date="2004-10-08">
<application name="App A">
<major name="Major A">
<minor name="Minor A">1</minor>
<minor name="Minor B">3</minor>
</major>
<major name="Major B">
<minor name="Minor A">15</minor>
</major>
</application>
</counts>
</counts-report>
The output I am looking for is something like:
+------------+---------------------------------------+
| | App A |
| +---------------------------------------+
| | Major A | Major B |
| +---------+---------+---------+---------+
| | Minor A | Minor B | Minor C | Minor A |
+------------+---------+---------+---------+---------+
| 2004-10-07 | 1 | | | |
+------------+---------+---------+---------+---------+
| 2004-10-07 | 1 | 3 | 7 | 15 |
+------------+---------+---------+---------+---------+
| Totals | 2 | 3 | 7 | 15 |
+------------+---------+---------+---------+---------+
Now I looked around for solutions that would let me tabularize truely sparese data but didn't have any luck and no elegant solutions came to mind.
The only solution I could come up with was to somehow massage the data beforehand. The two choices I explored were
1) Turning it in to a non-sparse data set
2) Put a "domain" entry in front listing the union of all
I went with #2 (mainly because for real world data #1 would inflate the size of the file significantly). So the final file looks like:
<counts-report>
<domain>
<application name="App A">
<major name="Major A">
<minor name="Minor A"/>
<minor name="Minor B"/>
<minor name="Minor C"/>
</major>
<major name="Major B>
<minor name="Minor A"/>
</major>
</application>
</domain>
<counts date="2004-10-07">
<application name="App A">
<major name="Major A">
<minor name="Minor A">1</minor>
<minor name="Minor C">7</minor>
</major>
</application>
</counts>
<counts date="2004-10-08">
<application name="App A">
<major name="Major A">
<minor name="Minor A">1</minor>
<minor name="Minor B">3</minor>
</major>
<major name="Major B">
<minor name="Minor A">15</minor>
</major>
</application>
</counts>
</counts-report>
The XSLT I am using is at the end of this message. This is my first non-trivial XSLT experiment so I would appreciate any comments. A couple of specific questions:
1) I wanted to put in blank cells instead of "-". I couldn't get the parsers to accept the (verbatim) in the xslt file (line 60)
2) I used two templates for minor and non-minor table headers because the colpan value was comming out to zero when (@line 27 & 34... referenced at 11, 12, and 13).
3) I access the actual data for each row by constructing a path expression using each "minor" entry found in the domain (line 55).
I would also value any stylistic comments.
Thanks in advance for any help
Charlie
1: <?xml version="1.0" encoding="UTF-8"?>
2: <xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 3: <xsl
utput method="html" version="1.0" encoding="UTF-8" indent="yes"/> 4: <xsl:template match="/counts-report"> 5: <html> 6: <head> 7: <title>My Table</title> 8: </head> 9: <body> 10: <table border="1" cellpadding="5"> 11: <tr> <th rowspan="3">Date</th> <xsl:apply-templates mode="header" select="domain/application"/> </tr> 12: <tr> <xsl:apply-templates mode="header" select="domain/application/major"/> </tr> 13: <tr> <xsl:apply-templates mode="header" select="domain/application/major/minor"/> </tr> 14:
15: <xsl:apply-templates mode="data" select="counts"/> 16:
17: <tr>
18: <th> Totals </th>
19: <xsl:apply-templates mode="totals"
select="domain/application/major/minor"/>
20: </tr>
21:
22: </table>
23: </body>
24: </html>
25: </xsl:template>
26:
27: <xsl:template mode="header" match="*">
28: <xsl:element name="th">
29: <xsl:attribute name="colspan"><xsl:value-of
select="count(.//minor)"/></xsl:attribute>
30: <xsl:value-of select="@name"/>
31: </xsl:element>
32: </xsl:template>
33:
34: <xsl:template mode="header" match="minor">
35: <xsl:element name="th">
36: <xsl:value-of select="@name"/>
37: </xsl:element>
38: </xsl:template>
39:
40: <xsl:template mode="data" match="counts">
41: <xsl:variable name="rownum" select="position"/> 42:
43: <xsl:element name="tr">
44: <xsl:if test="$rownum mod 2 = 0">
45: <xsl:attribute name="style">background-color: rgb(0, 204,
204);</xsl:attribute>
46: </xsl:if>
47:
48: <th> <xsl:value-of select="@date"/> </th>
49:
50: <xsl:for-each
select="/counts-report/domain/application/major/minor"> 51: <xsl:variable name="app" select="../../@name"/> 52: <xsl:variable name="major" select="../@name"/> 53: <xsl:variable name="minor" select="@name"/> 54:
55: <xsl:variable name="value"
select="/counts-report/counts[$rownum]/application[@name=$app]/major[@name=$major]/minor[@name=$minor]"/> 56:
57: <td>
58: <xsl:choose>
59: <xsl:when test="$value"> <xsl:value-of
select="$value"/> </xsl:when>
60: <xsl
therwise> - </xsl
therwise>
61: </xsl:choose>
62: </td>
63:
64: </xsl:for-each>
65:
66: </xsl:element>
67:
68: </xsl:template>
69:
70: <xsl:template mode="totals" match="minor">
71: <xsl
aram name="app" select="../../@name"/>
72: <xsl
aram name="major" select="../@name"/>
73: <xsl
aram name="minor" select="@name"/>
74:
75: <td>
76: <xsl:value-of
select="sum(/counts-report/counts/application[@name=$app]/major[@name=$major]/minor[@name=$minor])"/> 77: </td> 78: </xsl:template> 79:
80: </xsl:stylesheet>
I have all this working and all the files are below. I would value any feedback.
A simple example of the data looks like this:
<counts-report>
<counts date="2004-10-07">
<application name="App A">
<major name="Major A">
<minor name="Minor A">1</minor>
<minor name="Minor C">7</minor>
</major>
</application>
</counts>
<counts date="2004-10-08">
<application name="App A">
<major name="Major A">
<minor name="Minor A">1</minor>
<minor name="Minor B">3</minor>
</major>
<major name="Major B">
<minor name="Minor A">15</minor>
</major>
</application>
</counts>
</counts-report>
The output I am looking for is something like:
+------------+---------------------------------------+
| | App A |
| +---------------------------------------+
| | Major A | Major B |
| +---------+---------+---------+---------+
| | Minor A | Minor B | Minor C | Minor A |
+------------+---------+---------+---------+---------+
| 2004-10-07 | 1 | | | |
+------------+---------+---------+---------+---------+
| 2004-10-07 | 1 | 3 | 7 | 15 |
+------------+---------+---------+---------+---------+
| Totals | 2 | 3 | 7 | 15 |
+------------+---------+---------+---------+---------+
Now I looked around for solutions that would let me tabularize truely sparese data but didn't have any luck and no elegant solutions came to mind.
The only solution I could come up with was to somehow massage the data beforehand. The two choices I explored were
1) Turning it in to a non-sparse data set
2) Put a "domain" entry in front listing the union of all
I went with #2 (mainly because for real world data #1 would inflate the size of the file significantly). So the final file looks like:
<counts-report>
<domain>
<application name="App A">
<major name="Major A">
<minor name="Minor A"/>
<minor name="Minor B"/>
<minor name="Minor C"/>
</major>
<major name="Major B>
<minor name="Minor A"/>
</major>
</application>
</domain>
<counts date="2004-10-07">
<application name="App A">
<major name="Major A">
<minor name="Minor A">1</minor>
<minor name="Minor C">7</minor>
</major>
</application>
</counts>
<counts date="2004-10-08">
<application name="App A">
<major name="Major A">
<minor name="Minor A">1</minor>
<minor name="Minor B">3</minor>
</major>
<major name="Major B">
<minor name="Minor A">15</minor>
</major>
</application>
</counts>
</counts-report>
The XSLT I am using is at the end of this message. This is my first non-trivial XSLT experiment so I would appreciate any comments. A couple of specific questions:
1) I wanted to put in blank cells instead of "-". I couldn't get the parsers to accept the (verbatim) in the xslt file (line 60)
2) I used two templates for minor and non-minor table headers because the colpan value was comming out to zero when (@line 27 & 34... referenced at 11, 12, and 13).
3) I access the actual data for each row by constructing a path expression using each "minor" entry found in the domain (line 55).
I would also value any stylistic comments.
Thanks in advance for any help
Charlie
1: <?xml version="1.0" encoding="UTF-8"?>
2: <xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 3: <xsl
15: <xsl:apply-templates mode="data" select="counts"/> 16:
17: <tr>
18: <th> Totals </th>
19: <xsl:apply-templates mode="totals"
select="domain/application/major/minor"/>
20: </tr>
21:
22: </table>
23: </body>
24: </html>
25: </xsl:template>
26:
27: <xsl:template mode="header" match="*">
28: <xsl:element name="th">
29: <xsl:attribute name="colspan"><xsl:value-of
select="count(.//minor)"/></xsl:attribute>
30: <xsl:value-of select="@name"/>
31: </xsl:element>
32: </xsl:template>
33:
34: <xsl:template mode="header" match="minor">
35: <xsl:element name="th">
36: <xsl:value-of select="@name"/>
37: </xsl:element>
38: </xsl:template>
39:
40: <xsl:template mode="data" match="counts">
41: <xsl:variable name="rownum" select="position"/> 42:
43: <xsl:element name="tr">
44: <xsl:if test="$rownum mod 2 = 0">
45: <xsl:attribute name="style">background-color: rgb(0, 204,
204);</xsl:attribute>
46: </xsl:if>
47:
48: <th> <xsl:value-of select="@date"/> </th>
49:
50: <xsl:for-each
select="/counts-report/domain/application/major/minor"> 51: <xsl:variable name="app" select="../../@name"/> 52: <xsl:variable name="major" select="../@name"/> 53: <xsl:variable name="minor" select="@name"/> 54:
55: <xsl:variable name="value"
select="/counts-report/counts[$rownum]/application[@name=$app]/major[@name=$major]/minor[@name=$minor]"/> 56:
57: <td>
58: <xsl:choose>
59: <xsl:when test="$value"> <xsl:value-of
select="$value"/> </xsl:when>
60: <xsl
61: </xsl:choose>
62: </td>
63:
64: </xsl:for-each>
65:
66: </xsl:element>
67:
68: </xsl:template>
69:
70: <xsl:template mode="totals" match="minor">
71: <xsl
72: <xsl
73: <xsl
74:
75: <td>
76: <xsl:value-of
select="sum(/counts-report/counts/application[@name=$app]/major[@name=$major]/minor[@name=$minor])"/> 77: </td> 78: </xsl:template> 79:
80: </xsl:stylesheet>