BeerPressure: Difference between revisions

From ICO wiki
Jump to navigationJump to search
Ejogi (talk | contribs)
No edit summary
Saasma (talk | contribs)
 
(144 intermediate revisions by 3 users not shown)
Line 1: Line 1:
== Meeskond ja rollid ==
== Meeskond ja rollid ==


* Sigrid Aasma
* Sigrid Aasma (arendaja)
* Evelin Jõgi
* Evelin Jõgi (arendaja)
* Martin Kask
* Martin Kask (projektijuht, arendaja)
* Marko Nõu
* Marko Nõu (arendaja)


== Idee ==
== Idee ==
'''Päevapakkumiste teenus'''
'''Toidukohtade põhimenüüde ja päevapakkumiste teenus'''


On päris mitu kodulehte ‘päevapakkumised’ kus kirjas toidukohtade päevapraed jms. mida sellistel lehtedel pole, on masiloetavad andmed ehk siis variant, et saad saata API pihta mingi kuupäeva koos päevapraadidega
Soovime pakkuda toidukohtade põhimenüüde ja päevapakkumiste teenust. On olemas mitmeid kodulehti “päevapakkumised”, kus on kirjas toidukohtade päevapraed. Kuid tavaliselt sellistel lehtedel ei ole masinloetavaid andmeid ehk siis varianti, et saad saata API pihta mingi kuupäeva koos päevapraadidega andmete uuendamiseks ja saad sama API käest küsida tänaseid päevapraade. Andmeid võiks saada küsida restorani, kuupäeva, asukoha või otsisõna/otsisõnaosa põhiselt. Selline teenus lihtsustaks ka toidukohtade päevapakkumiste haldamist, kuna praegu peavad teenuse pakkujad vastavat informatsiooni uuendama mitmes erinevas kohas - nii erinevates portaalides, kui ka enda veebileheküljel. Samuti oleks toidukohtade kogu menüü haldamine ühes kohas.
ja saad sama API käest küsida tänased päevapraed kas firma, kuupäeva või asukoha põhiselt. Selline teenus lihtsustaks ka toidukohtade päevapakkumiste haldamist, kuna praegu peavad nad vastavat informatsiooni uuendama mitmes erinevas kohas - nii portaalides, kui ka enda leheküljel.
 
=== Esialgne prototüüp ===


[[File:TäisKõhtProto1.mp4]]
[[File:TäisKõhtProto1.mp4]]


