Solved

CONVERT XML DATA TO DISPLAY AS TIMETABLE USING XSLT

Posted on 2011-03-10
23
1,075 Views
Last Modified: 2013-11-18
How would you write a XSLT file to convert file stml.xml and display as timetable like you can see in file tt.png ? Of course XSLT must be sensetive for changes in XML file. stml.xml
tt.png
0
Comment
Question by:ZAPASCO
  • 12
  • 10
23 Comments
 
LVL 27

Expert Comment

by:BigRat
Comment Utility
Probably by first using a 24 hour clock!

I presume an HTML output?

If so the classes ought to be one-column tables which I position on the page using CSS positioning according to the start time and whose height I also calculate using the finish time.

Of course the biggest problem with this sort of thing is error handling. On line 49 of the XML you have <day>Fiday</day> which is wrong. One is tempted to do this rather with script - far better error handling and control. XSLT can be terribly awkward and arcane at times - and this is probably one of them.
0
 

Author Comment

by:ZAPASCO
Comment Utility
Yes, with 24 hour clock it is really good idea, so it is easy to sort by hours*100+mins. but the biggest problem is rows when the class start 14.30 and finishes 16.30 as this should be done in the table.
How would you do this ?
0
 
LVL 60

Expert Comment

by:Geert Bormans
Comment Utility
Well, this can be easily done using XSLT, but can you first tell us how you will execute the XSLT.
It would be nice if you could use XSLT2, it would make life easier.
I can make an example if you can use XSLT2, I don't bother if you need XSLT1, but I can tell you how I would do it using XSLT1

