Vinkjeller
XML og XPATH
XMLfila er bygget opp slik:
<?xml version="1.0" encoding="utf-8"?> <wines> <wine> <type>sparkling</type> <name>Gosset Grande Réserve Brut</name> <catalog>32299</catalog> <country>Frankrike</country> <volume>75</volume> <price>259.00</price> <dice>5</dice> <description>Duften minner mest om epler</description> </wine> ... </wines>
Rekkefølgen av viner i basisfila er for alle praktiske formål tilfeldig.
Vi ønsker å formulere XPath-uttrykk for å lage forskjellige utdrag fra denne XML-fila, f.eks.
Antall viner i lista:
count(//wine)
Alle franske hvitviner:
//wine[country='Frankrike' and type='white']
Alle franske rødviner på kartong(?):
//wine[country='Frankrike' and type='red' and volume=300]
Alle italienske hvitviner med terningkast 6:
//wine[country='Italia' and type='white' and dice=6]
Beskrivelsen av alle franske hvitviner med terningkast 6:
//wine[country='Frankrike' and type='white' and dice=6]/description/text()
Antall franske rødviner på flaske
count(//wine[country='Frankrike' and type='red' and volume=75])
Gjennomsnittsprisen på alle franske rødviner på flaske
sum(//wine[country='Frankrike' and type='red' and volume=75]/price) div count(//wine[country='Frankrike' and code='red' and volume=75])
Alle chilenske rødviner som innholder fyldig i beskrivelsen
//wine[country='Chile' and type='red' and contains(description,'fyldig')]
... og hva mer du måtte finne på
Merk at vi har brukt //wine for å få takk i alle viner. Alternativt kunne vi skrive /winelist/wine. I dette tilfelle gjør det ingen forskjell siden alle viner (//wine) er nøyaktig samme nodesett som alle viner under winelist(/winelist/wine).
Vi forsøker å bruke XPath i en XSLT-transformasjon. Vi vil lage en HTML-fil som presenterer et utvalg av viner. Det første vi gjør er å lage en basis fil som kan se slik ut:
<?xml version="1.0" encoding="ISO-8859-1"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output method="xml" encoding="ISO-8859-1" indent="yes" omit-xml-declaration="yes" doctype-public="-//W3C//DTD XHTML 1.0 Strict//EN" doctype-system="http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"/> <xsl:param name="mySelection" select="//wines/wine[country='Spania']"/> <xsl:template match="/"> <html> <head> <title>Vinliste</title> <link rel="STYLESHEET" href="vin.css"/> </head> <body> <h1>Vinliste</h1> <xsl:call-template name="ingress"/> <!-- here is where we do the patch --> <xsl:apply-templates select="$mySelection"> <xsl:sort select="type" order="ascending"/> <xsl:sort select="dice" order="descending"/> <xsl:sort select="price/volume" order="descending"/> </xsl:apply-templates> </body> </html> </xsl:template> <xsl:template name="ingress"> <p class="ingress"> <xsl:value-of select="count(//wine)"/> vinanmeldelser samlet fra dagspessen for demonstrasjonsformål. Ikke for kommersiell bruk.<br/> Børre S, mai.2006 </p> <hr size="1"/> </xsl:template> <xsl:template match="//wine"> <div class="wine"> <div style="float:left"> <xsl:element name="img"> <xsl:attribute name="src"> <xsl:value-of select="type"/> <xsl:value-of select="dice"/>.gif</xsl:attribute> <xsl:attribute name="alt"> dice</xsl:attribute> </xsl:element> </div> <div class="wine-name"> <xsl:value-of select="name"/> </div> <div class="wine-country"> <xsl:value-of select="country"/> <span class="wine-price"> : kr <xsl:value-of select="price"/> / <xsl:value-of select="volume"/>cl </span> </div> <div style="clear:left"/> <div class="wine-desc"> <xsl:value-of select="description"/> </div> </div> </xsl:template> </xsl:stylesheet>
lxml og parameter
I modulen Parametere demonstreres hvordan det er mulig å parameterstyre XSLT-transformasjoner. Vi prøver dette også her.
Vi kan f.eks. bruke følgende script (vinselect2.py):
#! /usr/bin/python import sys,cgi from lxml import etree import cgitb; cgitb.enable() #------------------------------------------------------------- # Return extracts from a winelist # See: http://lxml.de/xpathxslt.html # B. Stenseth 2011 #------------------------------------------------------------- def produce(xp): # fixed filenames xmlfile='viner.xml' xsltfile='tohtml_param.xslt' htmlfile='result.html' xmlTree=etree.parse(xmlfile) xsltTree=etree.parse(xsltfile) transform=etree.XSLT(xsltTree) resultTree=transform(xmlTree,mySelection=xp) print str(resultTree) #----------------------------------------- print "Content-type: text/html; charset=iso-8859-1\n" form=cgi.FieldStorage() # what was the question again ? xpath='' try: xpath=form['xpath'].value except: xpath='//wine' produce(xpath) #produce("//wine[country='Spania']")
Database og AJAX
Dataene som er brukt er beskrevet i modulen Noen datasett . Det er bygget en løsning som benytter AJAX for å hente fram informasjon om et utvalg av viner fra en database til en del av en HTML-side.
Løsningen er prinsippielt slik:
Javascript
Javascriptkoden som kjører på klienten bestiller et vinutvalg, tar i mot ferdig formatert tekst og plasserer denne teksten som "innerHTML". Skriptet er slik:
// relies on jquery.js function getIt(address,targetNodeId) { box = document.forms[0].vintype; winetype = box.options[box.selectedIndex].value; box = document.forms[0].land; prodland = box.options[box.selectedIndex].value; box = document.forms[0].volum; volum = box.options[box.selectedIndex].value; box = document.forms[0].terning; terning = box.options[box.selectedIndex].value; params='winetype='+winetype+'&prodland='+ prodland+'&volum='+volum+'&terning='+terning; $.ajax({ url:address, data:params, success:function(data) { $('#'+targetNodeId).html(data); }, error:function(data) { $('#'+targetNodeId).html("Could not access content"); } }); }
Serverskriptet
Serverskriptet tolker forespørselen fra Javascriptkoden, slår opp i databasen, formaterer dataene i HTML-fragmenter og returnerer bestillingen. Skriptet er skrevet i Python og ser slik ut:
#! /usr/bin/python import MySQLdb,cgi """ Retrieve data from MySql-base vin The return is selected wines wapped for HTML The HTML(fragments) uses some classes for styles, see fragments below """ SQL_SELECTED_WINES="""SELECT * FROM wines WHERE type='%s' AND country='%s' AND volume='%s' AND dice='%s'; """ HTML_WINE_DESCRIPTION=""" <div class="wine"> <div style="float:left"><img src="%s" alt="dice"></div> <div class="wine-name">%s</div> <div class="wine-country">%s <span class="wine-price">: kr %s / %scl </span> </div> <div style="clear:left"> </div><div class="wine-desc">%s</div> </div> """ HTML_WINE_SELECTION="""<div> %s </div> """ HTML_NO_WINE=""" <p style="margin:50px;font-weight:bold"> %s </p> """ #------------------------------------ # connect and execute a sql-request #------------------------------------ def connectAndExecute(sql): try: myBase=MySQLdb.connect(host='frigg.hiof.no', user='student', passwd='student', db='vin') myTab=myBase.cursor() myTab.execute(sql) myBase.close() return myTab.fetchall() except MySQLdb.Error, e: print "Error %d: %s" % (e.args[0], e.args[1]) return None #------------------------------------ # make selected list #------------------------------------ def makeSelection(vintype,land,volum,terning): resultTxt='' result=connectAndExecute(SQL_SELECTED_WINES%(vintype,land,volum,terning)) if result==None: return HTML_NO_WINE%'Ingen viner funnet' if len(result)==0: return HTML_NO_WINE%'Ingen viner funnet' for w in result: navn=str(w[1]) vintype=str(w[3]) land=str(w[4]) terning=str(w[5]) terningimage=vintype+terning+'.gif' volum=str(w[6]) pris=str(w[7]) beskrivelse=str(w[8]) resultTxt+=HTML_WINE_DESCRIPTION%\ (terningimage,navn,land,pris,volum,beskrivelse)+'\n' resultTxt=HTML_WINE_SELECTION%resultTxt return resultTxt #------------------------------------------------- # what is the job ? #------------------------------------------------- form=cgi.FieldStorage() print 'Content-type: text/html; charset=iso-8859-1\n' RESULT_TXT='' winetype='red' prodland='Frankrike' volum='75' terning='6' if form.has_key('winetype'): winetype=form['winetype'].value if form.has_key('prodland'): prodland=form['prodland'].value if form.has_key('volum'): volum=form['volum'].value if form.has_key('terning'): terning=form['terning'].value RESULT_TXT=makeSelection(winetype,prodland,volum,terning) print RESULT_TXT
Stilsett
Det er benyttet et enkelt stilsett (css):
body{ color: black; font-family:"Verdana","Geneva","Arial","Times",sans-serif; font-size:13px; text-align:left; margin:0px; padding:20px; } h1{font-size:24px} .ingress{font-size:12px} .wine{margin-top:15px} .wine-name{font-size:18px;padding-left:50px} .wine-desc{} .wine-country{padding-left:50px; font-weight:bold} .wine-price{font-weight:normal} .wine-author{font-weight:normal;font-size:10px;color:gray}