Ticketer

From ICO wiki

Meeskond

Liina Abner
Krista Rüütel
Roland Türi

Idee

Ticketeri näol on tegu piletimüügikeskkonnaga: üritusi korraldavad ettevõtted saavad lisada sündmusi ning registreeritud kliendid neile pileteid osta. Inspireeris http://www.piletilevi.ee/.

Projekt

XML

XML

<?xml version="1.0" encoding="utf-8" ?>

<Events>
  <Categories>
    <Category id="24" lang="est">Muusika</Category>
    <Category id="24" lang="eng">Music</Category>
    <Category id="24" lang="rus">Музыка</Category>
    <Category id="25" lang="est">Kogupere</Category>
    <Category id="25" lang="eng">Family</Category>
    <Category id="25" lang="rus">Для семьи</Category>
    <Category id="26" lang="est">Filmid</Category>
    <Category id="26" lang="eng">Movies</Category>
    <Category id="26" lang="rus">Кино</Category>
    <Category id="27" lang="est">Teater</Category>
    <Category id="27" lang="eng">Theatre</Category>
    <Category id="27" lang="rus">Театр</Category>
    <Category id="28" lang="est">Sport</Category>
    <Category id="28" lang="eng">Sport</Category>
    <Category id="28" lang="rus">Спорт</Category>
  </Categories>
  <Municipalities>
    <Municipality id="1" country="est">Harjumaa</Municipality>
    <Municipality id="2" country="est">Tartumaa</Municipality>
    <Municipality id="3" country="est">Ida-Virumaa</Municipality>
    <Municipality id="4" country="est">Pärnumaa</Municipality>
    <Municipality id="5" country="est">Lääne-Virumaa</Municipality>
    <Municipality id="6" country="est">Viljandimaa</Municipality>
    <Municipality id="7" country="est">Raplamaa</Municipality>
    <Municipality id="8" country="est">Võrumaa</Municipality>
    <Municipality id="9" country="est">Saaremaa</Municipality>
    <Municipality id="10" country="est">Jõgevamaa</Municipality>
    <Municipality id="11" country="est">Järvamaa</Municipality>
    <Municipality id="12" country="est">Valgamaa</Municipality>
    <Municipality id="13" country="est">Põlvamaa</Municipality>
    <Municipality id="14" country="est">Läänemaa</Municipality>
    <Municipality id="15" country="est">Hiiumaa</Municipality>
  </Municipalities>
  <Event id="1">
    <EventContent id="38">
      <Title>
        <![CDATA[Eesti meistrivõistlused korvpallis. Finaal.]]>
      </Title>
      <Categories>
        <Category id="28" />
        <Category id="25" />
      </Categories>
      <Description lang="EST"><![CDATA[Ela kaasa aasta tähtsündmusele. Unustamatu pallilahing.]]></Description>
      <ImageUrl>https://upload.wikimedia.org/wikipedia/commons/8/87/Gergati.jpg</ImageUrl>
      <Performers>
        <Performer><![CDATA[Kalev Cramo]]></Performer>
        <Performer><![CDATA[TÜ Rock]]></Performer>
      </Performers>
    </EventContent>
    <StartTime type="ISO 8601">15-04-2017T14:00+03:00</StartTime>
    <DurationInMinutes>90</DurationInMinutes>
    <Venue id="10" country="EST" municipality_id="1" capacity="200">
      <Name><![CDATA[Saku Suurhall]]></Name>
      <Description lang="est"><![CDATA[Avar ja kaunis Haabersti spordipalee. Eesti suurim multifunktsionaalne areen.]]></Description>
      <Description lang="eng"><![CDATA[Big big hall]]></Description>
      <Location lat="59.5467" lng="26.1830"/>
    </Venue>
    <PriceInfo>
      <Price min="10.00" max="25.00" />
    </PriceInfo>
  </Event>
  <Event id="2">
    <EventContent id="39">
      <Title>
        <![CDATA[Jaak Joala laulud]]>
      </Title>
      <Categories>
        <Category id="24" />
        <Category id="25" />
      </Categories>
      <Description lang="EST"><![CDATA[Kaunid laulud romantikasõpradele.]]></Description>
      <ImageUrl>https://www.rahvaraamat.ee/images/products/000/732/006/thumbnails/big/f1e93bcba84cc8826368227093a41c5f1212230d/jaak-joala-kuulsuse-ahelad.jpg</ImageUrl>
      <Performers>
        <Performer role="singer"><![CDATA[Koit Toome]]></Performer>
        <Performer role="singer"><![CDATA[Birgit Sarrap]]></Performer>
      </Performers>
    </EventContent>
    <StartTime type="ISO 8601">02-04-2017T14:00+03:00</StartTime>
    <DurationInMinutes>90</DurationInMinutes>
    <Venue id="10" country="EST" municipality_id="5" capacity="200">
      <Name><![CDATA[Vihula mõis]]></Name>
      <Description lang="est"><![CDATA[Eesti üks kauneimaid häärbereid metsade keskel.]]></Description>
      <Description lang="eng"><![CDATA[Beautiful Estonian manor house]]></Description>
      <Location lat="59.4260" lng="24.6474"/>
    </Venue>
    <PriceInfo>
      <Price min="5.00" max="15.00" />
    </PriceInfo>
  </Event>
  <Event id="3">
    <EventContent id="40">
      <Title>
        <![CDATA[TRAD.ATTACK! - uue albumi ''KULLAKARVA'' esitluskontsert]]>
      </Title>
      <Categories>
        <Category id="24" />
        <Category id="25" />
      </Categories>
      <Description lang="EST"><![CDATA[Maikuus ilmub ülemaailmselt Trad.Attack!'i uus album “Kullakarva”. Albumi esitlustuur viib bändi esinema paljudesse riikidesse nii Euroopas kui mujal maailmas ning rõõmu uuest muusikast jagab bänd loomulikult ka kodumaise publikuga!]]></Description>
      <ImageUrl>http://www.piletilevi.ee/imageGenerator/778eb6184f8ef9ff50e5fc3e84e9a801/eventDetails</ImageUrl>
      <Performers>
        <Performer role="singer"><![CDATA[Sandra Vabarna]]></Performer>
        <Performer role="singer"><![CDATA[Jalmar Vabarna]]></Performer>
        <Performer role="musician"><![CDATA[Tõnu Tubli]]></Performer>
      </Performers>
    </EventContent>
    <StartTime type="ISO 8601">30-06-2017T16:00+03:00</StartTime>
    <DurationInMinutes>120</DurationInMinutes>
    <Venue id="11" country="EST" municipality_id="1" capacity="300">
      <Name><![CDATA[Vaba Lava]]></Name>
      <Description lang="est"><![CDATA[Vaba Lava eesmärgiks kujuneda etenduskunstide valdkonna produktsoonikeskuseks ja loomeinkubaatoriks ning laiendada rahvusvahelist areaali, andes oma panuse nii kultuurieksporti kui -importi.]]></Description>
      <Description lang="eng"><![CDATA[Vaba Lava wishes to become a venue for young, experimental and innovative performing arts and network with theatres with similar profiles abroad.]]></Description>
      <Location lat="59.2623" lng="24.4344"/>
    </Venue>
    <PriceInfo>
      <Price min="16.00" max="79.00" />
    </PriceInfo>
  </Event>
  <Event id="4">
    <EventContent id="41">
      <Title>
        <![CDATA[Popi ja Huhuu]]>
      </Title>
      <Categories>
        <Category id="27" />
      </Categories>
      <Description lang="EST">
        <![CDATA[Tuglase lihtne kuid sümbolistlik lugu üllatab oma mitmekihilisuse ja ajatusega ning on praegugi teravalt päevakajaline. Tegemist on harukordse novelliga, mis pakub teatrile avaraid loomingulisi võimalusi. Pea sõnatu materjal tõlgituna universaalsesse teatrikeelde, võimaldab nukkude ja maskide, valguste ja varjude ning helide ja vaikustega luua maailma, mis on vaatajale mõistetav ja tajutav sõltumata riigipiirist või keelebarjäärist.]]>
      </Description>
      <ImageUrl>http://www.piletilevi.ee/imageGenerator/286dd7f573c4ec7b7543fbf2efbad714/eventDetails</ImageUrl>
      <Performers>
        <Performer role="producer"><![CDATA[Leino Rei]]></Performer>
        <Performer role="actor"><![CDATA[Tarmo Song]]></Performer>
        <Performer role="actor"><![CDATA[Mirko Rajas]]></Performer>
        <Performer role="actor"><![CDATA[Helvin Kaljula]]></Performer>
      </Performers>
    </EventContent>
    <StartTime type="ISO 8601">10-04-2017T16:00+03:00</StartTime>
    <DurationInMinutes>105</DurationInMinutes>
    <Venue id="12" country="EST" municipality_id="1" capacity="150">
      <Name><![CDATA[Eesti Draamateater]]></Name>
      <Description lang="est"><![CDATA[Eesti Draamateater on Eesti suurim sõnateater, praegu on siin 37 näitlejaga püsitrupp ja 4 koosseisulist lavastajat. Igal hooajal on teatris kümmekond esietendust, jooksvas repertuaaris on üle 20 lavastuse. Aasta jooksul annab Eesti Draamateater ligi 500 etendust ja neid vaatab üle 100 000 vaataja.]]></Description>
      <Description lang="eng"><![CDATA[The Estonian Drama Theatre is the biggest verbal theatre in Estonia; at the moment the permanent troupe consists of 37 actors and actresses and there are 5 permanent directors. Around 10 plays premier every season and more than 20 plays are constantly in the repertoire. During one year almost 500 performances are given and more than 100,000 people come to see the plays.]]></Description>
      <Location lat="59.4347" lng="24.7489"/>
    </Venue>
    <PriceInfo>
      <Price min="15.00" max="18.00" />
    </PriceInfo>
  </Event>
  <Event id="5">
    <EventContent id="42">
      <Title>
        <![CDATA[Furby tagasitulek]]>
      </Title>
      <Categories>
        <Category id="25" />
        <Category id="27" />
      </Categories>
      <Description lang="EST">
        <![CDATA[Kas sa tunned, et oled vahel väsinud? Ja see pole see väsimus, mis tekib peale väikest sörki metsarajal. See pole ka lihtsalt mingi meeldiv rammestus eduka tööpäeva lõpul. Vaid see on selline perekondlik väsimus, mis tekib siis, kui laste mahaaetud puder täidab põrandalaudade vahesid, kui lõppevad puhtad sokid, kui enam ei jõua kedagi kuulata, kui ainsad teemad naisega on sina tood, mina viin. Ehk lahendaks pagulased palju meie koduseid ja isiklikke, ehk ka intiimseid probleeme? Sest tööd on alati kuhjaga, aega ja raha, tähelepanu ja armastust aga vähe, üks inimene kuluks ära küll. Või siis ikkagi ei ole meil teda vaja? Kas meil üldse on teisi inimesi vaja? Kas meil on võimalik teistest inimestest aru saada? Endastki on raske aru saada. Neile küsimustele püüabki vastuseid leida Urmas Vadi lavastus "Furby tagasitulek".]]>
      </Description>
      <ImageUrl>http://www.piletilevi.ee/imageGenerator/798a6ac2db0d64faa3ce50e31106a1a9/eventDetails</ImageUrl>
      <Performers>
        <Performer role="producer"><![CDATA[Urmas Vadi]]></Performer>
        <Performer role="actor"><![CDATA[Liina Vahtrik]]></Performer>
        <Performer role="actor"><![CDATA[Aarne Soro]]></Performer>
        <Performer role="actor"><![CDATA[Peeter Rästas]]></Performer>
      </Performers>
    </EventContent>
    <StartTime type="ISO 8601">18-05-2017T15:00+03:00</StartTime>
    <DurationInMinutes>90</DurationInMinutes>
    <Venue id="11" country="EST" municipality_id="1" capacity="300">
      <Name><![CDATA[Vaba Lava]]></Name>
      <Description lang="est"><![CDATA[Vaba Lava eesmärgiks kujuneda etenduskunstide valdkonna produktsoonikeskuseks ja loomeinkubaatoriks ning laiendada rahvusvahelist areaali, andes oma panuse nii kultuurieksporti kui -importi.]]></Description>
      <Description lang="eng"><![CDATA[Vaba Lava wishes to become a venue for young, experimental and innovative performing arts and network with theatres with similar profiles abroad.]]></Description>
      <Location lat="59.2623" lng="24.4344"/>
    </Venue>
    <PriceInfo>
      <Price min="14.00" max="18.00" />
    </PriceInfo>
  </Event>
  <Event id="6">
    <EventContent id="43">
      <Title>
        <![CDATA[Vägi]]>
      </Title>
      <Categories>
        <Category id="27" />
      </Categories>
      <Description lang="EST">
        <![CDATA[Lavastus kohanemisest ja traditsioonidest.
            Mismoodi paistavad inimese elu ja tema uskumused kõrvalt vaadates?
            Mis saab teadmistest ja oskustest, kui nendest saab ajalugu, eksponaat vitriinis?
            Mida loevad uskumused ja tavad, kui selle taga pole enam kogemust?
            Kuidas ahistavad inimese elu tavad, millel pole enam aluspõhja?
            Mis saab väest, kui sellega ei oska enam midagi teha?
            Lavastus on loodud spetsiaalselt Eesti Rahva Muuseumi saalis mängimiseks.
            Lavastus moodustab muuseumi ekspositsiooniga omaette terviku, kuid on mõistetav ka eraldi.]]>
      </Description>
      <ImageUrl>http://www.piletilevi.ee/imageGenerator/18ad67687ce2692a6cab7a00eb61b142/eventDetails</ImageUrl>
      <Performers>
        <Performer role="producer"><![CDATA[Ivar Põllu]]></Performer>
        <Performer role="actor"><![CDATA[Maarja Jakobson]]></Performer>
        <Performer role="actor"><![CDATA[Maarja Mitt]]></Performer>
        <Performer role="actor"><![CDATA[Helgur Rosenthal]]></Performer>
      </Performers>
    </EventContent>
    <StartTime type="ISO 8601">01-04-2017T17:00+03:00</StartTime>
    <DurationInMinutes>135</DurationInMinutes>
    <Venue id="15" country="EST" municipality_id="2" capacity="250">
      <Name><![CDATA[Tartu Uus Teater]]></Name>
      <Description lang="est"><![CDATA[Tartu Uus Teater on avatud platvorm uutele ideedele.]]></Description>
      <Description lang="eng"><![CDATA[Tartu Uus Teater is an open platform for new ideas.]]></Description>
      <Location lat="59.3953" lng="26.7443"/>
    </Venue>
    <PriceInfo>
      <Price min="10.00" max="12.00" />
    </PriceInfo>
  </Event>
</Events>

XML schema (XSD)

<?xml version="1.0" encoding="utf-8"?>

<xs:schema attributeFormDefault="unqualified" elementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema">
  <xs:element name="Events">
    <xs:complexType>
      <xs:sequence>
        <xs:element name="Categories">
          <xs:complexType>
            <xs:sequence>
              <xs:element maxOccurs="unbounded" name="Category">
                <xs:complexType>
                  <xs:simpleContent>
                    <xs:extension base="xs:string">
                      <xs:attribute name="id" type="xs:unsignedByte" use="required" />
                      <xs:attribute name="lang" type="xs:string" use="required" />
                    </xs:extension>
                  </xs:simpleContent>
                </xs:complexType>
              </xs:element>
            </xs:sequence>
          </xs:complexType>
        </xs:element>
        <xs:element name="Municipalities">
          <xs:complexType>
            <xs:sequence>
              <xs:element maxOccurs="unbounded" name="Municipality">
                <xs:complexType>
                  <xs:simpleContent>
                    <xs:extension base="xs:string">
                      <xs:attribute name="id" type="xs:unsignedInt" use="required" />
                      <xs:attribute name="country" type="xs:string" use="required" />
                    </xs:extension>
                  </xs:simpleContent>
                </xs:complexType>
              </xs:element>
            </xs:sequence>
          </xs:complexType>
        </xs:element>
        <xs:element maxOccurs="unbounded" name="Event">
          <xs:complexType>
            <xs:sequence>
              <xs:element name="EventContent">
                <xs:complexType>
                  <xs:all>
                    <xs:element name="Title" type="xs:string" minOccurs="1" maxOccurs="1"/>
                    <xs:element name="Categories">
                      <xs:complexType>
                        <xs:sequence>
                          <xs:element maxOccurs="unbounded" name="Category">
                            <xs:complexType>
                              <xs:attribute name="id" type="xs:unsignedByte" use="required" />
                            </xs:complexType>
                          </xs:element>
                        </xs:sequence>
                      </xs:complexType>
                    </xs:element>
                    <xs:element name="Description">
                      <xs:complexType>
                        <xs:simpleContent>
                          <xs:extension base="xs:string">
                            <xs:attribute name="lang" type="xs:string" use="required" />
                          </xs:extension>
                        </xs:simpleContent>
                      </xs:complexType>
                    </xs:element>
                    <xs:element name="ImageUrl" type="xs:string" />
                    <xs:element name="Performers">
                      <xs:complexType>
                        <xs:sequence>
                          <xs:element maxOccurs="unbounded" name="Performer">
                            <xs:complexType>
                              <xs:simpleContent>
                                <xs:extension base="xs:string">
                                  <xs:attribute name="role" type="xs:string" use="optional" />
                                </xs:extension>
                              </xs:simpleContent>
                            </xs:complexType>
                          </xs:element>
                        </xs:sequence>
                      </xs:complexType>
                    </xs:element>
                  </xs:all>
                  <xs:attribute name="id" type="xs:unsignedInt" use="required" />
                </xs:complexType>
              </xs:element>
              <xs:element name="StartTime">
                <xs:complexType>
                  <xs:simpleContent>
                    <xs:extension base="xs:string">
                      <xs:attribute name="type" type="xs:string" use="required" />
                    </xs:extension>
                  </xs:simpleContent>
                </xs:complexType>
              </xs:element>
              <xs:element name="DurationInMinutes" type="xs:unsignedInt" />
              <xs:element name="Venue">
                <xs:complexType>
                  <xs:sequence>
                    <xs:element name="Name" type="xs:string" />
                    <xs:element maxOccurs="unbounded" name="Description">
                      <xs:complexType>
                        <xs:simpleContent>
                          <xs:extension base="xs:string">
                            <xs:attribute name="lang" type="xs:string" use="required" />
                          </xs:extension>
                        </xs:simpleContent>
                      </xs:complexType>
                    </xs:element>
                    <xs:element name="Location">
                      <xs:complexType>
                        <xs:attribute name="lat" type="xs:decimal" use="required" />
                        <xs:attribute name="lng" type="xs:decimal" use="required" />
                      </xs:complexType>
                    </xs:element>
                  </xs:sequence>
                  <xs:attribute name="id" type="xs:unsignedInt" use="required" />
                  <xs:attribute name="country" type="xs:string" use="required" />
                  <xs:attribute name="municipality_id" type="xs:unsignedInt" use="required" />
                  <xs:attribute name="capacity" type="xs:unsignedInt" />
                </xs:complexType>
              </xs:element>
              <xs:element name="PriceInfo">
                <xs:complexType>
                  <xs:sequence>
                    <xs:element name="Price">
                      <xs:complexType>
                        <xs:attribute name="min" type="xs:decimal" use="required" />
                        <xs:attribute name="max" type="xs:decimal" use="required" />
                      </xs:complexType>
                    </xs:element>
                  </xs:sequence>
                </xs:complexType>
              </xs:element>
            </xs:sequence>
            <xs:attribute name="id" type="xs:unsignedInt" use="required" />
          </xs:complexType>
        </xs:element>
      </xs:sequence>
    </xs:complexType>
  </xs:element>
</xs:schema>

XSLT 1 (html)

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl"
>
  <xsl:output method="html" indent="yes"/>

  <!-- template for calculating Julian day -->
  <!-- used for reference: http://stackoverflow.com/questions/5544762/finding-the-difference-between-2-dates-in-xslt -->
  <xsl:template name="calculateJulianDay">
    <xsl:param name="year"/>
    <xsl:param name="month"/>
    <xsl:param name="day"/>
    <xsl:variable name="a" select="floor((14 - $month) div 12)"/>
    <xsl:variable name="y" select="$year + 4800 - $a"/>
    <xsl:variable name="m" select="$month + 12 * $a - 3"/>

    <xsl:value-of select="$day + floor((153 * $m + 2) div 5) + $y * 365 + floor($y div 4) - floor($y div 100) + floor($y div 400) - 32045"/>
  </xsl:template>
  
  <!-- dummy base date for date comparison -->
  <xsl:variable name="dateTodayDummy" select="20170320"/>
  <xsl:variable name="todayDateJulian">
    <xsl:call-template name="calculateJulianDay" >
      <xsl:with-param name="year" select="substring($dateTodayDummy,1,4)"/>
      <xsl:with-param name="month" select="substring($dateTodayDummy,5,2)"/>
      <xsl:with-param name="day" select="substring($dateTodayDummy,7,2)"/>
    </xsl:call-template>
  </xsl:variable>

  <!-- template for calculating event start time -->
  <xsl:template name="calculateStartTime">
    <xsl:param name="timeString"/>
    <xsl:variable name="baseHours" select="substring($timeString,1,2)"/>
    <xsl:variable name="addHours" select="substring($timeString,7,2)"/>
    
    <xsl:value-of select="$baseHours + $addHours"/>
  </xsl:template>
  
  <xsl:template match="/">
    <xsl:text disable-output-escaping='yes'>&lt;!DOCTYPE html&gt;</xsl:text>
    <html>
      <head>
        <title>Ticteter - Esileht</title>
      </head>
      <body>

        <hr/>
        <h2 style="text-align: center">Peagi toimumas!</h2>
        <hr/>
        
        <xsl:for-each select="Events/Event">
          <!-- finding comparison date -->
          <xsl:variable name="node" select="StartTime" />
          <xsl:variable name="dateOfEvent" select="concat(substring(string($node),7,4),substring(string($node),4,2),substring(string($node),1,2))" />

          <xsl:variable name="compareDateJulian">
            <xsl:call-template name="calculateJulianDay">
              <xsl:with-param name="year" select="substring($dateOfEvent,1,4)"/>
              <xsl:with-param name="month" select="substring($dateOfEvent,5,2)"/>
              <xsl:with-param name="day" select="substring($dateOfEvent,7,2)"/>
            </xsl:call-template>
          </xsl:variable>

          <!-- display item if less than 30 days to event -->
          <xsl:if test="$compareDateJulian - $todayDateJulian  &lt; 30">
            <!-- event's info -->
            <div style="float: left; width: 200px; margin: 20px">
              <!-- event photo -->
              <img alt="eventart" height="300" width="200">
                <xsl:attribute name="src">
                  <xsl:value-of select="EventContent/ImageUrl"/>
                </xsl:attribute>
              </img><br/>
              <!-- event title -->
              <span style="font-weight: bold">
                <xsl:value-of select="EventContent/Title"/>
              </span><br/>
              <!-- date -->
              <xsl:variable name="Date" select="concat(substring(string($node),1,2),'.',substring(string($node),4,2),'.',substring(string($node),7,4))" />
              <xsl:value-of select="$Date"/><br/>
              <!-- time -->
              <xsl:variable name="startTime">
                <xsl:call-template name="calculateStartTime">
                  <xsl:with-param name="timeString" select="substring($node,12,11)"/>                 
                </xsl:call-template>
              </xsl:variable>
              <xsl:value-of select="concat($startTime,':',substring(string($node),15,2))"/><br/>
              <!-- venue -->
              <xsl:value-of select="Venue/Name"/><br/>
              <!-- price range -->
              <xsl:text></xsl:text><xsl:value-of select="PriceInfo/Price/@min"/>-<xsl:value-of select="PriceInfo/Price/@max"/>
            </div>
          </xsl:if>
        </xsl:for-each>
        <div style="clear: left"></div>

        <hr/>
        <h2 style="text-align: center">Kõik üritused</h2>
        <hr/>
        <xsl:for-each select="Events/Event">
          <xsl:variable name="node" select="StartTime" />
          <!-- event's info -->
          <div style="float: left; width: 200px; margin: 20px">
            <!-- event photo -->
            <img alt="eventart" height="300" width="200">
              <xsl:attribute name="src">
                <xsl:value-of select="EventContent/ImageUrl"/>
              </xsl:attribute>
            </img><br/>
            <!-- event title -->
            <span style="font-weight: bold">
              <xsl:value-of select="EventContent/Title"/>
            </span><br/>
            <!-- date -->
            <xsl:variable name="Date" select="concat(substring(string($node),1,2),'.',substring(string($node),4,2),'.',substring(string($node),7,4))" />
            <xsl:value-of select="$Date"/><br/>
            <!-- time -->
            <xsl:variable name="startTime">
              <xsl:call-template name="calculateStartTime">
                <xsl:with-param name="timeString" select="substring($node,12,11)"/>
              </xsl:call-template>
            </xsl:variable>
            <xsl:value-of select="concat($startTime,':',substring(string($node),15,2))"/><br/>
            <!-- venue -->
            <xsl:value-of select="Venue/Name"/><br/>
            <!-- price range -->
            <xsl:text></xsl:text><xsl:value-of select="PriceInfo/Price/@min"/>-<xsl:value-of select="PriceInfo/Price/@max"/>
          </div>
        </xsl:for-each>
        <div style="clear: left"></div>
      </body>
    </html>
  </xsl:template>
</xsl:stylesheet>

XSLT 2 (html)

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl"
>
  <!-- template for calculating event start time -->
  <xsl:template name="calculateStartTime">
    <xsl:param name="timeString"/>
    <xsl:variable name="baseHours" select="substring($timeString,1,2)"/>
    <xsl:variable name="addHours" select="substring($timeString,7,2)"/>
    <xsl:value-of select="$baseHours + $addHours"/>
  </xsl:template>

  <xsl:output method="html" indent="yes"/>

  <xsl:template match="/">
    <xsl:text disable-output-escaping='yes'>&lt;!DOCTYPE html&gt;</xsl:text>
    <html>
      <head>
        <title>Ticteter - Sündmused asula järgi</title>
      </head>
      <body>
        <hr/>
        <h2 style="text-align: center">Sündmused asula järgi</h2>
        <hr/>

        <xsl:for-each select="Events/Municipalities/Municipality">
          <xsl:variable name="municipalityId" select="@id" />
          <xsl:variable name="municipalityName" select="." />
          <hr/>
          <h2 style="text-align: center">
            <xsl:value-of select="$municipalityName"/>
          </h2>

          <xsl:for-each select="/Events/Event">
            <xsl:if test="Venue/@municipality_id = $municipalityId">
              <xsl:variable name="node" select="StartTime" />
              <!-- event's info -->
              <div style="float: left; width: 200px; margin: 20px">
                <!-- event photo -->
                <img alt="eventart" height="300" width="200">
                  <xsl:attribute name="src">
                    <xsl:value-of select="EventContent/ImageUrl"/>
                  </xsl:attribute>
                </img>
                <br/>
                <!-- event title -->
                <span style="font-weight: bold">
                  <xsl:value-of select="EventContent/Title"/>
                </span>
                <!-- date -->
                <xsl:variable name="Date" select="concat(substring(string($node),1,2),'.',substring(string($node),4,2),'.',substring(string($node),7,4))" />
                <xsl:value-of select="$Date"/><br/>
                <!-- time -->
                <xsl:variable name="startTime">
                  <xsl:call-template name="calculateStartTime">
                    <xsl:with-param name="timeString" select="substring($node,12,11)"/>
                  </xsl:call-template>
                </xsl:variable>
                <xsl:value-of select="concat($startTime,':',substring(string($node),15,2))"/><br/>
                <!-- venue -->
                <xsl:value-of select="Venue/Name"/><br/>
                <!-- price range -->
                <xsl:text></xsl:text><xsl:value-of select="PriceInfo/Price/@min"/>-<xsl:value-of select="PriceInfo/Price/@max"/>
              </div>
            </xsl:if>
          </xsl:for-each>

          <!-- Check if there are any events in the area -->
          <xsl:if test="count(/Events/Event/Venue[@municipality_id = $municipalityId]) = 0">
            Piirkonnas ei ole lähiajal ühtegi sündmust.
          </xsl:if>

          <div style="clear: left"></div>

          <hr/>
        </xsl:for-each>

      </body>
    </html>
  </xsl:template>
</xsl:stylesheet>

XSLT 3 (xml)

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl"
>
  <xsl:output method="xml" indent="yes"/>

  <xsl:template match="/">
    <Categories>
      <xsl:for-each select="Events/Categories/Category[@lang='est']">
        <xsl:variable name="catId" select="@id" />
        <xsl:variable name="catName" select="." />
        <Category>
          <xsl:attribute name="id">
            <xsl:value-of select="@id"/>
          </xsl:attribute>
          <name>
            <xsl:value-of select="."/>
          </name>
          <xsl:for-each select="/Events/Event">
            <xsl:if test="EventContent/Categories/Category/@id = $catId">
              <event>
                <xsl:copy-of select="EventContent/Title"/>
                <xsl:copy-of select="EventContent/Performers"/>
                <date>
                  <xsl:value-of select="substring(StartTime,1,10)"/>
                </date>
                <time>
                  <xsl:value-of select="substring(StartTime,12,5)"/>
                </time>
                <venue>
                  <xsl:value-of select="Venue/Name" />
                </venue>
                <minprice>
                  <xsl:value-of select="PriceInfo/Price/@min"/>
                </minprice>
                <maxprice>
                  <xsl:value-of select="PriceInfo/Price/@max"/>
                </maxprice>
                <xsl:if test="$catName='Sport'">
                  <discount>
                    <reduction unit="percent">50</reduction>
                    <clientGroup>MyFitness</clientGroup>
                    <clientGroup>Sparta</clientGroup>
                  </discount>
                </xsl:if>
              </event>
            </xsl:if>
          </xsl:for-each>
        </Category>
      </xsl:for-each>
    </Categories>
  </xsl:template>
</xsl:stylesheet>

Retsensioonid

XML failide retsensioon meeskonnale SPOT, 27.03.2017: Talk:Team_SPOT

XML failide retsensioon meeskonnale Talupood, 27.03.2017: Talk:Talupood

Veebiteenuse analüüs

Ticketer on online piletimüügikeskkond, kus ürituse korraldavad ettevõtted saavad lisada sündmusi ning registreerunud kliendid neile pileteid osta. Veebiteenus võimaldab sisestada uusi sündmusi, muuta olemasolevaid sündmusi, otsida sündmusi ning pileteid tellida. Teenus saadab välja ja võtab vastu JSON formaadis andmeid.

Kasutatavad tehnoloogiad

  • ASP.NET MVC Web API
  • Entity Framework 6.x
  • MSSQL (localdb)
  • JSON veebiteenused


Olulised mõisted

User – rakenduse registreeritud kasutaja.

UserGroup – kasutajagrupp kasutajaõiguste määramiseks, igasse gruppi kuulumisega lisanduvad kasutajale teatud õigused.

Organizer – üritusi korraldav ettevõte. Saab lisada uusi sündmusi ning neile pileteid müüa.

Event – üksiksündmus, millele saab pileteid osta. On seotud kindla toimumispaigaga ning tal on määratud algus- ja lõpuaeg. Lisaks võib olla osa ürituste sarjast. Sündmusega on seotud erinevad hinnad vastavalt korraldaja märgitud sektsioonidele ning hinnastamispoliitika (soodustused jm).

EventContent – sündmuse sisu, mis seob endas lisaks muule ka esineja(d) ja korraldaja(d). Sisu on eristatud üksiksündmusest, et eri paikades toimuval sama sisuga üritusel saaks mugavalt kasutada sama kirjeldust. Näiteks on sündmuse sisuks teatri x lavastus y; üksiksündmuseks (event) on lavastuse esitamine konkreetses kohas ja konkreetsel ajal.

Ticket – pilet konkreetsele üksiksündmusele, kus on määratud toimumiskoht ja selle sektsioon (võib olla ka ’üldala’), vajadusel ka istekoht (hetkel ei ole istekoht andmemudelis veel kajastatud). Piletil on hind, mis arvutatakse lähtuvalt valitud sektsioonist ja soodustusest.

Order – ostukorv ehk kasutaja poolt koostatud tellimus, mis võib sisaldada 1 kuni mitu piletit.


Rakenduse kirjeldus

Must-have funktsionaalsus

Üldised nõuded

  • Veebiteenus saadab välja ja võtab vastu andmeid JSON formaadis.
  • Klientrakendus töötab kõikides enamlevinud veebibrauserites.

Sündmuste haldamine

  • Sündmuste haldamine hõlmab uute sündmuste sisestamist, olemasolevate muutmist ja arhiveerimist.
  • Sündmusi saavad hallata registreeritud kasutajad, kes on seotud vähemalt ühe üritusi korraldava ettevõttega.
  • Kasutaja võib hallata ainult üritusi, mille korraldajaks on organisatsioon, millega ta meie süsteemis seotud on.
  • Sündmuse puhul eristatakse sündmuse toimumiskorda (event) ja sündmuse sisu (event content).
  • Sündmuse sisestamisel määratakse hind sektorite kaupa.
  • Ürituste korraldaja saab sisestada erinevaid soodustusi ning siduda neid enda korraldatavate üritustega.
    • Korraldajal on komplekt tema kasutatavaid soodustusi, nt tudeng -20%, tudeng -30%
    • Iga üksiküritusega (event) saab siduda erineva komplekti soodustusi.


Toimumiskohtade haldamine

  • Toimumiskohti saavad hallata ainult rakendusepoolsed administraatorkasutajad.
  • Toimumiskoht on seotud omavalitsusüksusega (linn/vald/küla/vm) ja omavalitsusüksus omakorda riigiga.
  • Toimumiskohad on jaotatud sektoriteks (näiteks parter, I rõdu jne). Ühel toimumiskohal on 1…n sektorit (1 sektori korral on selleks ’üldala’).
  • Sektoris võivad, aga ei pruugi olla, täpsustatud istekohad.


Sündmuste otsing

  • Kõik kasutajad (k.a. registreerimata kasutajad) saavad sündmusi otsida ning sündmuste detailinfot vaadata.
  • Sündmusi saab filtreerida kategooria alusel (muusika, teater, pere jne).


Pileti ostmine

  • Rakenduse registreerunud kasutajad saavad pileteid osta.
  • Ostmine tähendab antud rakenduse puhul arve kinnitamist (nt pangalingile klikkimist).
  • Pileti hinnale võivad kehtida soodustused.
  • Korraga saab piletile kehtida ainult 1 soodustus.
  • Soodustusele vastavuse eest vastutab piletiostja, nt kas tal on õigus tudengisoodustusele.
  • Pileti hind arvutatakse lähtuvalt ürituse korraldaja määratud ürituse sektsiooni baashinnast ning valitud soodustusest.
  • Kasutaja saab valitud koha(d) sektsioonis ostukorvi/arve koostamise ajal 15 minutiks broneerida.
    • Kui 15 minutit saab täis ja arve pole kinnitatud, siis pilet tühistatakse ning valitud koht sektsioonis vabaneb.
    • Kui arve kinnitatakse, siis muutub piletil märgitud koht sektsioonis teistele kasutajatele lõplikult kättesaamatuks.


Kasutajaks registreerimine

  • Tavakasutajaks registreerimisele piirangud puuduvad.
  • Kasutajaks registreerimisel on kohustuslikeks andmeteks email ja parool (andmemudel!)
  • Kasutajaks registreerimisel peab kasutaja kinnitama teenusetingimustega tutvumist - Loo konto nuppu saab vajutada ainult siis, kui “Olen tutvunud” checkbox on märgitud.


Kasutajate haldamine

  • Registreeritud kasutaja saab esitada organisatsiooni registreerimise taotluse, mille peab heaks kiitma rakenduse administraator.
  • Korraldajaga saab siduda uusi kasutajaid rakendusepoolne administraatorkasutaja või kasutaja, kes on juba korraldajaga seotud.
  • Üks kasutaja võib olla seotud 0..n organisatsiooniga.
  • Rakenduse poolne administraatorkasutaja saab arhiveerida korraldajaid ja muuta selle korraldajaga seotud kasutajate õigusi. Sel viisil on võimalik blokeerida ebausaldusväärseid ürituste korraldajaid veebiteenust kasutamast.


Kasutusstatistika

  • Rakenduse administraator saab teha väljavõtte kasutusstatistikast
    • Registreeritud kasutajad hetkel / teatud perioodil
    • Sisselogitud kasutajad hetkel / teatud perioodil
    • Konkreetse kasutaja tegevused teatud perioodil
    • Sisestatud / aktiivsed üritused ja neile müüdud / vabad piletid
    • Müüdud piletid teatud perioodil
  • Ürituste korraldaja saab teha väljavõtte
    • Sisestatud / aktiivsed üritused ja neile müüdud / vabad piletid
    • Müüdud piletid teatud perioodil


Teenuse poole pöördumine

  • Süsteemi saab korraga olla sisse logitud mitu kasutajat
  • Rakenduse administraator saab piirata teenuse poole pöördumist


Nice-to-have funktsionaalsus

  • Kasutajaks registreerimisel on kasutusel robotite vältimise mehhanism, kas CAPTCHA näol või veel parem - emailile saadetakse konto aktiveerimislink
  • Kasutajaks registreerimine ID-kaardi ja mobiil-ID-ga
  • Kasutajal on võimalik lähtestada parooli temaga seotud meiliaadressi abil.
  • Kasutajal on võimalik näha enda ostude ajalugu.
  • Piletite ostmisel kogub kasutaja lähtuvalt kulutatud summast boonust, mida ta saab kasutada tulevikus piletite ostmiseks.
  • Pileteid saab osta ka registreerimata kasutaja.
  • Pileteid saab broneerida pikemaks ajaks kui 15 minutit, nt asutuse sekretär teeb tellimuse 20 piletile, kuid 2 nädala pärast ostab neist välja 15 ja ülejäänud piletid tühistatakse.


Andmemudel

Ticketer ERD.png


Veebiteenuse ja kliendirakenduse kood

http://enos.itcollege.ee/~rturi/VR2/Ticketer/

Töölogi

  • 08.03.2017 - projekti alustamine, teema määratlemine.
  • 18.03.2017 - projekti koosolek: andmemudeli koostamisega alustamine, põhifunktsionaalsuste määratlemine.
  • 18.-20.03.2017 - XMLi ülesande lahendamine.
  • 23.-25.03.2017 - XMLi ülesande retsensioonide koostamine.
  • 08.04.2017 - projekti koosolek: andmemudeli paikaloksutamine.
  • 10.-17.04.2017 - veebiteenuse analüüsi koostamine
  • 30.04.2017 - projekti koosolek: vastutusvaldkondade määramine, veebiserveri esialgne ülesehitus