Vin
XPATH
XSLT
AJAX
Database
HTML
Børre Stenseth
Eksempler >Vinkjeller

Vinkjeller

Hva

Dataene som brukes er ved hjelp av litt kreativ editering og Pythonscripting hentet fra vinspalter i dagspressen og gjort om til XML. Dataene er ikke kvalitetsikret og er ikke egnet som vinguide. De er bare ment brukt til demonstrasjonsformål.

Utgangspunktet er vinbeskrivelser som er lagret både som XML og i en database.

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

Vi bruker lxml [1] og parameterstyrte XSLT-transformasjoner.

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']")
Test her https://borres.hiof.no/wep/htm/eksempler/vin/vinutvalg.html

Database og AJAX

Det løsningen 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.

Databasen er bygget opp med en enkel tabell, slik:

create table wines(
    vin_id INT PRIMARY KEY AUTO_INCREMENT,
    name VARCHAR(100),
    catalog CHAR(10),
    type CHAR(10),
    country VARCHAR(20),
    dice TINYINT,
    volume INT,
    price CHAR(10),
    description TEXT
);
Du kan teste løsningen her https://borres.hiof.no/wep/htm/eksempler/vin/index.html

Løsningen er prinsippielt slik:

vindb
Vin fra database

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=' itstud.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}
[2] [3]
Referanser
  1. lxml - XML and HTML with Python lxml.de/ 03-03-2014
  1. XPath 1.0 W3C www.w3.org/TR/xpath 14-03-2014
  1. XPath 2.0 W3C www.w3.org/TR/xpath20 14-03-2014
Eksempler >Vinkjeller