I would make a row in a table for every minute or every 5 minutes (or whatever precision you need)
and for each event I would calculate the row span.
I would get all the events using a key combining start time and day
I would also check for other events starting within the same period (sequence or collection of possible start times in the reserved block) using the same key
(or don't you allow the possibility of having multiple events going on at the same time)
You could pre calculate the maximum number of joint event per colum and possibly split the columns before hand

XSLT2 gives you the power of date types so calculating time differences is a lot easier than XSLT1
XSLT2 also gives you functions
As I said I would help if you can use XSLT2, if not you'll have to sort most of it out yourself (though I can assist in the process)

I suggest that you add a first step for validating the data
Make a little schema to make sure that you can't have stuff such as Fiday (use an enumeration on the date)
And as BigRat suggests, either use a 24 hour clock or add a field stating pm/am, your data is currently ambiguous.

I would also add a preprocess step that slightly changes your data, to make the times start or end at a precision boundary,
(and maybe correct the ambiguous times)
0
 
LVL 60

Expert Comment

by:Geert Bormans
Comment Utility
To answer your follow up question
If you do this using a row per minute (or five, or ten, whatever precission you need)
you can easily calculate the number of rows for a block and get the rowspan
The block will fall into place automatically
0
 
LVL 60

Expert Comment

by:Geert Bormans
Comment Utility
For the rows, you can simply iterate over a sequence that contains the dates
0
 

Author Comment

by:ZAPASCO
Comment Utility
Row should be done every 30 minutes, of course i have got dtd for this file but didn't attached it. I attached corrected XML file. I would prefer XSLT 1 (easier to run), but if you can make example of solution in XSLT2 that would be great.
stml.xml
0
 
LVL 60

Expert Comment

by:Geert Bormans
Comment Utility
Well, a DTD is not sufficient to exclude the errors in the days of course
(enumeration works on every simple type in XSD, only on attributes in DTD)

Give me a couple of hours, I need to finish some customer stuff first

Can you have classes at the same time (do you possibly need double columns?)
0
 

Author Comment

by:ZAPASCO
Comment Utility
This rowspan thing is really compicated in this case i think so. I was just thinking of simple solution like checking for every 30 mins row if there is a class, if not just leave empty space in other case just put appropriate data. what do you think ?
0
 

Author Comment

by:ZAPASCO
Comment Utility
no, you can't have two classes at the same time.
0
 
LVL 60

Expert Comment

by:Geert Bormans
Comment Utility
Well, using the rowspan avoids data being put in twice or more.
Basically it is easy to calculate the rowspan.
A class (information added) starts when the start time of the class equals the start time of the cell (or in the first half hour after it)
And based on the end time you can easily calculate the row span
The next row in the calculation does not need a cell, when the cell start time falls in the period start-to-end of an event

You can make this easier to swallow by simply doing a preprocess, listing every cell start time that is touched by an event

Split columns are a lot harder than the row spans actually. Do you need them?

Why do you consider XSLT1 easier to execute?
Do you need it in a browser, or in .Net?
Or do you have a specific processor in mind?
Can you answer me how you want to execute the XSLT, it would help in the requirements

If I post a XSLT2 solution, it will be very very tough to port that to XSLT1,
I need to be sure that the effort is well spent
0
 
LVL 60

Expert Comment

by:Geert Bormans
Comment Utility
OK, missed your answer on the classes at the same time, so it won't be too hard actually
0
How your wiki can always stay up-to-date

Quip doubles as a “living” wiki and a project management tool that evolves with your organization. As you finish projects in Quip, the work remains, easily accessible to all team members, new and old.
- Increase transparency
- Onboard new hires faster
- Access from mobile/offline

 

Author Comment

by:ZAPASCO
Comment Utility
Both XSLT 1 AND XSLT 2 are cool !
0
 

Author Comment

by:ZAPASCO
Comment Utility
That's fine, I just realized that XSLT 2 is of course right for this. I just had a problem as my software was a little bit too old, I just updated to newer version and everthing is good ! So any ideas for a solution ?
0
 
LVL 60

Expert Comment

by:Geert Bormans
Comment Utility
Had more work than I expected for my customer.
I coincidentally just started working on it.
It is past midnight here, so I might not end this today, will see
0
 
LVL 60

Accepted Solution

by:
Geert Bormans earned 500 total points
Comment Utility
This should get you started
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema" 
    xmlns:pfn="com:c-moria:xslt:functions"
    exclude-result-prefixes="xs pfn"
    version="2.0">
    
    <xsl:param name="table-start" as="xs:time">09:00:00</xsl:param><!-- start time of the table -->
    <xsl:param name="table-end" as="xs:time">18:00:00</xsl:param><!-- end time of the table -->
    <xsl:param name="precision" as="xs:integer">30</xsl:param><!-- precision in minutes -->
    
    <xsl:param name="days" select="('Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday')"/><!-- a sequence, list of days used  -->
    
    <xsl:variable name="time-dur-precision" select="xs:dayTimeDuration(concat('PT', $precision, 'M'))"></xsl:variable>
    <xsl:variable name="num-of-rows" select="xs:integer(floor(xs:dayTimeDuration($table-end - $table-start) div $time-dur-precision))" as="xs:integer"/>
    
    <xsl:variable name="rows" as="xs:time+">
        <xsl:sequence select="for $i in 1 to $num-of-rows return ($table-start + ($i - 1) * $time-dur-precision)"/>
     </xsl:variable>
    
    <xsl:output indent="yes"></xsl:output>
    
    <xsl:key name="event" match="active_cell" use="concat(ancestor::class/day, .)"/>
    
    <xsl:variable name="step1">
        <xsl:apply-templates select="/node()" mode="preproc"/>
    </xsl:variable>
    
    <xsl:template match="/">
        <xsl:call-template name="make-grid"/>
    </xsl:template>
    
    
    <xsl:template name="make-grid">
        <table border="1">
            <tr>
                <th></th>
                <xsl:for-each select="$days">
                    <th><xsl:value-of select="."/></th>
                </xsl:for-each>
            </tr>
            <xsl:for-each select="$rows">
                <xsl:variable name="this-row" select="."/>
                <tr>
                    <th><xsl:value-of select="."/></th>
                    <xsl:for-each select="$days">
                        <xsl:choose>
                            <xsl:when test="key('event', concat(lower-case(.), $this-row), $step1)">
                                <xsl:variable name="this-active-cell" select="key('event', concat(lower-case(.), $this-row), $step1)"/>
                                <xsl:if test="not($this-active-cell/preceding-sibling::active_cell)">
                                    <td rowspan="{count($this-active-cell/parent::*/active_cell)}">
                                        <xsl:value-of select="$this-active-cell/ancestor::class/CRN"/>
                                    </td>
                                </xsl:if>
                            </xsl:when>
                            <xsl:otherwise>                        
                                <td> </td>
                            </xsl:otherwise>
                        </xsl:choose>
                    </xsl:for-each>
                </tr>
            </xsl:for-each>
        </table>
    </xsl:template>
    
    <xsl:template match="node()" mode="preproc">
        <xsl:copy>
            <xsl:copy-of select="@*"/>
            <xsl:apply-templates select="node()" mode="preproc"/>
        </xsl:copy>
    </xsl:template>

    <xsl:template match="day" mode="preproc">
        <xsl:copy>
            <xsl:copy-of select="@*"/>
            <xsl:value-of select="lower-case(.)"/>
        </xsl:copy>
    </xsl:template>
    
    <xsl:template match="finish_time" mode="preproc">
        <xsl:copy>
            <xsl:copy-of select="@*"/>
            <xsl:value-of select="pfn:timify(.)"/>
        </xsl:copy>
    </xsl:template>
    
    <xsl:template match="start_time" mode="preproc">
        <xsl:variable name="parent-node" select="parent::class"/>
        <xsl:copy>
            <xsl:copy-of select="@*"/>
            <xsl:value-of select="pfn:timify(.)"/>
        </xsl:copy>
        <xsl:element name="active_cells">
             <xsl:for-each select="$rows">
                <xsl:if test="(. + $time-dur-precision) gt pfn:timify($parent-node/start_time) and . lt pfn:timify($parent-node/start_time)">
                    <xsl:element name="active_cell"><xsl:value-of select="."/></xsl:element>
                </xsl:if>
                <xsl:if test=". ge pfn:timify($parent-node/start_time) and . lt pfn:timify($parent-node/finish_time)">
                    <xsl:element name="active_cell"><xsl:value-of select="."/></xsl:element>
                </xsl:if>
            </xsl:for-each>
        </xsl:element>
    </xsl:template>
    
    <xsl:function name="pfn:timify" as="xs:time">
        <xsl:param name="time"/>
        <xsl:variable name="norm-time">
            <xsl:analyze-string select="normalize-space($time)" regex="^(\d+):(\d+).*$">
                <xsl:matching-substring>
                    <xsl:value-of select="concat( format-number(number(regex-group(1)), '00') ,':' , format-number(number(regex-group(2)), '00'), ':00')"></xsl:value-of>
                </xsl:matching-substring>
            </xsl:analyze-string>
        </xsl:variable>
        <xsl:value-of select="xs:time($norm-time)"/>
    </xsl:function>
    
</xsl:stylesheet>

Open in new window

0
 

Author Comment

by:ZAPASCO
Comment Utility
it looks great, but when i want to run it in Firefox 4, it says: Error during XSLT transformation: An XPath expression was expected to return a NodeSet. What should i do to run it ? Of course points for you only tell me how to give them to you ? ! :D Thanks
0
 
LVL 60

Expert Comment

by:Geert Bormans
Comment Utility
Ah, it is XSLT2, you can't run it in FireFox (that is why I asked how you planned to execute it)
Best you can do is download Saxon 9.3HE from
http://saxon.sourceforge.net/

and see how it is run from the command line here

http://www.saxonica.com/documentation/about/gettingstarted/gettingstartedjava.xml

save the XML as a file on  the filesystem, do the same with the XSLT and run the above mentioned command line
(put the jar for "he" on the class path or run with -cp as suggested)
0
 
LVL 60

Expert Comment

by:Geert Bormans
Comment Utility
Why would you delete?
I worked three hours to get you a good solution.
You should accept with a tripple A instead
0
 

Author Comment

by:ZAPASCO
Comment Utility
Of course i want to give a grade but it is tricky, I really like it so should I mark A B C as the best one ?
and then got questions  ie. Was the solution complete? and three choices but nothing next to it, so what should i mark ? top middle or bottom ?

By the way what tips would you give me for XML 1 ? thanks a lot ! really appreciate it !
0
 
LVL 60

Expert Comment

by:Geert Bormans
Comment Utility
Ah, I see,
If the solution is complete and working (I tested it, it does) you should grade A.
About the questions, I don't know, when in doubt, thick the top :-)

What tips are you looking for?

General tips on XML?
There is a lot on www.xml.com

references to XSLT1 books?
this one is a very good learning book
http://my.safaribooksonline.com/book/databases/xml/0596000537
the latest edition also covers XSLT2
http://my.safaribooksonline.com/9780596527211

or do you want hints on how to redo the stuff in XSLT1?
I strongly discourage you to do this for the following reasons
- XSLT1 has no time datatypes... it is very inconvenient for comparing times
- XSLT1 has no concept of an intermediate tree storage (cfr. $step1) unless nodeset extensions
- XSLT1 has no functions, only named templates, that is a bit unconvenient here
- XSLT1 has no sequences, so it is tougher to make a $rows iterator, or a $days iterator

If you really want to go down that route, because you want to serve up XML with XSLT for a browser,
then I recommend that you have two steps
An XSLT2 step on the server for doing the difficult bits
A browser XSLT1 for some late minute layouting or reordering
0
 

Author Comment

by:ZAPASCO
Comment Utility
I just will have to find my own solution, I just wanted some idea, now it is time for me to practise.... as practise makes perfect, as some people say, and i know that :D

I have seen some useful functions at http://www.w3schools.com/xpath/xpath_functions.asp and gotto make sure that it works in the browser.
0
 
LVL 60

Expert Comment

by:Geert Bormans
Comment Utility
the problem with W3schools is that they fail to say...
most of the functions are XSLT2 and will not work in a browser.
Please take my advise serious... do the bulk of the work outside the browser, using XSLT2
0
 

Author Comment

by:ZAPASCO
Comment Utility
I am just trying to do it in 1.0 found quite useful website:
http://zvon.org/xxl/XSLTreference/Output/xpathFunctionIndex.html

I am  new to XSLT so I should start with sth easy then keep going to harder staff. Now it is time to start with basics, by the way I am really impressed by your knowledge, you must have quite a lot of experience. I am 24 now, when I was 13 I started to make first money on html, php, mysql and things like domains, servers, ads, but last 5 years "wasted" as i wasn't doing too much IT staff, you know, GIRLS :). Now trying spend whole days on practising and get a nice job. Any ideas what should I practise most this time ? Now I would like to find job in Internet Application Development, in few years maybe move to Financial Services when get bored with Internet Applications.
0

Featured Post

IT, Stop Being Called Into Every Meeting

Highfive is so simple that setting up every meeting room takes just minutes and every employee will be able to start or join a call from any room with ease. Never be called into a meeting just to get it started again. This is how video conferencing should work!

Join & Write a Comment

Preface This article introduces an authentication and authorization system for a website.  It is understood by the author and the project contributors that there is no such thing as a "one size fits all" system.  That being said, there is a certa…
Preface This is the third article about the EE Collaborative Login Project. A Better Website Login System (http://www.experts-exchange.com/A_2902.html) introduces the Login System and shows how to implement a login page. The EE Collaborative Logi…
The viewer will learn the basics of jQuery including how to code hide show and toggles. Reference your jQuery libraries: (CODE) Include your new external js/jQuery file: (CODE) Write your first lines of code to setup your site for jQuery…
The viewer will learn how to create a basic form using some HTML5 and PHP for later processing. Set up your basic HTML file. Open your form tag and set the method and action attributes.: (CODE) Set up your first few inputs one for the name and …

771 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

10 Experts available now in Live!

Get 1:1 Help Now