Esialgne prototüüp: [http://enos.itcollege.ee/~saasma/VR2/Proto/K%C3%B5htT%C3%A4is/#g=1&p=home navigeeritav versioon]
Esialgne prototüüp: [http://enos.itcollege.ee/~saasma/VR2/Proto/K%C3%B5htT%C3%A4is/#g=1&p=home navigeeritav versioon vol.1]
 
=== Prototüübi muutus projekti käigus ===
 
Prototüüp: [http://enos.itcollege.ee/~saasma/VR2/Proto/T%C3%A4isK%C3%B5ht/K%C3%B5htT%C3%A4is/#g=1&p=home navigeeritav versioon vol.2]
 
== Kasutatav arendustehnoloogia ==


Projekti TFS: TODO
Veebiteenuse loomisel kasutame ASP.NET Core tehnoloogiat ja kursusel õpitud arendusmustreid.


'''Meie kirjutatud retsensioon'''
Klientrakenduse loomisel kasutame Node.js + React'i.
TODO


'''Meie kirjutatud lõpptoote retsensioon'''
== Veebiteenuse analüüs ==
TODO
Pakume toidukohtade põhimenüüde ja päevapakkumiste teenust, kuhu on kokku koondatud erinevate toidukohtade menüüd üle Eesti. Põhimenüü alla kuuluvad pikaajalised menüüd, mis kehtivad püsivalt, hooajaliselt või mingil konkreetsel nädalapäeval. Päevapakkumiste alla kuuluvad konkreetse kuupäevaga seotud päevapakkumised. Päevapakkumiste korral kuvatakse lisainfona, kas pakkumine kehtib mingis konkreetses ajavahemikus või kogu toidukoha lahtioleku aja.


Projekti Prototüüp:
Eesmärk on koguda ühte kohta kokku erinevate toidukohtade menüüd ja päevapakkumised, et kliendid saaksid mugavalt ja kiiresti oma valikuid teha. Kliendid käivad enamasti päevapakkumisi söömas lõuna ajal ja selleks ettenähtud aeg on piiratud. Sellepärast on oluline, et klient saaks teha oma valiku võimalikult kiiresti. Tänu loodavale veebiteenusele ei pea kliendid käima erinevates portaalides, et tutvuda erinevate toidukohtade pakkumistega. Kuna teenust on mugav kasutada ja klient saab oma valiku kiiresti tehtud, siis tõenäoliselt teeb klient valiku selles keskkonnas olevate toiduasutuste seast. Lisaks võimaldab teenus toiduasutustel paika panna oma pikaajalisem põhimenüü ning seda rakenduses ja võimalusel ka söögikohas kuvada. Sellepärast on ka toidukohtade huvi antud keskkonnas üleval olla ja meiega oma andmeid jagada, sest see suurendab nende klientuuri ning muudab oma menüü haldamise kergemaks.
TODO


'''Lõpptoode'''
Veebiteenuse loomisel lähtume eelkõige sellest, et kliendil oleks teenust mugav kasutada ja ta saaks oma valiku kiiresti tehtud. Arvestame, et kliendid on erinevad. Mõni eelistab mõnda kindlat toidukohta, sel juhul saab ta valida oma soovitud kohad ja võrrelda nende kohtade tänase päeva pakkumisi ning menüüsid. Mõnele inimesele on oluline pakutav söök ja ta eelistab pakkumist otsida prae nimetuses oleva sõna või sõnaosa järgi ning lähtuda seejärel valiku tegemisel toidukoha keskmisest hindest või toidu hinnast. Mõne kliendi jaoks on oluline hind ja selleks, et oma valikut teha sobivas hinnavahemikus on tal võimalik sisestada maksimum hinna piirang. Kindlasti on antud teenuse juures kõikide klientide jaoks oluline päevapakkumiste asukohapõhine eristatus. Kliendid, kes soovivad oma konto luua, võivad oma eelistatud valikud meelde jätta ja järgmisel korral kuvatakse neid talle automaatselt.
TODO


Teenuse must-have funktsionaalsus tagab teenuse toimimise ja põhifunktsioonid. Teenuse nice-to-have funktsionaalsus annab rakendusele lisandväärtust.


== Veebiteenuse analüüs ==
Arvestades pakutava teenusega, siis näeme, et aktiivseim teenuse kasutamine võib olla tööpäevadel vahemikus 11.00-15.00, kui kliendid tutvuvad päevapakkumistega. Teenus peab tekkivale koormusele vastu pidama.
 
Teenuse pakkumine peab olema turvaline. Selle tagamiseks tuleb sisestatavaid andmeid valideerida ja andmebaasi sisestavate andmete mahtu piirata. Toidukohtade sisestatud andmed peavad olema kaitstud, et toitude koostised ei saaks avalikuks. Põhjendatud juhtudel on võimalik kasutajaid lukku panna.
 
== Kasutajad ==
 
 
* Admin - lehe administraator, kellel on õigus lehte, kasutajaid ja nende õiguseid hallata. Tema huvi on, et teenus toimiks tõrgeteta.
* Toiduasutus (tasuta kasutaja) - saab hoida lehel ühte põhimenüüd ja päevapakkumisi.
* Toiduasutus (tellimusega kasutaja) - saab hoida lehel piiramatu arv põhimenüüsid ja päevapakkumisi.
Toiduasutused on teenusest huvitatud, et nende menüüde haldamine oleks kerge ja mugav ning nad saaksid oma klientide arvu suurendada.
* Külastaja - klient, kes valib söögikohta. Tema on huvitatud eelkõige sellest, et teenust saaks kasutada mugavalt ja kiirelt ning toidukohtade valik oleks võimalikult suur.
 
== Rakenduse must-have funktsionaalsus ==
 
* Kasutajad saavad sisse logida
* Kasutajatel on erinevad rollid: admin, toidukoht (tasuta ja tasulise tellimusega), klient
* Kasutajate arvepidamine kasutajate lõikes:
** sisselogimise kuupäev, kellaaeg
** pakkumiste lisamine
** pakkumiste muutmine
** pakkumiste kustutamine
** õiguste kehtimise periood (tasuta ja tellimusega toidukoht)
** pakkumistele klikkimise arv
* Kasutajaid on võimalik hallata:
** kasutajale õiguste andmine (menüüde arvu suurendamine, tellimusega toidukohal), rolli määramine
** Kasutajat on võimalik piirata: õiguseid piirata (piiratud menüüde arv, tasuta liitunud toidukohal) ja lukku panna
* Päevapakkumisi/menüüsid saab küsida:
** toidukoha järgi,
** kuupäeva põhiselt (ainult päevapakkumisi),
** asukoha põhiselt,
** toidu nimetuses esineva otsisõna/otsisõna osa põhiselt,
** hinna põhiselt (max hinna piirang).
* Klientrakenduses realiseeritakse CRUD meetodid (menüüsid/päevapakkumisi saab sisestada, lugeda, uuendada, kustutada)
* Toidukohale saab anda hindeid ja kuvatakse keskmist hinnet.
* Toidule saab anda hindeid ja kuvatakse keskmist hinnet.
 
== Nice-to-have funktsionaalsus ==
 
* Info selle kohta, kas päevapakkumine on otsas
* Päevapakkumisi/menüüsid saab kommenteerida ja neid kommentaare kuvatakse avalikult.
* Söögikohtade kodulehtedelt otsimise script, mis genereerib juba olemasolevate kodulehtede baasil päevamenüüsid.
* Toiduvaliku piiramine allergeenide/vegan/jms. koostisosade põhiselt.
* Aruandlused ja võrdlused toidukohtadele, et võrrelda end konkurentidega.
* Teenus soovitab toidukohtadele nende varasemate päevapraadide populaarsuse alusel uue nädala menüüd.
* Erinevad toidukohtade/toitude pingeread klientidele, et anda valikuteks ideid.
* Soovitused klientidele, mida teised kliendid sarnaste eelistustega on veel valinud.
 
== Andmebaasi ülesehitus - projekti algus ==
 
[[File: BeerPressureAndmebaasimudel.png]]
 
== Andmebaasi ülesehitus - projekti lõpp ==
 
[[File: Ta2isKo6ht-DB-FINAL.png]]
 
== Projekti repository ==
 
https://github.com/sikumiku/TaisKohtApi
 
Zip fail saadaval 27.05 seisuga:
(master branchi viimane commit esitamise ajal)
https://github.com/sikumiku/TaisKohtApi/archive/61438e187a1a972410c8a3bc6274d69489d00aa6.zip
 
Uuendatud .zip fail 08.06 seisuga: https://github.com/sikumiku/TaisKohtApi/archive/7acbc702b13550daee840069ca9be886807dd900.zip
 
== API v1 dokumentatsioon ==
 
===Security===
'''POST /api/account/register'''
<source>
Params: RegisterViewModel registerViewModel
 
Returns: token
</source>
 
'''POST /api/account/login'''
<source> 
Params: LoginViewModel model
 
Returns: token
</source>
 
'''POST /api/account/logout'''
<source> 
Params: -
 
Returns: -
</source>
 
===Accounts===
'''GET /api/v1/accounts/getAllUsersInRole'''
<source> 
Params: string role
 
Returns: List<UserDTO>
</source>
 
'''GET /api/v1/accounts/{id}'''
<source> 
Params: string id
 
Returns: UserDTO
</source>
 
'''PUT /api/v1/accounts/{id}'''
<source> 
Params: string id, UpdateUserDTO userDTO
 
Returns: -
</source>
 
'''POST /api/v1/accounts/addRole'''
<source> 
Params: string role
 
Returns: -
</source>
 
'''POST /api/v1/accounts/addRoleToUser'''
<source> 
Params: string role, string userId
 
Returns: UserDTO
</source>
 
'''DELETE /api/v1/accounts/deactivate/{id}'''
<source> 
Params: string id
 
Returns: -
</source>
 
===Promotions===
'''GET /api/v1/promotions '''
<source>
Params: -
 
Returns: List<PromotionDTO>
</source>
 
'''GET /api/v1/promotions/{id}'''
<source> 
Params: int id
 
Returns: PromotionDTO
</source>
 
'''POST /api/v1/promotions'''
<source> 
Params: PromotionDTO promotionDTO
 
Returns: PromotionDTO newPromotion
</source>
 
'''PUT /api/v1/promotions/{id}'''
<source> 
Params: int id, PromotionDTO promotionDTO
 
Returns: PromotionDTO updatedPromotion
</source>
 
'''DELETE /api/v1/promotions/{id}'''
<source> 
Params: int id
 
Returns: -
</source>
 
===Restaurants===
'''GET /api/v1/restaurants'''
<source> 
Params: -
 
Returns: List<SimpleRestaurantDTO>
</source>
 
'''GET /api/v1/restaurants/owner'''
<source> 
Params: -
 
Returns: List<SimpleRestaurantDTO>
</source>
 
'''GET /api/v1/restaurants/search?name=th'''
<source> 
Params: string name
 
Returns: List<SimpleRestaurantDTO> result
</source>
 
'''GET /api/v1/restaurants/top'''
<source> 
Params: int amount
 
Returns: List<SimpleRestaurantDTO> result
</source>
 
'''GET /api/v1/restaurants/{id}'''
<source> 
Params: int id
 
Returns: RestaurantDTO r
</source>
 
'''POST /api/v1/restaurants/addUserToRestaurant'''
<source> 
Params: int id, string userId
 
Returns: -
</source>
 
'''POST /api/v1/restaurants'''
<source> 
Params: PostRestaurantDTO restaurantDTO
 
Returns: RestaurantDTO newRestaurant
</source>
 
'''PUT /api/v1/restaurants/{id}'''
<source> 
Params: int id, PostRestaurantDTO restaurantDTO
 
Returns: PostRestaurantDTO updatedRestaurantDTO
</source>
 
'''DELETE /api/v1/restaurants/{id}'''
<source> 
Params: int id
 
Returns: -
</source>
 
===Menus===
'''GET /api/v1/menus'''
<source>
Params: -
 
Returns: List<MenuDTO>
</source>
 
'''GET /api/v1/menus/{id}'''
<source> 
Params: int id
 
Returns: MenuDTO menuDTO
</source>
 
'''POST /api/v1/menus'''
<source> 
Params: PostMenuDTO menuDTO
 
Returns: MenuDTO newMenu
</source>
 
'''PUT /api/v1/menus/{id}'''
<source> 
Params: int id, PostMenuDTO menuDTO
 
Returns: MenuDTO updatedMenu
</source>
 
'''PUT api/v1/Menus/{id}/Dishes'''
<source> 
Params: int menuId, int[] dishIds
 
Returns: -
</source>
 
'''DELETE /api/v1/menus/{id}'''
<source> 
Params: int id
 
Returns: -
</source>
 
===Dishes===
'''GET /api/v1/dishes'''
<source> 
Params: -
 
Returns: List<DishDTO>
</source>
 
'''GET /api/v1/dishes/daily'''
<source> 
Params: bool vegan, bool glutenFree, bool lactoseFree
 
Returns: List<SimpleDishDTO> result
</source>
 
'''GET: api/v1/dishes/search?title=th&priceLimit=null'''
<source> 
Params: string title, decimal? priceLimit
 
Returns: List<DishDTO> result
</source>
 
'''GET /api/v1/dishes/top'''
<source> 
Params: int amount
 
Returns: List<DishDTO> result
</source>
 
'''GET /api/v1/dishes/{id}'''
<source> 
Params: int id
 
Returns: DishDTO dishDTO
</source>
 
'''POST /api/v1/dishes'''
<source> 
Params: PostDishDTO dishDTO
 
Returns: DishDTO newDish
</source>
 
'''PUT /api/v1/dishes/{id}'''
<source> 
Params: int id, PostDishDTO dishDTO
 
Returns: DishDTO updatedDish
</source>
 
'''PUT api/v1/Dishes/{id}/Ingredients'''
<source> 
Params: int dishId, PostIngredientForDishDTO[] ingredients
 
Returns: -
</source>
 
'''DELETE /api/v1/dishes/{id}'''
<source> 
Params: int id
 
Returns: -
</source>
 
===Ingredients===
'''GET /api/v1/ingredients'''
<source> 
Params: -
 
Returns: List<IngredientDTO>
</source>
 
'''GET /api/v1/ingredients/{id}'''
<source> 
Params: int id
 
Returns: IngredientDTO i
</source>
 
'''POST /api/v1/ingredients'''
<source> 
Params: PostIngredientDTO ingredientDTO
 
Returns: IngredientDTO newIngredient
</source>
 
'''PUT /api/v1/ingredients/{id}'''
<source> 
Params: int id, PostIngredientDTO ingredientDTO
 
Returns: IngredientDTO updatedIngredient
</source>
 
'''DELETE /api/v1/ingredients/{id}'''
<source> 
Params: int id
 
Returns: -
</source>
 
===Ratings===
'''GET /api/v1/ratings'''
<source> 
Params: -
 
Returns: List<RatingLogDTO>
</source>
 
'''GET /api/v1/ratings/{id}'''
<source> 
Params: int id
 
Returns: RatingLogDTO dto
</source>
 
'''POST /api/v1/ratings'''
<source> 
Params: RatingLogForEntityDTO ratingDTO
 
Returns: RatingLogDTO newRating
</source>
 
'''PUT api/v1/ratings/{id}'''
<source> 
Params: int id, RatingLogForEntityDTO ratingDTO
 
Returns: RatingLogDTO updatedRating
</source>
 
'''DELETE api/v1/ratings/{id}'''
<source> 
Params: int id
 
Returns: -
</source>
 
===RequestLog===
'''GET: api/v1/usageData/{userId}'''
<source> 
Params: string userId


Returns: List<RequestLogDTO>
</source>


== Lõpptoote kasutusjuhend ==
== Lõpptoote kasutusjuhend ==


TODO
* Kloonida kohalikku arvutisse repository aadressilt [https://github.com/sikumiku/TaisKohtApi https://github.com/sikumiku/TaisKohtApi]
* Installida NodeJS
* <source>npm install webpack -g</source>
* <source>npm install -cli -g</source>
* Frontend rakenduse kasutamiseks valida Startup meetoditest IIS Express, veebiteenuse ja Swaggeri kasutamiseks käivitada Api Swagger
* Esimese admin kasutaja loomiseks tuleb kasutaja registreerida admin@gmail.com e-mailiga
 
== XML/XSD/XSLT ==
 
=== XML ===
 
<source><?xml version="1.0" encoding="utf-8" ?>
<restaurants>
  <restaurant id="1" url="https://kuuspelmeeni.ee" contactnumber="507783342" email="kuuspelmeeni@gmail.com">
    <name><![CDATA[Kuus Pelmeeni]]></name>
    <menus>
      <menu id="1" repetitioninterval="30">
        <name><![CDATA[Päevamenüü]]></name>
        <activefrom>2018-05-27T12:00:00</activefrom>
        <activeto>2018-05-28T12:00:00</activeto>
        <dishes>
          <dish id="1" vegan="false" lactosefree="false" glutenfree="false" daily="true" kcal="550" dailyprice="4">
            <title><![CDATA[8 pelmeeni]]></title>
            <description><![CDATA[8 pelmeeni 6 asemel serveeritud tervisliku koguse hapukoore ja tilliga]]></description>
            <availablefrom>2018-05-27T12:00:00</availablefrom>
            <availableto>2018-05-28T12:00:00</availableto>
            <ingredients>
              <ingredient id="1" amountunit="g">
                <name>Sealiha</name>
                <description>Rakvere sealiha</description>
              </ingredient>
              <ingredient id="2" amountunit="g">
                <name>Hapukoor</name>
                <description>Alma hapukoor</description>
              </ingredient>
            </ingredients>
            <rating ratingvalue="9">
              <comments>
                <comment>
                  <commenttext><![CDATA[Parim päevaroog viimasel ajal.]]></commenttext>
                  <username>peeterpakiraam66</username>
                  <userid>3a9dd653-30fb-40ab-97b1-33cb82d336eb</userid>
                </comment>
              </comments>
            </rating>
            <promotion id="1" type="visual" classname="bold-red-border-2px">
              <name>2px red border</name>
              <description><![CDATA[Displays a 2px wide red border around the component that is promoted.]]></description>
              <validto>2018-06-25T12:00:00</validto>
            </promotion>
          </dish>
          <dish id="2" vegan="true" lactosefree="true" glutenfree="true" daily="true" kcal="320" price="4">
            <title><![CDATA[Kurgisalat]]></title>
            <description><![CDATA[Erinevatel viisidel töödeldud kurk serveeritud koos jõhvikatega. Tervislik amps.]]></description>
            <availablefrom>2018-05-27T12:00:00</availablefrom>
            <availableto>2018-05-28T12:00:00</availableto>
            <ingredients/>
            <rating ratingvalue="4">
              <comments>
                <comment>
                  <commenttext><![CDATA[Jäin veel väga näljaseks peale selle söömist.]]></commenttext>
                  <username>ingupingu22</username>
                  <userid>45b09f05-33fd-4041-995e-ecdb7ec61939</userid>
                </comment>
              </comments>
            </rating>
          </dish>
        </dishes>
      </menu>
      <menu id="2">
        <name><![CDATA[Põhimenüü]]></name>
        <dishes>
          <dish id="4" vegan="false" lactosefree="false" glutenfree="false" daily="false" kcal="550" price="5">
            <title><![CDATA[6 pelmeeni]]></title>
            <description><![CDATA[6 pelmeeni serveeritud tervisliku koguse hapukoore ja tilliga]]></description>
            <ingredients>
              <ingredient id="1" amountunit="g">
                <name>Sealiha</name>
                <description>Rakvere sealiha</description>
              </ingredient>
            </ingredients>
            <rating ratingvalue="9.5">
              <comments>
                <comment>
                  <commenttext><![CDATA[Käin seda iga päev söömas.]]></commenttext>
                  <username>ingupingu22</username>
                  <userid>45b09f05-33fd-4041-995e-ecdb7ec61939</userid>
                </comment>
                <comment>
                  <commenttext><![CDATA[Parim toit selles restoranis.]]></commenttext>
                  <username>peeterpakiraam66</username>
                  <userid>3a9dd653-30fb-40ab-97b1-33cb82d336eb</userid>
                </comment>
              </comments>
            </rating>
          </dish>
          <dish id="5" vegan="false" lactosefree="false" glutenfree="true" daily="false" kcal="460" price="3.8">
            <title><![CDATA[Kartulisalat]]></title>
            <description><![CDATA[Sinki sisaldav traditsiooniline kartulisalat.]]></description>
            <ingredients/>
          </dish>
          <dish id="6" vegan="false" lactosefree="false" glutenfree="false" daily="false" kcal="610" price="3.9">
            <title><![CDATA[Šokolaadikook]]></title>
            <description><![CDATA[Ahvatlev glasuuritud šokolaadikook.]]></description>
            <ingredients/>
            <rating ratingvalue="7.8">
              <comments>
                <comment>
                  <commenttext><![CDATA[Šokolaadikooki on keeruline metsa keerata.]]></commenttext>
                  <username>mutikas3</username>
                  <userid>3a29b94a-3f17-469d-a44d-f7d85e98dd3e</userid>
                </comment>
              </comments>
            </rating>
          </dish>
        </dishes>
      </menu>
    </menus>
    <dishes/>
    <address id="1" country="Eesti">
      <addressfirstline>Tatari 12</addressfirstline>
      <locality>Tallinn</locality>
      <postcode>10132</postcode>
      <region>Harjumaa</region>
    </address>
    <promotion/>
    <rating ratingvalue="7.5">
      <comments>
        <comment>
          <commenttext><![CDATA[Parim restoran üldse!]]></commenttext>
          <username>peeterpakiraam66</username>
          <userid>3a9dd653-30fb-40ab-97b1-33cb82d336eb</userid>
        </comment>
        <comment>
          <commenttext><![CDATA[Ma sain ainult 5 pelmeeni. Ootasin kuute.]]></commenttext>
          <username>kyllikekallike1</username>
          <userid>6af271dd-0e11-4ae6-98be-111762664318</userid>
        </comment>
      </comments>
    </rating>
  </restaurant>
  <restaurant id="2" url="https://kolmkokka.ee" contactnumber="6522351" email="kolmkokka@gmail.com">
    <name><![CDATA[Kolm kokka]]></name>
    <menus/>
    <dishes>
      <dish id="7" vegan="false" lactosefree="true" glutenfree="false" daily="false" kcal="470" price="14">
        <title><![CDATA[Ahjulõhe juurikatega]]></title>
        <description><![CDATA[Ahjulõhe serveeritud püreestatud porgandite ja lillkapsaga.]]></description>
        <ingredients>
          <ingredient id="11" amountunit="tk" amount="1">
            <name>Ahjulõhe</name>
            <description><![CDATA[Värske ahjulõhe]]></description>
          </ingredient>
          <ingredient id="12" amountunit="g">
            <name>Püreestatud porgand</name>
            <description><![CDATA[Eesti porgand, mis on kergelt püreestatud]]></description>
          </ingredient>
          <ingredient id="13" amountunit="g">
            <name>Püreestatud lillkapsas</name>
            <description><![CDATA[Eesti lillkapsas, mis on kergelt püreestatud ja maitsestatud]]></description>
          </ingredient>
        </ingredients>
        <rating ratingvalue="8.9">
          <comments>
            <comment>
              <commenttext><![CDATA[Kindlasti minu üks lemmikuid.]]></commenttext>
              <username>ingupingu22</username>
              <userid>45b09f05-33fd-4041-995e-ecdb7ec61939</userid>
            </comment>
            <comment>
              <commenttext><![CDATA[Pole midagi halba öelda.]]></commenttext>
              <username>tiiuviiu</username>
              <userid>0730a7b7-3a9c-4e26-962f-da9a8daeab8a</userid>
            </comment>
          </comments>
        </rating>
      </dish>
      <dish id="8" vegan="true" lactosefree="true" glutenfree="true" daily="true" dailyprice="5">
        <title><![CDATA[Ingverisupp]]></title>
        <description><![CDATA[Ingveriga vürtsistatud ingverisupp, mis viib keele alla.]]></description>
        <availablefrom>2018-05-25T12:00:00</availablefrom>
        <availableto>2018-06-25T12:00:00</availableto>
        <ingredients>
          <ingredient id="14" amountunit="g" amount="20">
            <name>Ingver</name>
            <description><![CDATA[Tükeldatud ingver]]></description>
          </ingredient>
        </ingredients>
        <rating ratingvalue="6">
          <comments>
            <comment>
              <commenttext><![CDATA[Liiga palju ingverit.]]></commenttext>
              <username>kyllikekallike1</username>
              <userid>6af271dd-0e11-4ae6-98be-111762664318</userid>
            </comment>
          </comments>
        </rating>
      </dish>
      <dish id="9" vegan="false" lactosefree="false" glutenfree="true" daily="false" kcal="340" price="4.5">
        <title><![CDATA[Apelsinijäätis]]></title>
        <description><![CDATA[Apelsinimaitseline jäätis serveeritud riivitud apelsinikoorega.]]></description>
        <ingredients/>
        <rating ratingvalue="8">
          <comments>
            <comment>
              <commenttext><![CDATA[Väga huvitav magustoit, prooviks veel.]]></commenttext>
              <username>mutikas3</username>
              <userid>3a29b94a-3f17-469d-a44d-f7d85e98dd3e</userid>
            </comment>
            <comment>
              <commenttext><![CDATA[Üks mu lemmikuid magustoite.]]></commenttext>
              <username>ingupingu22</username>
              <userid>45b09f05-33fd-4041-995e-ecdb7ec61939</userid>
            </comment>
          </comments>
        </rating>
      </dish>
    </dishes>
    <address id="2" country="Eesti">
      <addressfirstline>Laia 12</addressfirstline>
      <locality>Tallinn</locality>
      <postcode>13022</postcode>
      <region>Harjumaa</region>
    </address>
    <promotion/>
    <rating ratingvalue="8.1">
      <comments>
        <comment>
          <commenttext><![CDATA[Hinnad on natuke liiga kallid minu jaoks, aga toit om maitsev.]]></commenttext>
          <username>peeterpakiraam66</username>
          <userid>3a9dd653-30fb-40ab-97b1-33cb82d336eb</userid>
        </comment>
        <comment>
          <commenttext><![CDATA[Ma kindlasti telliks kala uuesti. Väga mahlakas oli.]]></commenttext>
          <username>kyllikekallike1</username>
          <userid>6af271dd-0e11-4ae6-98be-111762664318</userid>
        </comment>
        <comment>
          <commenttext><![CDATA[Asukoht oli päris tore. Miljöö ka.]]></commenttext>
          <username>mutikas3</username>
          <userid>3a29b94a-3f17-469d-a44d-f7d85e98dd3e</userid>
        </comment>
      </comments>
    </rating>
  </restaurant>
</restaurants></source>
 
=== XSD ===
 
<source><?xml version="1.0" encoding="utf-8"?>
<xs:schema attributeFormDefault="unqualified" elementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema">
  <xs:element name="restaurants">
    <xs:complexType>
      <xs:sequence minOccurs="0" maxOccurs="unbounded">
        <xs:element name="restaurant">
          <xs:complexType>
            <xs:sequence>
              <xs:element name="name" type="xs:string" />
              <xs:element name="menus">
                <xs:complexType>
                  <xs:sequence minOccurs="0" maxOccurs="unbounded">
                    <xs:element name="menu">
                      <xs:complexType>
                        <xs:sequence>
                          <xs:element name="name" type="xs:string" />
                          <xs:element name="activefrom" type="xs:dateTime" minOccurs="0" />
                          <xs:element name="activeto" type="xs:dateTime" minOccurs="0" />
                          <xs:element name="dishes">
                            <xs:complexType>
                              <xs:sequence minOccurs="0" maxOccurs="unbounded">
                                <xs:element name="dish">
                                  <xs:complexType>
                                    <xs:sequence>
                                      <xs:element name="title" type="xs:string" />
                                      <xs:element name="description" type="xs:string" />
                                      <xs:element name="availablefrom" type="xs:dateTime" minOccurs="0" />
                                      <xs:element name="availableto" type="xs:dateTime" minOccurs="0" />
                                      <xs:element name="servetime" type="xs:dateTime" minOccurs="0" />
                                      <xs:element name="ingredients">
                                        <xs:complexType>
                                          <xs:sequence minOccurs="0" maxOccurs="unbounded">
                                            <xs:element name="ingredient">
                                              <xs:complexType>
                                                <xs:sequence>
                                                  <xs:element name="name" type="xs:string" />
                                                  <xs:element name="description" type="xs:string" />
                                                </xs:sequence>
                                                <xs:attribute name="id" type="xs:int" use="required" />
                                                <xs:attribute name="amountunit" type="xs:string" use="required" />
                                                <xs:attribute name="amount" type="xs:decimal" use="optional" />
                                              </xs:complexType>
                                            </xs:element>
                                          </xs:sequence>
                                        </xs:complexType>
                                      </xs:element>
                                      <xs:element name="rating" minOccurs="0">
                                        <xs:complexType>
                                          <xs:sequence minOccurs="0" maxOccurs="unbounded">
                                            <xs:element name="comments">
                                              <xs:complexType>
                                                <xs:sequence minOccurs="0" maxOccurs="unbounded">
                                                  <xs:element name="comment">
                                                    <xs:complexType>
                                                      <xs:sequence>
                                                        <xs:element name="commenttext" type="xs:string" />
                                                        <xs:element name="username" type="xs:string" />
                                                        <xs:element name="userid" type="xs:string" />
                                                      </xs:sequence>
                                                    </xs:complexType>
                                                  </xs:element>
                                                </xs:sequence>
                                              </xs:complexType>
                                            </xs:element>
                                          </xs:sequence>
                                          <xs:attribute name="ratingvalue" type="xs:decimal" use="required" />
                                        </xs:complexType>
                                      </xs:element>
                                      <xs:element name="promotion" minOccurs="0">
                                        <xs:complexType>
                                          <xs:sequence>
                                            <xs:element name="name" type="xs:string" />
                                            <xs:element name="description" type="xs:string" />
                                            <xs:element name="validto" type="xs:dateTime" minOccurs="0" />
                                          </xs:sequence>
                                          <xs:attribute name="id" type="xs:int" use="required" />
                                          <xs:attribute name="type" type="xs:string" use="required" />
                                          <xs:attribute name="classname" type="xs:string" use="required" />
                                        </xs:complexType>
                                      </xs:element>
                                    </xs:sequence>
                                    <xs:attribute name="id" type="xs:int" use="required" />
                                    <xs:attribute name="vegan" type="xs:boolean" use="optional" />
                                    <xs:attribute name="lactosefree" type="xs:boolean" use="optional" />
                                    <xs:attribute name="glutenfree" type="xs:boolean" use="optional" />
                                    <xs:attribute name="daily" type="xs:boolean" use="optional" />
                                    <xs:attribute name="kcal" type="xs:int" use="optional" />
                                    <xs:attribute name="weightg" type="xs:decimal" use="optional" />
                                    <xs:attribute name="price" type="xs:decimal" use="optional" />
                                    <xs:attribute name="dailyprice" type="xs:decimal" use="optional" />
                                  </xs:complexType>
                                </xs:element>
                              </xs:sequence>
                            </xs:complexType>
                          </xs:element>
                          <xs:element name="promotion" minOccurs="0">
                            <xs:complexType>
                              <xs:sequence>
                                <xs:element name="name" type="xs:string" />
                                <xs:element name="description" type="xs:string" />
                                <xs:element name="validto" type="xs:dateTime" minOccurs="0" />
                              </xs:sequence>
                              <xs:attribute name="id" type="xs:int" use="required" />
                              <xs:attribute name="type" type="xs:string" use="required" />
                              <xs:attribute name="classname" type="xs:string" use="required" />
                            </xs:complexType>
                          </xs:element>
                        </xs:sequence>
                        <xs:attribute name="id" type="xs:int" use="required" />
                        <xs:attribute name="repetitioninterval" type="xs:int" use="optional" />
                      </xs:complexType>
                    </xs:element>
                  </xs:sequence>
                </xs:complexType>
              </xs:element>
              <xs:element name="dishes">
                <xs:complexType>
                  <xs:sequence minOccurs="0" maxOccurs="unbounded">
                    <xs:element name="dish">
                      <xs:complexType>
                        <xs:sequence>
                          <xs:element name="title" type="xs:string" />
                          <xs:element name="description" type="xs:string" />
                          <xs:element name="availablefrom" type="xs:dateTime" minOccurs="0" />
                          <xs:element name="availableto" type="xs:dateTime" minOccurs="0" />
                          <xs:element name="servetime" type="xs:dateTime" minOccurs="0" />
                          <xs:element name="ingredients">
                            <xs:complexType>
                              <xs:sequence minOccurs="0" maxOccurs="unbounded">
                                <xs:element name="ingredient">
                                  <xs:complexType>
                                    <xs:sequence>
                                      <xs:element name="name" type="xs:string" />
                                      <xs:element name="description" type="xs:string" />
                                    </xs:sequence>
                                    <xs:attribute name="id" type="xs:int" use="required" />
                                    <xs:attribute name="amountunit" type="xs:string" use="required" />
                                    <xs:attribute name="amount" type="xs:decimal" use="optional" />
                                  </xs:complexType>
                                </xs:element>
                              </xs:sequence>
                            </xs:complexType>
                          </xs:element>
                          <xs:element name="rating">
                            <xs:complexType>
                              <xs:sequence>
                                <xs:element name="comments">
                                  <xs:complexType>
                                    <xs:sequence minOccurs="0" maxOccurs="unbounded">
                                      <xs:element name="comment">
                                        <xs:complexType>
                                          <xs:sequence>
                                            <xs:element name="commenttext" type="xs:string" />
                                            <xs:element name="username" type="xs:string" />
                                            <xs:element name="userid" type="xs:string" />
                                          </xs:sequence>
                                        </xs:complexType>
                                      </xs:element>
                                    </xs:sequence>
                                  </xs:complexType>
                                </xs:element>
                              </xs:sequence>
                              <xs:attribute name="ratingvalue" type="xs:decimal" use="required" />
                            </xs:complexType>
                          </xs:element>
                          <xs:element name="promotion" minOccurs="0">
                            <xs:complexType>
                              <xs:sequence>
                                <xs:element name="name" type="xs:string" />
                                <xs:element name="description" type="xs:string" />
                                <xs:element name="validto" type="xs:dateTime" minOccurs="0" />
                              </xs:sequence>
                              <xs:attribute name="id" type="xs:int" use="required" />
                              <xs:attribute name="type" type="xs:string" use="required" />
                              <xs:attribute name="classname" type="xs:string" use="required" />
                            </xs:complexType>
                          </xs:element>
                        </xs:sequence>
                        <xs:attribute name="id" type="xs:int" use="required" />
                        <xs:attribute name="vegan" type="xs:boolean" use="optional" />
                        <xs:attribute name="lactosefree" type="xs:boolean" use="optional" />
                        <xs:attribute name="glutenfree" type="xs:boolean" use="optional" />
                        <xs:attribute name="daily" type="xs:boolean" use="optional" />
                        <xs:attribute name="kcal" type="xs:int" use="optional" />
                        <xs:attribute name="weightg" type="xs:decimal" use="optional" />
                        <xs:attribute name="price" type="xs:decimal" use="optional" />
                        <xs:attribute name="dailyprice" type="xs:decimal" use="optional" />
                      </xs:complexType>
                    </xs:element>
                  </xs:sequence>
                </xs:complexType>
              </xs:element>
              <xs:element name="address">
                <xs:complexType>
                  <xs:sequence>
                    <xs:element name="addressfirstline" type="xs:string" />
                    <xs:element name="locality" type="xs:string" />
                    <xs:element name="postcode" type="xs:string" />
                    <xs:element name="region" type="xs:string" />
                  </xs:sequence>
                  <xs:attribute name="id" type="xs:int" use="required" />
                  <xs:attribute name="country" type="xs:string" use="required" />
                </xs:complexType>
              </xs:element>
              <xs:element name="promotion" />
              <xs:element name="rating" minOccurs="0">
                <xs:complexType>
                  <xs:sequence>
                    <xs:element name="comments">
                      <xs:complexType>
                        <xs:sequence minOccurs="0" maxOccurs="unbounded">
                          <xs:element name="comment">
                            <xs:complexType>
                              <xs:sequence>
                                <xs:element name="commenttext" type="xs:string" />
                                <xs:element name="username" type="xs:string" />
                                <xs:element name="userid" type="xs:string" />
                              </xs:sequence>
                            </xs:complexType>
                          </xs:element>
                        </xs:sequence>
                      </xs:complexType>
                    </xs:element>
                  </xs:sequence>
                  <xs:attribute name="ratingvalue" type="xs:decimal" use="required" />
                </xs:complexType>
              </xs:element>
            </xs:sequence>
            <xs:attribute name="id" type="xs:int" use="required" />
            <xs:attribute name="url" type="xs:string" use="required" />
            <xs:attribute name="contactnumber" type="xs:string" use="required" />
            <xs:attribute name="email" type="xs:string" use="required" />
          </xs:complexType>
        </xs:element>
      </xs:sequence>
    </xs:complexType>
  </xs:element>
</xs:schema></source>
 
=== XSLT(HTML) ===
 
<source><?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"/>
  <xsl:template match="/restaurants">
    <html>
      <head>
        <title>Täis Kõht</title>
      </head>
      <body>
        <xsl:for-each select="restaurant">
          <h1 style="color:DodgerBlue">
            <xsl:value-of select="name"/>
          </h1>
          <div>
            <xsl:value-of select="@url"/>
          </div>
          <div>
            <xsl:text>Kontakt number: </xsl:text>
            <xsl:value-of select="@contactnumber"/>
          </div>
          <div>
            <xsl:text>email: </xsl:text>
            <xsl:value-of select="@email"/>
          </div>
          <div>
            <xsl:text>Aadress: </xsl:text>
            <xsl:value-of select="address/addressfirstline"/>
            <xsl:text>, </xsl:text>
            <xsl:value-of select="address/locality"/>
            <xsl:text>, </xsl:text>
            <xsl:value-of select="address/postcode"/>
            <xsl:text>, </xsl:text>
            <xsl:value-of select="address/@country"/>
          </div>
 
          <xsl:choose>
            <xsl:when test="menus/menu">
              <xsl:for-each select="menus/menu">
                <xsl:sort select="name"/>
                <h3>
                  <xsl:value-of select="name"/>
                </h3>
 
                <xsl:if test="activefrom != '' and activeto != ''">
                  <xsl:text> </xsl:text>
                  <xsl:value-of select="substring(activefrom,1,10)"/>
                  <xsl:text> kuni </xsl:text>
                  <xsl:value-of select="substring(activeto,1,10)"/>
                </xsl:if>
 
                <ul>
                  <xsl:for-each select="dishes/dish">
                    <xsl:sort select="title"/>
 
                    <xsl:if test="availablefrom != '' and availableto != ''">
                      <xsl:value-of select="substring(availablefrom,1,10)"/>
                      <xsl:text> kuni </xsl:text>
                      <xsl:value-of select="substring(availableto,1,10)"/>
                    </xsl:if>
 
                    <li>
                      <span style="font-weight:bold">
                        <xsl:value-of select="title"/>
                      </span>
                      <xsl:text> - </xsl:text>
                      <xsl:value-of select="description"/>
                      <xsl:text>  </xsl:text>
                      <span style="font-weight:bold">
                        <xsl:if test="@glutenfree='true'">
                          <xsl:text>G </xsl:text>
                        </xsl:if>
                        <xsl:if test="@lactosefree='true'">
                          <xsl:text>L </xsl:text>
                        </xsl:if>
                        <xsl:if test="@vegan='true'">
                          <xsl:text>V </xsl:text>
                        </xsl:if>
                      </span>
                    </li>
 
                    <xsl:for-each select="ingredients/ingredient">
                      <xsl:value-of select="name"/>
                      <xsl:if test="position()!=last()">
                        <xsl:text>, </xsl:text>
                      </xsl:if>
                    </xsl:for-each>
 
                    <p style="font-weight:bold">
                      <xsl:text>Hind: </xsl:text>
                      <xsl:choose>
                        <xsl:when test="@price!=''">
                          <xsl:value-of select="@price"/>
                        </xsl:when>
                        <xsl:otherwise>
                          <xsl:text>-</xsl:text>
                        </xsl:otherwise>
                      </xsl:choose>
                      <xsl:text> / Päevapakkumise hind: </xsl:text>
                      <xsl:choose>
                        <xsl:when test="@dailyprice!=''">
                          <xsl:value-of select="@dailyprice"/>
                        </xsl:when>
                        <xsl:otherwise>
                          <xsl:text>-</xsl:text>
                        </xsl:otherwise>
                      </xsl:choose>
                    </p>
 
                    <div style="background-color:LightGray;">
                      <xsl:text>Hinnang praele: </xsl:text>
                      <xsl:choose>
                        <xsl:when test="rating/@ratingvalue!=''">
                          <xsl:value-of select="rating/@ratingvalue"/>
                        </xsl:when>
                        <xsl:otherwise>
                          <xsl:text>Ei ole veel hinnatud!</xsl:text>
                        </xsl:otherwise>
                      </xsl:choose>
 
                      <br/>
                      <xsl:for-each select="rating/comments/comment">
                        <xsl:value-of select="username"/>
                        <xsl:text>: </xsl:text>
                        <xsl:value-of select="commenttext"/>
                        <br/>
                      </xsl:for-each>
                    </div>
                    <br/>
                  </xsl:for-each>
                </ul>
              </xsl:for-each>
            </xsl:when>
            <xsl:otherwise>
              <ul>
                <xsl:for-each select="dishes/dish">
                  <xsl:sort select="title"/>
 
                  <xsl:if test="availablefrom != '' and availableto != ''">
                    <xsl:value-of select="substring(availablefrom,1,10)"/>
                    <xsl:text> kuni </xsl:text>
                    <xsl:value-of select="substring(availableto,1,10)"/>
                  </xsl:if>
 
                  <li>
                    <span style="font-weight:bold">
                      <xsl:value-of select="title"/>
                    </span>
                    <xsl:text> - </xsl:text>
                    <xsl:value-of select="description"/>
                    <xsl:text>  </xsl:text>
                    <span style="font-weight:bold">
                      <xsl:if test="@glutenfree='true'">
                        <xsl:text>G </xsl:text>
                      </xsl:if>
                      <xsl:if test="@lactosefree='true'">
                        <xsl:text>L </xsl:text>
                      </xsl:if>
                      <xsl:if test="@vegan='true'">
                        <xsl:text>V </xsl:text>
                      </xsl:if>
                    </span>
                  </li>
 
                  <xsl:for-each select="ingredients/ingredient">
                    <xsl:value-of select="name"/>
                    <xsl:if test="position()!=last()">
                      <xsl:text>, </xsl:text>
                    </xsl:if>
                  </xsl:for-each>
 
                  <p style="font-weight:bold">
                    <xsl:text>Hind: </xsl:text>
                    <xsl:choose>
                      <xsl:when test="@price!=''">
                        <xsl:value-of select="@price"/>
                      </xsl:when>
                      <xsl:otherwise>
                        <xsl:text>-</xsl:text>
                      </xsl:otherwise>
                    </xsl:choose>
                    <xsl:text> / Päevapakkumise hind: </xsl:text>
                    <xsl:choose>
                      <xsl:when test="@dailyprice!=''">
                        <xsl:value-of select="@dailyprice"/>
                      </xsl:when>
                      <xsl:otherwise>
                        <xsl:text>-</xsl:text>
                      </xsl:otherwise>
                    </xsl:choose>
                  </p>
 
                  <div style="background-color:LightGray;">
                    <xsl:text>Hinnang praele: </xsl:text>
                    <xsl:choose>
                      <xsl:when test="rating/@ratingvalue!=''">
                        <xsl:value-of select="rating/@ratingvalue"/>
                      </xsl:when>
                      <xsl:otherwise>
                        <xsl:text>Ei ole veel hinnatud!</xsl:text>
                      </xsl:otherwise>
                    </xsl:choose>
 
                    <br/>
                    <xsl:for-each select="rating/comments/comment">
                      <xsl:value-of select="username"/>
                      <xsl:text>: </xsl:text>
                      <xsl:value-of select="commenttext"/>
                      <br/>
                    </xsl:for-each>
                  </div>
                  <br/>
                </xsl:for-each>
              </ul>
            </xsl:otherwise>
          </xsl:choose>
         
          <div style="background-color:LightGray;">
            <xsl:text>Hinnang restoranile: </xsl:text>
            <xsl:choose>
              <xsl:when test="rating/@ratingvalue!=''">
                <xsl:value-of select="rating/@ratingvalue"/>
              </xsl:when>
              <xsl:otherwise>
                <xsl:text>Ei ole veel hinnatud!</xsl:text>
              </xsl:otherwise>
            </xsl:choose>
           
           
            <br/>
            <xsl:for-each select="rating/comments/comment">
              <xsl:value-of select="username"/>
              <xsl:text>: </xsl:text>
              <xsl:value-of select="commenttext"/>
              <br/>
            </xsl:for-each>
          </div>
 
          <p>
            <xsl:text>*G - gluteenivaba, </xsl:text>
            <xsl:text>L - laktoosivaba, </xsl:text>
            <xsl:text>V - vegan </xsl:text>
          </p>
         
        </xsl:for-each>
      </body>
    </html>
  </xsl:template>
</xsl:stylesheet></source>
 
=== XSLT(XML) ===
 
XML'is kuvatakse elemendid ja atribuudid eesti keeles. Kuvatakse restorani andmed ja toidud, mille hinnang on 8 või rohkem punkti.
 
<source><?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="/restaurants">
    <restoranid>
      <xsl:for-each select="restaurant">
        <restoran url="{@url}" telefon="{@contactnumber}" email="{@email}">
          <nimi>
            <xsl:value-of select="name"/>
          </nimi>
          <aadress>
            <postiaadress>
              <xsl:value-of select="address/addressfirstline"/>
            </postiaadress>
            <linn>
              <xsl:value-of select="address/locality"/>
            </linn>
            <indeks>
              <xsl:value-of select="address/postcode"/>
            </indeks>
            <riik>
              <xsl:value-of select="address/@country"/>
            </riik>
          </aadress>


== Rakenduse must-have sisu ==
          <parimadToidud>
            <xsl:choose>
              <xsl:when test="menus/menu/dishes/dish">
                <xsl:for-each select="menus/menu/dishes/dish">
                  <xsl:if test="rating/@ratingvalue &gt;= 8.0">
                    <heaToit hinnang="{rating/@ratingvalue}" paevapakkumiseHind="{@dailyprice}" hind="{@price}"
                            vegan="{@vegan}" laktoosivaba="{@lactosefree}" gluteenivaba="{@glutenfree}">
                      <nimi>
                        <xsl:value-of select="title"/>
                      </nimi>
                      <kirjeldus>
                        <xsl:value-of select="description"/>
                      </kirjeldus>
                    </heaToit>
                  </xsl:if>
                </xsl:for-each>
              </xsl:when>
              <xsl:otherwise>
                <xsl:for-each select="dishes/dish">
                  <xsl:if test="rating/@ratingvalue &gt;= 8.0">
                    <heaToit hinnang="{rating/@ratingvalue}" paevapakkumiseHind="{@dailyprice}" hind="{@price}"
                            vegan="{@vegan}" laktoosivaba="{@lactosefree}" gluteenivaba="{@glutenfree}">
                      <nimi>
                        <xsl:value-of select="title"/>
                      </nimi>
                      <kirjeldus>
                        <xsl:value-of select="description"/>
                      </kirjeldus>
                    </heaToit>
                  </xsl:if>
                </xsl:for-each>
              </xsl:otherwise>
            </xsl:choose>
          </parimadToidud>
        </restoran>
      </xsl:for-each>
    </restoranid>
  </xsl:template>
</xsl:stylesheet>
</source>


TODO
== Retsensioonid ==


== Nice-to-have lisad ==
'''Veebiteenuse retsensioon:''' [https://wiki.itcollege.ee/index.php/Talk:MOT#Veebiteenuse_retsensensioon_by_meeskond_BeerPressure: Team MOT retsensioon]


TODO
'''Klientrakenduse retsensioon:''' [https://wiki.itcollege.ee/index.php/Talk:Kuldneloojang#Klientrakenduse_retsensensioon_by_meeskond_BeerPressure: Team Kuldneloojang retsensioon]
 
'''XML retsensioon:''' [https://wiki.itcollege.ee/index.php/Talk:MOT#XML_retsensioon_by_meeskond_BeerPressure: Team MOT retsensioon]


== Logiraamat ==
== Logiraamat ==
Line 67: Line 1,298:


Postitatud esimeste analüüsi arutelude põhjal valminud algne prototüüp.
Postitatud esimeste analüüsi arutelude põhjal valminud algne prototüüp.
=== 30.03.2018 ===
Valmis andmebaasimudel.
=== 08.04.2018 ===
Tehtud muudatused analüüsis vastavalt tagasisidele.
=== 19.04.2018 ===
Loodud projekti põhi koos domeenimudelitega. Projekt lisatud Githubi: https://github.com/sikumiku/TaisKohtApi
=== 05-06.05.2018 ===
Osalesime hackathonil. Lisatud controllerid, service'id koos vajalike kihtidega.
=== 16.05.2018 ===
Uuendatud andmebaasimudelit, DTO'sid.
=== 18-19.05.2018 ===
Lisatud klientrakendus, security, äriloogikat.
=== 20.05.2018 ===
Lisatud API dokumentatsioon.
=== 22-24.05.2018 ===
Uuendatud contoller'eid, service'id.
=== 25-27.05.2018 ===
Uuendatud contoller'eid, service'id. Lisatud kasutajate ja rollide kontrolle meetoditesse. Täiustatud klientrakendust. Swagger dokumentatsioon.
=== 31.05-01.06.2018 ===
Tegeletud XML, XSD ja XSLT failide loomisega.
=== 01.06-03.06.2018 ===
Tegeletud XML ja XSLT failide täiustamisega ja retsensioonide kirjutamisega.
=== 05.06.2018 ===
Lisatud XML retsensioon meeskonnale MOT
=== 08.06.2018 ===
Lisatud uus projekti .zip muudatustega ja täiendustega

Latest revision as of 22:33, 8 June 2018

Meeskond ja rollid

  • Sigrid Aasma (arendaja)
  • Evelin Jõgi (arendaja)
  • Martin Kask (projektijuht, arendaja)
  • Marko Nõu (arendaja)

Idee

Toidukohtade põhimenüüde ja päevapakkumiste teenus

Soovime pakkuda toidukohtade põhimenüüde ja päevapakkumiste teenust. On olemas mitmeid kodulehti “päevapakkumised”, kus on kirjas toidukohtade päevapraed. Kuid tavaliselt sellistel lehtedel ei ole masinloetavaid andmeid ehk siis varianti, et saad saata API pihta mingi kuupäeva koos päevapraadidega andmete uuendamiseks ja saad sama API käest küsida tänaseid päevapraade. Andmeid võiks saada küsida restorani, kuupäeva, asukoha või otsisõna/otsisõnaosa põhiselt. Selline teenus lihtsustaks ka toidukohtade päevapakkumiste haldamist, kuna praegu peavad teenuse pakkujad vastavat informatsiooni uuendama mitmes erinevas kohas - nii erinevates portaalides, kui ka enda veebileheküljel. Samuti oleks toidukohtade kogu menüü haldamine ühes kohas.

Esialgne prototüüp

Esialgne prototüüp: navigeeritav versioon vol.1

Prototüübi muutus projekti käigus

Prototüüp: navigeeritav versioon vol.2

Kasutatav arendustehnoloogia

Veebiteenuse loomisel kasutame ASP.NET Core tehnoloogiat ja kursusel õpitud arendusmustreid.

Klientrakenduse loomisel kasutame Node.js + React'i.

Veebiteenuse analüüs

Pakume toidukohtade põhimenüüde ja päevapakkumiste teenust, kuhu on kokku koondatud erinevate toidukohtade menüüd üle Eesti. Põhimenüü alla kuuluvad pikaajalised menüüd, mis kehtivad püsivalt, hooajaliselt või mingil konkreetsel nädalapäeval. Päevapakkumiste alla kuuluvad konkreetse kuupäevaga seotud päevapakkumised. Päevapakkumiste korral kuvatakse lisainfona, kas pakkumine kehtib mingis konkreetses ajavahemikus või kogu toidukoha lahtioleku aja.

Eesmärk on koguda ühte kohta kokku erinevate toidukohtade menüüd ja päevapakkumised, et kliendid saaksid mugavalt ja kiiresti oma valikuid teha. Kliendid käivad enamasti päevapakkumisi söömas lõuna ajal ja selleks ettenähtud aeg on piiratud. Sellepärast on oluline, et klient saaks teha oma valiku võimalikult kiiresti. Tänu loodavale veebiteenusele ei pea kliendid käima erinevates portaalides, et tutvuda erinevate toidukohtade pakkumistega. Kuna teenust on mugav kasutada ja klient saab oma valiku kiiresti tehtud, siis tõenäoliselt teeb klient valiku selles keskkonnas olevate toiduasutuste seast. Lisaks võimaldab teenus toiduasutustel paika panna oma pikaajalisem põhimenüü ning seda rakenduses ja võimalusel ka söögikohas kuvada. Sellepärast on ka toidukohtade huvi antud keskkonnas üleval olla ja meiega oma andmeid jagada, sest see suurendab nende klientuuri ning muudab oma menüü haldamise kergemaks.

Veebiteenuse loomisel lähtume eelkõige sellest, et kliendil oleks teenust mugav kasutada ja ta saaks oma valiku kiiresti tehtud. Arvestame, et kliendid on erinevad. Mõni eelistab mõnda kindlat toidukohta, sel juhul saab ta valida oma soovitud kohad ja võrrelda nende kohtade tänase päeva pakkumisi ning menüüsid. Mõnele inimesele on oluline pakutav söök ja ta eelistab pakkumist otsida prae nimetuses oleva sõna või sõnaosa järgi ning lähtuda seejärel valiku tegemisel toidukoha keskmisest hindest või toidu hinnast. Mõne kliendi jaoks on oluline hind ja selleks, et oma valikut teha sobivas hinnavahemikus on tal võimalik sisestada maksimum hinna piirang. Kindlasti on antud teenuse juures kõikide klientide jaoks oluline päevapakkumiste asukohapõhine eristatus. Kliendid, kes soovivad oma konto luua, võivad oma eelistatud valikud meelde jätta ja järgmisel korral kuvatakse neid talle automaatselt.

Teenuse must-have funktsionaalsus tagab teenuse toimimise ja põhifunktsioonid. Teenuse nice-to-have funktsionaalsus annab rakendusele lisandväärtust.

Arvestades pakutava teenusega, siis näeme, et aktiivseim teenuse kasutamine võib olla tööpäevadel vahemikus 11.00-15.00, kui kliendid tutvuvad päevapakkumistega. Teenus peab tekkivale koormusele vastu pidama.

Teenuse pakkumine peab olema turvaline. Selle tagamiseks tuleb sisestatavaid andmeid valideerida ja andmebaasi sisestavate andmete mahtu piirata. Toidukohtade sisestatud andmed peavad olema kaitstud, et toitude koostised ei saaks avalikuks. Põhjendatud juhtudel on võimalik kasutajaid lukku panna.

Kasutajad

  • Admin - lehe administraator, kellel on õigus lehte, kasutajaid ja nende õiguseid hallata. Tema huvi on, et teenus toimiks tõrgeteta.
  • Toiduasutus (tasuta kasutaja) - saab hoida lehel ühte põhimenüüd ja päevapakkumisi.
  • Toiduasutus (tellimusega kasutaja) - saab hoida lehel piiramatu arv põhimenüüsid ja päevapakkumisi.

Toiduasutused on teenusest huvitatud, et nende menüüde haldamine oleks kerge ja mugav ning nad saaksid oma klientide arvu suurendada.

  • Külastaja - klient, kes valib söögikohta. Tema on huvitatud eelkõige sellest, et teenust saaks kasutada mugavalt ja kiirelt ning toidukohtade valik oleks võimalikult suur.

Rakenduse must-have funktsionaalsus

  • Kasutajad saavad sisse logida
  • Kasutajatel on erinevad rollid: admin, toidukoht (tasuta ja tasulise tellimusega), klient
  • Kasutajate arvepidamine kasutajate lõikes:
    • sisselogimise kuupäev, kellaaeg
    • pakkumiste lisamine
    • pakkumiste muutmine
    • pakkumiste kustutamine
    • õiguste kehtimise periood (tasuta ja tellimusega toidukoht)
    • pakkumistele klikkimise arv
  • Kasutajaid on võimalik hallata:
    • kasutajale õiguste andmine (menüüde arvu suurendamine, tellimusega toidukohal), rolli määramine
    • Kasutajat on võimalik piirata: õiguseid piirata (piiratud menüüde arv, tasuta liitunud toidukohal) ja lukku panna
  • Päevapakkumisi/menüüsid saab küsida:
    • toidukoha järgi,
    • kuupäeva põhiselt (ainult päevapakkumisi),
    • asukoha põhiselt,
    • toidu nimetuses esineva otsisõna/otsisõna osa põhiselt,
    • hinna põhiselt (max hinna piirang).
  • Klientrakenduses realiseeritakse CRUD meetodid (menüüsid/päevapakkumisi saab sisestada, lugeda, uuendada, kustutada)
  • Toidukohale saab anda hindeid ja kuvatakse keskmist hinnet.
  • Toidule saab anda hindeid ja kuvatakse keskmist hinnet.

Nice-to-have funktsionaalsus

  • Info selle kohta, kas päevapakkumine on otsas
  • Päevapakkumisi/menüüsid saab kommenteerida ja neid kommentaare kuvatakse avalikult.
  • Söögikohtade kodulehtedelt otsimise script, mis genereerib juba olemasolevate kodulehtede baasil päevamenüüsid.
  • Toiduvaliku piiramine allergeenide/vegan/jms. koostisosade põhiselt.
  • Aruandlused ja võrdlused toidukohtadele, et võrrelda end konkurentidega.
  • Teenus soovitab toidukohtadele nende varasemate päevapraadide populaarsuse alusel uue nädala menüüd.
  • Erinevad toidukohtade/toitude pingeread klientidele, et anda valikuteks ideid.
  • Soovitused klientidele, mida teised kliendid sarnaste eelistustega on veel valinud.

Andmebaasi ülesehitus - projekti algus

Andmebaasi ülesehitus - projekti lõpp

Projekti repository

https://github.com/sikumiku/TaisKohtApi

Zip fail saadaval 27.05 seisuga: (master branchi viimane commit esitamise ajal) https://github.com/sikumiku/TaisKohtApi/archive/61438e187a1a972410c8a3bc6274d69489d00aa6.zip

Uuendatud .zip fail 08.06 seisuga: https://github.com/sikumiku/TaisKohtApi/archive/7acbc702b13550daee840069ca9be886807dd900.zip

API v1 dokumentatsioon

Security

POST /api/account/register

Params: RegisterViewModel registerViewModel

Returns: token

POST /api/account/login

  
Params: LoginViewModel model

Returns: token

POST /api/account/logout

  
Params: -

Returns: -

Accounts

GET /api/v1/accounts/getAllUsersInRole

  
Params: string role

Returns: List<UserDTO>

GET /api/v1/accounts/{id}

  
Params: string id

Returns: UserDTO

PUT /api/v1/accounts/{id}

  
Params: string id, UpdateUserDTO userDTO

Returns: -

POST /api/v1/accounts/addRole

  
Params: string role

Returns: -

POST /api/v1/accounts/addRoleToUser

  
Params: string role, string userId

Returns: UserDTO

DELETE /api/v1/accounts/deactivate/{id}

  
Params: string id

Returns: -

Promotions

GET /api/v1/promotions

 
Params: -

Returns: List<PromotionDTO>

GET /api/v1/promotions/{id}

  
Params: int id

Returns: PromotionDTO

POST /api/v1/promotions

  
Params: PromotionDTO promotionDTO

Returns: PromotionDTO newPromotion

PUT /api/v1/promotions/{id}

  
Params: int id, PromotionDTO promotionDTO

Returns: PromotionDTO updatedPromotion

DELETE /api/v1/promotions/{id}

  
Params: int id

Returns: -

Restaurants

GET /api/v1/restaurants

  
Params: -

Returns: List<SimpleRestaurantDTO>

GET /api/v1/restaurants/owner

  
Params: -

Returns: List<SimpleRestaurantDTO>

GET /api/v1/restaurants/search?name=th

  
Params: string name

Returns: List<SimpleRestaurantDTO> result

GET /api/v1/restaurants/top

  
Params: int amount

Returns: List<SimpleRestaurantDTO> result

GET /api/v1/restaurants/{id}

  
Params: int id

Returns: RestaurantDTO r

POST /api/v1/restaurants/addUserToRestaurant

  
Params: int id, string userId

Returns: -

POST /api/v1/restaurants

  
Params: PostRestaurantDTO restaurantDTO

Returns: RestaurantDTO newRestaurant

PUT /api/v1/restaurants/{id}

  
Params: int id, PostRestaurantDTO restaurantDTO

Returns: PostRestaurantDTO updatedRestaurantDTO

DELETE /api/v1/restaurants/{id}

  
Params: int id

Returns: -

Menus

GET /api/v1/menus

Params: -

Returns: List<MenuDTO>

GET /api/v1/menus/{id}

  
Params: int id

Returns: MenuDTO menuDTO

POST /api/v1/menus

  
Params: PostMenuDTO menuDTO

Returns: MenuDTO newMenu

PUT /api/v1/menus/{id}

  
Params: int id, PostMenuDTO menuDTO

Returns: MenuDTO updatedMenu

PUT api/v1/Menus/{id}/Dishes

  
Params: int menuId, int[] dishIds

Returns: -

DELETE /api/v1/menus/{id}

  
Params: int id

Returns: -

Dishes

GET /api/v1/dishes

  
Params: -

Returns: List<DishDTO>

GET /api/v1/dishes/daily

  
Params: bool vegan, bool glutenFree, bool lactoseFree

Returns: List<SimpleDishDTO> result

GET: api/v1/dishes/search?title=th&priceLimit=null

  
Params: string title, decimal? priceLimit

Returns: List<DishDTO> result

GET /api/v1/dishes/top

  
Params: int amount

Returns: List<DishDTO> result

GET /api/v1/dishes/{id}

  
Params: int id

Returns: DishDTO dishDTO

POST /api/v1/dishes

  
Params: PostDishDTO dishDTO

Returns: DishDTO newDish

PUT /api/v1/dishes/{id}

  
Params: int id, PostDishDTO dishDTO

Returns: DishDTO updatedDish

PUT api/v1/Dishes/{id}/Ingredients

  
Params: int dishId, PostIngredientForDishDTO[] ingredients

Returns: -

DELETE /api/v1/dishes/{id}

  
Params: int id

Returns: -

Ingredients

GET /api/v1/ingredients

  
Params: -

Returns: List<IngredientDTO>

GET /api/v1/ingredients/{id}

  
Params: int id

Returns: IngredientDTO i

POST /api/v1/ingredients

  
Params: PostIngredientDTO ingredientDTO

Returns: IngredientDTO newIngredient

PUT /api/v1/ingredients/{id}

  
Params: int id, PostIngredientDTO ingredientDTO

Returns: IngredientDTO updatedIngredient

DELETE /api/v1/ingredients/{id}

  
Params: int id

Returns: -

Ratings

GET /api/v1/ratings

  
Params: -

Returns: List<RatingLogDTO>

GET /api/v1/ratings/{id}

  
Params: int id

Returns: RatingLogDTO dto

POST /api/v1/ratings

  
Params: RatingLogForEntityDTO ratingDTO

Returns: RatingLogDTO newRating

PUT api/v1/ratings/{id}

  
Params: int id, RatingLogForEntityDTO ratingDTO

Returns: RatingLogDTO updatedRating

DELETE api/v1/ratings/{id}

  
Params: int id

Returns: -

RequestLog

GET: api/v1/usageData/{userId}

  
Params: string userId

Returns: List<RequestLogDTO>

Lõpptoote kasutusjuhend

  • Kloonida kohalikku arvutisse repository aadressilt https://github.com/sikumiku/TaisKohtApi
  • Installida NodeJS
  • npm install webpack -g
  • npm install -cli -g
  • Frontend rakenduse kasutamiseks valida Startup meetoditest IIS Express, veebiteenuse ja Swaggeri kasutamiseks käivitada Api Swagger
  • Esimese admin kasutaja loomiseks tuleb kasutaja registreerida admin@gmail.com e-mailiga

XML/XSD/XSLT

XML

<?xml version="1.0" encoding="utf-8" ?>
<restaurants>
  <restaurant id="1" url="https://kuuspelmeeni.ee" contactnumber="507783342" email="kuuspelmeeni@gmail.com">
    <name><![CDATA[Kuus Pelmeeni]]></name>
    <menus>
      <menu id="1" repetitioninterval="30">
        <name><![CDATA[Päevamenüü]]></name>
        <activefrom>2018-05-27T12:00:00</activefrom>
        <activeto>2018-05-28T12:00:00</activeto>
        <dishes>
          <dish id="1" vegan="false" lactosefree="false" glutenfree="false" daily="true" kcal="550" dailyprice="4">
            <title><![CDATA[8 pelmeeni]]></title>
            <description><![CDATA[8 pelmeeni 6 asemel serveeritud tervisliku koguse hapukoore ja tilliga]]></description>
            <availablefrom>2018-05-27T12:00:00</availablefrom>
            <availableto>2018-05-28T12:00:00</availableto>
            <ingredients>
              <ingredient id="1" amountunit="g">
                <name>Sealiha</name>
                <description>Rakvere sealiha</description>
              </ingredient>
              <ingredient id="2" amountunit="g">
                <name>Hapukoor</name>
                <description>Alma hapukoor</description>
              </ingredient>
            </ingredients>
            <rating ratingvalue="9">
              <comments>
                <comment>
                  <commenttext><![CDATA[Parim päevaroog viimasel ajal.]]></commenttext>
                  <username>peeterpakiraam66</username>
                  <userid>3a9dd653-30fb-40ab-97b1-33cb82d336eb</userid>
                </comment>
              </comments>
            </rating>
            <promotion id="1" type="visual" classname="bold-red-border-2px">
              <name>2px red border</name>
              <description><![CDATA[Displays a 2px wide red border around the component that is promoted.]]></description>
              <validto>2018-06-25T12:00:00</validto>
            </promotion>
          </dish>
          <dish id="2" vegan="true" lactosefree="true" glutenfree="true" daily="true" kcal="320" price="4">
            <title><![CDATA[Kurgisalat]]></title>
            <description><![CDATA[Erinevatel viisidel töödeldud kurk serveeritud koos jõhvikatega. Tervislik amps.]]></description>
            <availablefrom>2018-05-27T12:00:00</availablefrom>
            <availableto>2018-05-28T12:00:00</availableto>
            <ingredients/>
            <rating ratingvalue="4">
              <comments>
                <comment>
                  <commenttext><![CDATA[Jäin veel väga näljaseks peale selle söömist.]]></commenttext>
                  <username>ingupingu22</username>
                  <userid>45b09f05-33fd-4041-995e-ecdb7ec61939</userid>
                </comment>
              </comments>
            </rating>
          </dish>
        </dishes>
      </menu>
      <menu id="2">
        <name><![CDATA[Põhimenüü]]></name>
        <dishes>
          <dish id="4" vegan="false" lactosefree="false" glutenfree="false" daily="false" kcal="550" price="5">
            <title><![CDATA[6 pelmeeni]]></title>
            <description><![CDATA[6 pelmeeni serveeritud tervisliku koguse hapukoore ja tilliga]]></description>
            <ingredients>
              <ingredient id="1" amountunit="g">
                <name>Sealiha</name>
                <description>Rakvere sealiha</description>
              </ingredient>
            </ingredients>
            <rating ratingvalue="9.5">
              <comments>
                <comment>
                  <commenttext><![CDATA[Käin seda iga päev söömas.]]></commenttext>
                  <username>ingupingu22</username>
                  <userid>45b09f05-33fd-4041-995e-ecdb7ec61939</userid>
                </comment>
                <comment>
                  <commenttext><![CDATA[Parim toit selles restoranis.]]></commenttext>
                  <username>peeterpakiraam66</username>
                  <userid>3a9dd653-30fb-40ab-97b1-33cb82d336eb</userid>
                </comment>
              </comments>
            </rating>
          </dish>
          <dish id="5" vegan="false" lactosefree="false" glutenfree="true" daily="false" kcal="460" price="3.8">
            <title><![CDATA[Kartulisalat]]></title>
            <description><![CDATA[Sinki sisaldav traditsiooniline kartulisalat.]]></description>
            <ingredients/>
          </dish>
          <dish id="6" vegan="false" lactosefree="false" glutenfree="false" daily="false" kcal="610" price="3.9">
            <title><![CDATA[Šokolaadikook]]></title>
            <description><![CDATA[Ahvatlev glasuuritud šokolaadikook.]]></description>
            <ingredients/>
            <rating ratingvalue="7.8">
              <comments>
                <comment>
                  <commenttext><![CDATA[Šokolaadikooki on keeruline metsa keerata.]]></commenttext>
                  <username>mutikas3</username>
                  <userid>3a29b94a-3f17-469d-a44d-f7d85e98dd3e</userid>
                </comment>
              </comments>
            </rating>
          </dish>
        </dishes>
      </menu>
    </menus>
    <dishes/>
    <address id="1" country="Eesti">
      <addressfirstline>Tatari 12</addressfirstline>
      <locality>Tallinn</locality>
      <postcode>10132</postcode>
      <region>Harjumaa</region>
    </address>
    <promotion/>
    <rating ratingvalue="7.5">
      <comments>
        <comment>
          <commenttext><![CDATA[Parim restoran üldse!]]></commenttext>
          <username>peeterpakiraam66</username>
          <userid>3a9dd653-30fb-40ab-97b1-33cb82d336eb</userid>
        </comment>
        <comment>
          <commenttext><![CDATA[Ma sain ainult 5 pelmeeni. Ootasin kuute.]]></commenttext>
          <username>kyllikekallike1</username>
          <userid>6af271dd-0e11-4ae6-98be-111762664318</userid>
        </comment>
      </comments>
    </rating>
  </restaurant>
  <restaurant id="2" url="https://kolmkokka.ee" contactnumber="6522351" email="kolmkokka@gmail.com">
    <name><![CDATA[Kolm kokka]]></name>
    <menus/>
    <dishes>
      <dish id="7" vegan="false" lactosefree="true" glutenfree="false" daily="false" kcal="470" price="14">
        <title><![CDATA[Ahjulõhe juurikatega]]></title>
        <description><![CDATA[Ahjulõhe serveeritud püreestatud porgandite ja lillkapsaga.]]></description>
        <ingredients>
          <ingredient id="11" amountunit="tk" amount="1">
            <name>Ahjulõhe</name>
            <description><![CDATA[Värske ahjulõhe]]></description>
          </ingredient>
          <ingredient id="12" amountunit="g">
            <name>Püreestatud porgand</name>
            <description><![CDATA[Eesti porgand, mis on kergelt püreestatud]]></description>
          </ingredient>
          <ingredient id="13" amountunit="g">
            <name>Püreestatud lillkapsas</name>
            <description><![CDATA[Eesti lillkapsas, mis on kergelt püreestatud ja maitsestatud]]></description>
          </ingredient>
        </ingredients>
        <rating ratingvalue="8.9">
          <comments>
            <comment>
              <commenttext><![CDATA[Kindlasti minu üks lemmikuid.]]></commenttext>
              <username>ingupingu22</username>
              <userid>45b09f05-33fd-4041-995e-ecdb7ec61939</userid>
            </comment>
            <comment>
              <commenttext><![CDATA[Pole midagi halba öelda.]]></commenttext>
              <username>tiiuviiu</username>
              <userid>0730a7b7-3a9c-4e26-962f-da9a8daeab8a</userid>
            </comment>
          </comments>
        </rating>
      </dish>
      <dish id="8" vegan="true" lactosefree="true" glutenfree="true" daily="true" dailyprice="5">
        <title><![CDATA[Ingverisupp]]></title>
        <description><![CDATA[Ingveriga vürtsistatud ingverisupp, mis viib keele alla.]]></description>
        <availablefrom>2018-05-25T12:00:00</availablefrom>
        <availableto>2018-06-25T12:00:00</availableto>
        <ingredients>
          <ingredient id="14" amountunit="g" amount="20">
            <name>Ingver</name>
            <description><![CDATA[Tükeldatud ingver]]></description>
          </ingredient>
        </ingredients>
        <rating ratingvalue="6">
          <comments>
            <comment>
              <commenttext><![CDATA[Liiga palju ingverit.]]></commenttext>
              <username>kyllikekallike1</username>
              <userid>6af271dd-0e11-4ae6-98be-111762664318</userid>
            </comment>
          </comments>
        </rating>
      </dish>
      <dish id="9" vegan="false" lactosefree="false" glutenfree="true" daily="false" kcal="340" price="4.5">
        <title><![CDATA[Apelsinijäätis]]></title>
        <description><![CDATA[Apelsinimaitseline jäätis serveeritud riivitud apelsinikoorega.]]></description>
        <ingredients/>
        <rating ratingvalue="8">
          <comments>
            <comment>
              <commenttext><![CDATA[Väga huvitav magustoit, prooviks veel.]]></commenttext>
              <username>mutikas3</username>
              <userid>3a29b94a-3f17-469d-a44d-f7d85e98dd3e</userid>
            </comment>
            <comment>
              <commenttext><![CDATA[Üks mu lemmikuid magustoite.]]></commenttext>
              <username>ingupingu22</username>
              <userid>45b09f05-33fd-4041-995e-ecdb7ec61939</userid>
            </comment>
          </comments>
        </rating>
      </dish>
    </dishes>
    <address id="2" country="Eesti">
      <addressfirstline>Laia 12</addressfirstline>
      <locality>Tallinn</locality>
      <postcode>13022</postcode>
      <region>Harjumaa</region>
    </address>
    <promotion/>
    <rating ratingvalue="8.1">
      <comments>
        <comment>
          <commenttext><![CDATA[Hinnad on natuke liiga kallid minu jaoks, aga toit om maitsev.]]></commenttext>
          <username>peeterpakiraam66</username>
          <userid>3a9dd653-30fb-40ab-97b1-33cb82d336eb</userid>
        </comment>
        <comment>
          <commenttext><![CDATA[Ma kindlasti telliks kala uuesti. Väga mahlakas oli.]]></commenttext>
          <username>kyllikekallike1</username>
          <userid>6af271dd-0e11-4ae6-98be-111762664318</userid>
        </comment>
        <comment>
          <commenttext><![CDATA[Asukoht oli päris tore. Miljöö ka.]]></commenttext>
          <username>mutikas3</username>
          <userid>3a29b94a-3f17-469d-a44d-f7d85e98dd3e</userid>
        </comment>
      </comments>
    </rating>
  </restaurant>
</restaurants>

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="restaurants">
    <xs:complexType>
      <xs:sequence minOccurs="0" maxOccurs="unbounded">
        <xs:element name="restaurant">
          <xs:complexType>
            <xs:sequence>
              <xs:element name="name" type="xs:string" />
              <xs:element name="menus">
                <xs:complexType>
                  <xs:sequence minOccurs="0" maxOccurs="unbounded">
                    <xs:element name="menu">
                      <xs:complexType>
                        <xs:sequence>
                          <xs:element name="name" type="xs:string" />
                          <xs:element name="activefrom" type="xs:dateTime" minOccurs="0" />
                          <xs:element name="activeto" type="xs:dateTime" minOccurs="0" />
                          <xs:element name="dishes">
                            <xs:complexType>
                              <xs:sequence minOccurs="0" maxOccurs="unbounded">
                                <xs:element name="dish">
                                  <xs:complexType>
                                    <xs:sequence>
                                      <xs:element name="title" type="xs:string" />
                                      <xs:element name="description" type="xs:string" />
                                      <xs:element name="availablefrom" type="xs:dateTime" minOccurs="0" />
                                      <xs:element name="availableto" type="xs:dateTime" minOccurs="0" />
                                      <xs:element name="servetime" type="xs:dateTime" minOccurs="0" />
                                      <xs:element name="ingredients">
                                        <xs:complexType>
                                          <xs:sequence minOccurs="0" maxOccurs="unbounded">
                                            <xs:element name="ingredient">
                                              <xs:complexType>
                                                <xs:sequence>
                                                  <xs:element name="name" type="xs:string" />
                                                  <xs:element name="description" type="xs:string" />
                                                </xs:sequence>
                                                <xs:attribute name="id" type="xs:int" use="required" />
                                                <xs:attribute name="amountunit" type="xs:string" use="required" />
                                                <xs:attribute name="amount" type="xs:decimal" use="optional" />
                                              </xs:complexType>
                                            </xs:element>
                                          </xs:sequence>
                                        </xs:complexType>
                                      </xs:element>
                                      <xs:element name="rating" minOccurs="0">
                                        <xs:complexType>
                                          <xs:sequence minOccurs="0" maxOccurs="unbounded">
                                            <xs:element name="comments">
                                              <xs:complexType>
                                                <xs:sequence minOccurs="0" maxOccurs="unbounded">
                                                  <xs:element name="comment">
                                                    <xs:complexType>
                                                      <xs:sequence>
                                                        <xs:element name="commenttext" type="xs:string" />
                                                        <xs:element name="username" type="xs:string" />
                                                        <xs:element name="userid" type="xs:string" />
                                                      </xs:sequence>
                                                    </xs:complexType>
                                                  </xs:element>
                                                </xs:sequence>
                                              </xs:complexType>
                                            </xs:element>
                                          </xs:sequence>
                                          <xs:attribute name="ratingvalue" type="xs:decimal" use="required" />
                                        </xs:complexType>
                                      </xs:element>
                                      <xs:element name="promotion" minOccurs="0">
                                        <xs:complexType>
                                          <xs:sequence>
                                            <xs:element name="name" type="xs:string" />
                                            <xs:element name="description" type="xs:string" />
                                            <xs:element name="validto" type="xs:dateTime" minOccurs="0" />
                                          </xs:sequence>
                                          <xs:attribute name="id" type="xs:int" use="required" />
                                          <xs:attribute name="type" type="xs:string" use="required" />
                                          <xs:attribute name="classname" type="xs:string" use="required" />
                                        </xs:complexType>
                                      </xs:element>
                                    </xs:sequence>
                                    <xs:attribute name="id" type="xs:int" use="required" />
                                    <xs:attribute name="vegan" type="xs:boolean" use="optional" />
                                    <xs:attribute name="lactosefree" type="xs:boolean" use="optional" />
                                    <xs:attribute name="glutenfree" type="xs:boolean" use="optional" />
                                    <xs:attribute name="daily" type="xs:boolean" use="optional" />
                                    <xs:attribute name="kcal" type="xs:int" use="optional" />
                                    <xs:attribute name="weightg" type="xs:decimal" use="optional" />
                                    <xs:attribute name="price" type="xs:decimal" use="optional" />
                                    <xs:attribute name="dailyprice" type="xs:decimal" use="optional" />
                                  </xs:complexType>
                                </xs:element>
                              </xs:sequence>
                            </xs:complexType>
                          </xs:element>
                          <xs:element name="promotion" minOccurs="0">
                            <xs:complexType>
                              <xs:sequence>
                                <xs:element name="name" type="xs:string" />
                                <xs:element name="description" type="xs:string" />
                                <xs:element name="validto" type="xs:dateTime" minOccurs="0" />
                              </xs:sequence>
                              <xs:attribute name="id" type="xs:int" use="required" />
                              <xs:attribute name="type" type="xs:string" use="required" />
                              <xs:attribute name="classname" type="xs:string" use="required" />
                            </xs:complexType>
                          </xs:element>
                        </xs:sequence>
                        <xs:attribute name="id" type="xs:int" use="required" />
                        <xs:attribute name="repetitioninterval" type="xs:int" use="optional" />
                      </xs:complexType>
                    </xs:element>
                  </xs:sequence>
                </xs:complexType>
              </xs:element>
              <xs:element name="dishes">
                <xs:complexType>
                  <xs:sequence minOccurs="0" maxOccurs="unbounded">
                    <xs:element name="dish">
                      <xs:complexType>
                        <xs:sequence>
                          <xs:element name="title" type="xs:string" />
                          <xs:element name="description" type="xs:string" />
                          <xs:element name="availablefrom" type="xs:dateTime" minOccurs="0" />
                          <xs:element name="availableto" type="xs:dateTime" minOccurs="0" />
                          <xs:element name="servetime" type="xs:dateTime" minOccurs="0" />
                          <xs:element name="ingredients">
                            <xs:complexType>
                              <xs:sequence minOccurs="0" maxOccurs="unbounded">
                                <xs:element name="ingredient">
                                  <xs:complexType>
                                    <xs:sequence>
                                      <xs:element name="name" type="xs:string" />
                                      <xs:element name="description" type="xs:string" />
                                    </xs:sequence>
                                    <xs:attribute name="id" type="xs:int" use="required" />
                                    <xs:attribute name="amountunit" type="xs:string" use="required" />
                                    <xs:attribute name="amount" type="xs:decimal" use="optional" />
                                  </xs:complexType>
                                </xs:element>
                              </xs:sequence>
                            </xs:complexType>
                          </xs:element>
                          <xs:element name="rating">
                            <xs:complexType>
                              <xs:sequence>
                                <xs:element name="comments">
                                  <xs:complexType>
                                    <xs:sequence minOccurs="0" maxOccurs="unbounded">
                                      <xs:element name="comment">
                                        <xs:complexType>
                                          <xs:sequence>
                                            <xs:element name="commenttext" type="xs:string" />
                                            <xs:element name="username" type="xs:string" />
                                            <xs:element name="userid" type="xs:string" />
                                          </xs:sequence>
                                        </xs:complexType>
                                      </xs:element>
                                    </xs:sequence>
                                  </xs:complexType>
                                </xs:element>
                              </xs:sequence>
                              <xs:attribute name="ratingvalue" type="xs:decimal" use="required" />
                            </xs:complexType>
                          </xs:element>
                          <xs:element name="promotion" minOccurs="0">
                            <xs:complexType>
                              <xs:sequence>
                                <xs:element name="name" type="xs:string" />
                                <xs:element name="description" type="xs:string" />
                                <xs:element name="validto" type="xs:dateTime" minOccurs="0" />
                              </xs:sequence>
                              <xs:attribute name="id" type="xs:int" use="required" />
                              <xs:attribute name="type" type="xs:string" use="required" />
                              <xs:attribute name="classname" type="xs:string" use="required" />
                            </xs:complexType>
                          </xs:element>
                        </xs:sequence>
                        <xs:attribute name="id" type="xs:int" use="required" />
                        <xs:attribute name="vegan" type="xs:boolean" use="optional" />
                        <xs:attribute name="lactosefree" type="xs:boolean" use="optional" />
                        <xs:attribute name="glutenfree" type="xs:boolean" use="optional" />
                        <xs:attribute name="daily" type="xs:boolean" use="optional" />
                        <xs:attribute name="kcal" type="xs:int" use="optional" />
                        <xs:attribute name="weightg" type="xs:decimal" use="optional" />
                        <xs:attribute name="price" type="xs:decimal" use="optional" />
                        <xs:attribute name="dailyprice" type="xs:decimal" use="optional" />
                      </xs:complexType>
                    </xs:element>
                  </xs:sequence>
                </xs:complexType>
              </xs:element>
              <xs:element name="address">
                <xs:complexType>
                  <xs:sequence>
                    <xs:element name="addressfirstline" type="xs:string" />
                    <xs:element name="locality" type="xs:string" />
                    <xs:element name="postcode" type="xs:string" />
                    <xs:element name="region" type="xs:string" />
                  </xs:sequence>
                  <xs:attribute name="id" type="xs:int" use="required" />
                  <xs:attribute name="country" type="xs:string" use="required" />
                </xs:complexType>
              </xs:element>
              <xs:element name="promotion" />
              <xs:element name="rating" minOccurs="0">
                <xs:complexType>
                  <xs:sequence>
                    <xs:element name="comments">
                      <xs:complexType>
                        <xs:sequence minOccurs="0" maxOccurs="unbounded">
                          <xs:element name="comment">
                            <xs:complexType>
                              <xs:sequence>
                                <xs:element name="commenttext" type="xs:string" />
                                <xs:element name="username" type="xs:string" />
                                <xs:element name="userid" type="xs:string" />
                              </xs:sequence>
                            </xs:complexType>
                          </xs:element>
                        </xs:sequence>
                      </xs:complexType>
                    </xs:element>
                  </xs:sequence>
                  <xs:attribute name="ratingvalue" type="xs:decimal" use="required" />
                </xs:complexType>
              </xs:element>
            </xs:sequence>
            <xs:attribute name="id" type="xs:int" use="required" />
            <xs:attribute name="url" type="xs:string" use="required" />
            <xs:attribute name="contactnumber" type="xs:string" use="required" />
            <xs:attribute name="email" type="xs:string" use="required" />
          </xs:complexType>
        </xs:element>
      </xs:sequence>
    </xs:complexType>
  </xs:element>
</xs:schema>

XSLT(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"/>
  <xsl:template match="/restaurants">
    <html>
      <head>
        <title>Täis Kõht</title>
      </head>
      <body>
        <xsl:for-each select="restaurant">
          <h1 style="color:DodgerBlue">
            <xsl:value-of select="name"/>
          </h1>
          <div>
            <xsl:value-of select="@url"/>
          </div>
          <div>
            <xsl:text>Kontakt number: </xsl:text>
            <xsl:value-of select="@contactnumber"/>
          </div>
          <div>
            <xsl:text>email: </xsl:text>
            <xsl:value-of select="@email"/>
          </div>
          <div>
            <xsl:text>Aadress: </xsl:text>
            <xsl:value-of select="address/addressfirstline"/>
            <xsl:text>, </xsl:text>
            <xsl:value-of select="address/locality"/>
            <xsl:text>, </xsl:text>
            <xsl:value-of select="address/postcode"/>
            <xsl:text>, </xsl:text>
            <xsl:value-of select="address/@country"/>
          </div>

          <xsl:choose>
            <xsl:when test="menus/menu">
              <xsl:for-each select="menus/menu">
                <xsl:sort select="name"/>
                <h3>
                  <xsl:value-of select="name"/>
                </h3>

                <xsl:if test="activefrom != '' and activeto != ''">
                  <xsl:text> </xsl:text>
                  <xsl:value-of select="substring(activefrom,1,10)"/>
                  <xsl:text> kuni </xsl:text>
                  <xsl:value-of select="substring(activeto,1,10)"/>
                </xsl:if>

                <ul>
                  <xsl:for-each select="dishes/dish">
                    <xsl:sort select="title"/>

                    <xsl:if test="availablefrom != '' and availableto != ''">
                      <xsl:value-of select="substring(availablefrom,1,10)"/>
                      <xsl:text> kuni </xsl:text>
                      <xsl:value-of select="substring(availableto,1,10)"/>
                    </xsl:if>

                    <li>
                      <span style="font-weight:bold">
                        <xsl:value-of select="title"/>
                      </span>
                      <xsl:text> - </xsl:text>
                      <xsl:value-of select="description"/>
                      <xsl:text>  </xsl:text>
                      <span style="font-weight:bold">
                        <xsl:if test="@glutenfree='true'">
                          <xsl:text>G </xsl:text>
                        </xsl:if>
                        <xsl:if test="@lactosefree='true'">
                          <xsl:text>L </xsl:text>
                        </xsl:if>
                        <xsl:if test="@vegan='true'">
                          <xsl:text>V </xsl:text>
                        </xsl:if>
                      </span>
                    </li>

                    <xsl:for-each select="ingredients/ingredient">
                      <xsl:value-of select="name"/>
                      <xsl:if test="position()!=last()">
                        <xsl:text>, </xsl:text>
                      </xsl:if>
                    </xsl:for-each>

                    <p style="font-weight:bold">
                      <xsl:text>Hind: </xsl:text>
                      <xsl:choose>
                        <xsl:when test="@price!=''">
                          <xsl:value-of select="@price"/>
                        </xsl:when>
                        <xsl:otherwise>
                          <xsl:text>-</xsl:text>
                        </xsl:otherwise>
                      </xsl:choose>
                      <xsl:text> / Päevapakkumise hind: </xsl:text>
                      <xsl:choose>
                        <xsl:when test="@dailyprice!=''">
                          <xsl:value-of select="@dailyprice"/>
                        </xsl:when>
                        <xsl:otherwise>
                          <xsl:text>-</xsl:text>
                        </xsl:otherwise>
                      </xsl:choose>
                    </p>

                    <div style="background-color:LightGray;">
                      <xsl:text>Hinnang praele: </xsl:text>
                      <xsl:choose>
                        <xsl:when test="rating/@ratingvalue!=''">
                          <xsl:value-of select="rating/@ratingvalue"/>
                        </xsl:when>
                        <xsl:otherwise>
                          <xsl:text>Ei ole veel hinnatud!</xsl:text>
                        </xsl:otherwise>
                      </xsl:choose>

                      <br/>
                      <xsl:for-each select="rating/comments/comment">
                        <xsl:value-of select="username"/>
                        <xsl:text>: </xsl:text>
                        <xsl:value-of select="commenttext"/>
                        <br/>
                      </xsl:for-each>
                    </div>
                    <br/>
                  </xsl:for-each>
                </ul>
              </xsl:for-each>
            </xsl:when>
            <xsl:otherwise>
              <ul>
                <xsl:for-each select="dishes/dish">
                  <xsl:sort select="title"/>

                  <xsl:if test="availablefrom != '' and availableto != ''">
                    <xsl:value-of select="substring(availablefrom,1,10)"/>
                    <xsl:text> kuni </xsl:text>
                    <xsl:value-of select="substring(availableto,1,10)"/>
                  </xsl:if>

                  <li>
                    <span style="font-weight:bold">
                      <xsl:value-of select="title"/>
                    </span>
                    <xsl:text> - </xsl:text>
                    <xsl:value-of select="description"/>
                    <xsl:text>  </xsl:text>
                    <span style="font-weight:bold">
                      <xsl:if test="@glutenfree='true'">
                        <xsl:text>G </xsl:text>
                      </xsl:if>
                      <xsl:if test="@lactosefree='true'">
                        <xsl:text>L </xsl:text>
                      </xsl:if>
                      <xsl:if test="@vegan='true'">
                        <xsl:text>V </xsl:text>
                      </xsl:if>
                    </span>
                  </li>

                  <xsl:for-each select="ingredients/ingredient">
                    <xsl:value-of select="name"/>
                    <xsl:if test="position()!=last()">
                      <xsl:text>, </xsl:text>
                    </xsl:if>
                  </xsl:for-each>

                  <p style="font-weight:bold">
                    <xsl:text>Hind: </xsl:text>
                    <xsl:choose>
                      <xsl:when test="@price!=''">
                        <xsl:value-of select="@price"/>
                      </xsl:when>
                      <xsl:otherwise>
                        <xsl:text>-</xsl:text>
                      </xsl:otherwise>
                    </xsl:choose>
                    <xsl:text> / Päevapakkumise hind: </xsl:text>
                    <xsl:choose>
                      <xsl:when test="@dailyprice!=''">
                        <xsl:value-of select="@dailyprice"/>
                      </xsl:when>
                      <xsl:otherwise>
                        <xsl:text>-</xsl:text>
                      </xsl:otherwise>
                    </xsl:choose>
                  </p>

                  <div style="background-color:LightGray;">
                    <xsl:text>Hinnang praele: </xsl:text>
                    <xsl:choose>
                      <xsl:when test="rating/@ratingvalue!=''">
                        <xsl:value-of select="rating/@ratingvalue"/>
                      </xsl:when>
                      <xsl:otherwise>
                        <xsl:text>Ei ole veel hinnatud!</xsl:text>
                      </xsl:otherwise>
                    </xsl:choose>

                    <br/>
                    <xsl:for-each select="rating/comments/comment">
                      <xsl:value-of select="username"/>
                      <xsl:text>: </xsl:text>
                      <xsl:value-of select="commenttext"/>
                      <br/>
                    </xsl:for-each>
                  </div>
                  <br/>
                </xsl:for-each>
              </ul>
            </xsl:otherwise>
          </xsl:choose>
          
          <div style="background-color:LightGray;">
            <xsl:text>Hinnang restoranile: </xsl:text>
            <xsl:choose>
              <xsl:when test="rating/@ratingvalue!=''">
                <xsl:value-of select="rating/@ratingvalue"/>
              </xsl:when>
              <xsl:otherwise>
                <xsl:text>Ei ole veel hinnatud!</xsl:text>
              </xsl:otherwise>
            </xsl:choose>
            
            
            <br/>
            <xsl:for-each select="rating/comments/comment">
              <xsl:value-of select="username"/>
              <xsl:text>: </xsl:text>
              <xsl:value-of select="commenttext"/>
              <br/>
            </xsl:for-each>
          </div>

          <p>
            <xsl:text>*G - gluteenivaba, </xsl:text>
            <xsl:text>L - laktoosivaba, </xsl:text>
            <xsl:text>V - vegan </xsl:text>
          </p>
          
        </xsl:for-each>
      </body>
    </html>
  </xsl:template>
</xsl:stylesheet>

XSLT(XML)

XML'is kuvatakse elemendid ja atribuudid eesti keeles. Kuvatakse restorani andmed ja toidud, mille hinnang on 8 või rohkem punkti.

<?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="/restaurants">
    <restoranid>
      <xsl:for-each select="restaurant">
        <restoran url="{@url}" telefon="{@contactnumber}" email="{@email}">
          <nimi>
            <xsl:value-of select="name"/>
          </nimi>
          <aadress>
            <postiaadress>
              <xsl:value-of select="address/addressfirstline"/>
            </postiaadress>
            <linn>
              <xsl:value-of select="address/locality"/>
            </linn>
            <indeks>
              <xsl:value-of select="address/postcode"/>
            </indeks>
            <riik>
              <xsl:value-of select="address/@country"/>
            </riik>
          </aadress>

          <parimadToidud>
            <xsl:choose>
              <xsl:when test="menus/menu/dishes/dish">
                <xsl:for-each select="menus/menu/dishes/dish">
                  <xsl:if test="rating/@ratingvalue &gt;= 8.0">
                    <heaToit hinnang="{rating/@ratingvalue}" paevapakkumiseHind="{@dailyprice}" hind="{@price}" 
                             vegan="{@vegan}" laktoosivaba="{@lactosefree}" gluteenivaba="{@glutenfree}">
                      <nimi>
                        <xsl:value-of select="title"/>
                      </nimi>
                      <kirjeldus>
                        <xsl:value-of select="description"/>
                      </kirjeldus>
                    </heaToit>
                  </xsl:if>
                </xsl:for-each>
              </xsl:when>
              <xsl:otherwise>
                <xsl:for-each select="dishes/dish">
                  <xsl:if test="rating/@ratingvalue &gt;= 8.0">
                    <heaToit hinnang="{rating/@ratingvalue}" paevapakkumiseHind="{@dailyprice}" hind="{@price}" 
                             vegan="{@vegan}" laktoosivaba="{@lactosefree}" gluteenivaba="{@glutenfree}">
                      <nimi>
                        <xsl:value-of select="title"/>
                      </nimi>
                      <kirjeldus>
                        <xsl:value-of select="description"/>
                      </kirjeldus>
                    </heaToit>
                  </xsl:if>
                </xsl:for-each>
              </xsl:otherwise>
            </xsl:choose>
          </parimadToidud>
        </restoran>
      </xsl:for-each>
    </restoranid>
  </xsl:template>
</xsl:stylesheet>

Retsensioonid

Veebiteenuse retsensioon: Team MOT retsensioon

Klientrakenduse retsensioon: Team Kuldneloojang retsensioon

XML retsensioon: Team MOT retsensioon

Logiraamat

07.02.2018

Gupi loomine ning suhtluskanali seadistamine

24.03.2018

Trello boardi loomine

25.03.2018

Wiki lehe loomine. Esimene tõsisem projekti koosolek ning tegevuskava paika panemine.

26.03.2018

Alustasime veebiteenuse analüüsiga ja funktsionaalsuse kirjeldamisega.

27.03.2018

Täiendasime veebiteenuse analüüsi.

Esmase andmebaasi mudeli koostamine.

Postitatud esimeste analüüsi arutelude põhjal valminud algne prototüüp.

30.03.2018

Valmis andmebaasimudel.

08.04.2018

Tehtud muudatused analüüsis vastavalt tagasisidele.

19.04.2018

Loodud projekti põhi koos domeenimudelitega. Projekt lisatud Githubi: https://github.com/sikumiku/TaisKohtApi

05-06.05.2018

Osalesime hackathonil. Lisatud controllerid, service'id koos vajalike kihtidega.

16.05.2018

Uuendatud andmebaasimudelit, DTO'sid.

18-19.05.2018

Lisatud klientrakendus, security, äriloogikat.

20.05.2018

Lisatud API dokumentatsioon.

22-24.05.2018

Uuendatud contoller'eid, service'id.

25-27.05.2018

Uuendatud contoller'eid, service'id. Lisatud kasutajate ja rollide kontrolle meetoditesse. Täiustatud klientrakendust. Swagger dokumentatsioon.

31.05-01.06.2018

Tegeletud XML, XSD ja XSLT failide loomisega.

01.06-03.06.2018

Tegeletud XML ja XSLT failide täiustamisega ja retsensioonide kirjutamisega.

05.06.2018

Lisatud XML retsensioon meeskonnale MOT

08.06.2018

Lisatud uus projekti .zip muudatustega ja täiendustega