WatchWinders

From ICO wiki

Meeskond

Toomas Juhkov, toomas.juhkov@gmail.com
Tiit Kuuskmäe, tiit.kuuskmae@gmail.com
Kunnar Kukk, Kunnar.kukk@gmail.com

Tegevuste logi

08.02.2018 - meeskonna loomine, kokkuleppe sõlmimine teema osas
10.02.2018 - õppejõu teavitamine koos esmase andmemudeliga
15.02.2018 - lõpliku andmemudeli valmimine(versioon 5)
18.02.2018 - õppejõu kinnitus, et teema ja meeskond kvalifiseeruvad
22.02.2018 - viki lehe loomine ja analüüsi koostamise alustamine
26.02.2018 - tiimi lisandus Kunnar Kukk
01.03.2018 - tiimi sprint koosolek(seoses täiendava tiimiliikme lisandumisega täpsustasime skoopi)
03.03.2018 - alustasime koodi kirjutamisega (tabelid klassideks ja annotatsioonid peale)
06.03.2018 - täiendasime andmemudelit olemiga "UserTypeState" (et saaks muuta kasutajatüüpi)
08.03.2018 - tiimi sprint koosolek
18.03.2018 - lisasime olemid "Address", "AddressU", "AddressM", "OfferOfUser", "MediaInOffer"
22.03.2018 - tiimi sprint koosolek, mis oli pühendatud andmemudeli täiendamisele
23.03.2018 - pikem tiimi hackathon andmemudeli teemal
29.03.2018 - täiendatud wiki lehte täielikult ringitöötatud andmemudeliga
30.03.2018 - täiendatud wiki lehte andmemudeli metainfoga
09.04.2018 - silusime andmemudelit (mõningad ebavajalikud viited olid sisse jäänud)
06.05.2018 - panime uuendatud andmemudeli projekti wiki lehele üles
07.05.2018 - osalesime õppejõu Mait Poska korraldatud hackathonil
08.06.2018 - XML ülesanne, klientrakenduse ja API kood sai wiki lehel avalikuks
19.06.2018 - seoses ärilise konfidentsiaalsuse nõudega eemaldasime nii andmemudeli kui koodi (kooskõlastatud õppejõuga)

Terve maikuu oli igapäevane suhtlus üle Skype'i ja fleep.io, mida me ei loginud (liiga tihedaks läks!).

Kasutatavad metoodikad

WatchWinders projektis plaanime kasutada peamiselt kaht metoodikat:

1. läbiv retsenseerimine (üks paariline teeb midagi valmis ja teine teeb sellele ülevaatuse; üksus lisatakse kui ülevaatus on lõplikult läbitud ning osapoolte vahel on saabunud konsensus)
2. paaris programmeerimine (istume ühe masina taha maha ja kirjutame kolmekesi)

Tulenevalt kaugõppe formaadist saab ülekaalus olema esimene variant. Teist metoodikat kasutame nende osade kirjutamiseks, mille puhul võib arvata, et nende lahendamine on keeruline või et lahenduskäike võib olla väga erinevaid. Püüame jooksvalt siinses vikis kajastada ka seda, millise metoodika järgi üks või teine osa valmis.

Projekti osad

Alljärgnevalt anname ülevaate projekti osadest:

(1) andmemudel
(2) veebiteenuse analüüs
(3) Minimum Viable Product definitsioon
(4) Inkrement #1
(5) Inkrement #2
(6) Inkrement #3

Projekti osad on meie viki lehel kirjeldatud valmimise järjekorras.

Andmemudel

Andmemudeli koostamisel lähtusime õppejõu ette antud kriteeriumist, mille järgi andmemudelis on vähemalt 6 põhiolemit (tähistatud tumesinise ja lillaga - meie mudelis 11 tk), mille hulgas ei sisaldu kasutajate tabelit (Person).

Põhiolemid:

  • Watch - kell
  • Person - kasutaja/kollektsionäär või firma esindaja
  • Box - kellakarp
  • Organization - tootjad, edasimüüjad, hooldus
  • Settings - kellakarbi seade
  • Offer - pakkumine kollektsionäärile
  • Media - meediafailid kellade ja karpide kohta
  • Address - kasutaja/organisatsiooni aadress
  • ElectronicContact - elektrooniliste kontaktide loend
  • Log - logi
  • Access - paroolid ja kasutajanimed
  • Error - vead


Type abiolemid:

  • WatchType - kellatüübid
  • PersonType - isikutüübid: admin, kasutaja
  • OrganizationType - organisatsioonitüübid: tootja, hooldaja, edasimüüja jt
  • ActionType - logi tüübid
  • ContactType - elektroonilise kontakti tüüp: skype, email, telefon jt


Olekud:

  • PersonTypeState - isiku rolli kehtivus ajahetkel (isiku ja tüübi vaheline seos)
  • OrganizationTypeState - organisatsiooni olek(ud) ajahetkel


Kuuluvus:

  • PersonWatch - isikute ja kellade vahelised seosed
  • PersonBox - isiku ja karbi vaheline seos
  • PersonAccess - isiku ja autentimisandmete vaheline seos
  • WatchInBox - kella ja karbi vaheline seos
  • ErrorInWatch - vea ja kella vaheline seos
  • SettingsInBox - seade ja karbi vaheline seos
  • WatchInOrganization - kella ja tootja vaheline seos
  • PersonInOrganization - isiku ja ettevõtte/organisatsiooni vaheline seos
  • SettingsOfWatchType - seade ja kellatüübi vaheline seos


Abstraktsioonid:

  • Locator - abstraktsioon erinevate kontaktitüüpide jaoks
  • Party - abstraktsioon erinevate inimeste, organisatsioonide jt jaoks


Kommunikatsioon:

  • OfferToPerson - isikule suunatud pakkumised
  • OfferFromPerson - isikult lähtuv pakkumine




[ andmemudel eemaldatud 19.06.2018 seoses ärilise konfidentsiaalsuse nõudega; andmemudeli eemaldamine kooskõlastatud õppejõuga ]


Andmemudel valmis (1) metoodika abil, vt lähemalt projekti metoodikate sektsioonist.

Veebiteenuse analüüs

Alljärgnevalt esitame kellakarpide infosüsteemi analüüsi järgmistes osades:

(1) toote/teenuse kirjeldus
(2) probleemi kirjeldus
(3) lahendus
(4) süsteemi kasutajad
(5) loodava API võimalused
(6) funktsionaalsed nõuded
(7) mittefunktsionaalsed nõuded

Toote/teenuse kirjeldus

Teenuse eesmärgiks on lahendada probleem, kuidas jälgida kellakarpide kaudu mehhaaniliste käekellade seisundit ning keerata neid automaatselt üles distantsilt, näiteks juhul, kui kellade kollektsionäär ei asu füüsiliselt igapäevaselt oma kellade juures. Teenus võimaldab ka tootjal uuendada kontaktandmeid, saata personaalseid teateid. Administraatorile annab ülevaate platvormis toimuvast.

Probleemi kirjeldus

Mehhaaniliste käekellade kollektsionääride huvi kellakarpide vastu on mitmetahuline. Kollektsionääre huvitab arvepidamine – mitu kella, missuguste omaduste ja eripäradega neil kollektsioonis on. Teisalt soov eksponeerida oma kogusid kolleegidele ja sõpradele. Kellakarpidega täidetud sein või riiulid loovad selleks suurepärase võimaluse. Kolmandaks praktiline vajadus tagada kellavedrude õige vinnastatuse aste. Kaasaja mehhaanilised kellamehhanismid (automatic) võimaldavad kellakorpuse füüsilisel liigutamisel kellavedru automaatselt vinnastada.

Näiteks piisab kella kinnitamisest käele, et see hakkaks end käe liigutamisel üles keerama. Kuidas keerata aga üles seisvat kella, mis seisab paigal kellakarbis? Selleks on tänapäeva kellakarpides elektrimootor, mis tagab kella liigutamise vastavalt kellatüübile ettenähtud ööpäevase graafiku alusel. Kollektsionääride üheks probleemiks on see, kuidas juhtida sellist karpi ja omada jooksvat ülevaadet kõikidest oma karpidest, nende tehnilisest seadistusest ja kollektsiooni kuuluvatest kelladest olles tööreisil välisriigis?

Lahendus

Probleemi lahendamiseks ehitame REST + ASP.NET Core + WebAPI koos kliendiga, mis võimaldab kellakollektsionääridel hallata kellasid karpides, administraatoritel saada ülevaate süsteemist ning tootjatele lisada infot kollektsionääridele. Käesoleva projekti käigus ei tegeleta kahe IoT lahenduse kui terviku seisukohast olulise nüansiga: (i) karbi püsivara (firmware) loomisega, (ii) klientrakenduse frontend lõpliku häälestamisega (ilus .css ja pildid).

Süsteemi kasutajad

Kollektsionäär - haldab ja muudab infot oma kellade ning oma kellakarpide kohta.
Administraator - haldab süsteemi infot, vajadusel eemaldab probleeme.
Tootja - saab saata süsteemi kollektsionääridele mõeldud infot.
Hooldaja - saab infot hooldamist vajavate kellade ja -karpide kohta.

Loodava API võimalused

Alljärgnevalt vaatame loodava API võimalusi kasutajagruppide kaupa:

Kasutajale/kellakollektsionäärile

  • Saada ülevaade kelladest ja kellakarpidest (lisada, muuta, vaadata).
  • Muuta karbis oleva kella kohta käivat informatsiooni (kella nimi, tootja, tootmise aeg, viitenumber, mehhanism, jõureservi jääk (valikuline), foto kellast, kellarihma liik, muu).
  • Karpe juhtida (keerata automaatselt üles). Näha ja muuta kella üleskeerava mehhanismi liikumise kiirust ja muid detaile (nt liikumise suund).
  • Näha karbi seisundit, kas karp on hetkel wifiga ühendatud või mitte.
  • Näha karbilt saabunud veateateid.
  • Sisestada infot kella seisundist.
  • Suhelda hooldajate ja tootjatega

Tootjale

  • Uuendada kontaktandmeid.
  • Saata personaalseid teateid kellakollektsionääridele.

Administraatorile

  • Näha võrku ühendatud karpide arvu.
  • Näha karpidesse paigaldatud kellade nimistut (ilma muude kasutajaandmeteta).
  • Näha kellade üleskeeramise mehhanismide arvnäitajaid (madalaim kiirus, keskmine kiirus, tippkiirus; samuti liikumise suundade arvnäitajaid).
  • IP-aadresside järgi ülevaadet, millistes riikides karpe kasutatakse (üldised arvandmed).
  • Veateatega karpide arv süsteemis (üldised arvandmed).

Funktsionaalsed nõuded

  • Kõik tegevused peavad olema autoriseeritud.
  • REST + ASP.NET Core + WebAPI tagastab infot valideeritud JSON formaadis.
  • API veatöötlus on tehtud, kasutades standardseid veakoode.
  • API-le on tehtud 'health check'.
  • Kõik kasutajad peavad saama end autoriseerida.
  • Kasutaja, tootja ja administraatori tegevused logitakse.
  • Logi ei ole muudetav, vaid on kättesaadav.
  • Kasutaja ega tootja ei näe teiste kasutajate ega tootjate infot.
  • API klient on kohanduv mobiilile, tahvlile ja desktoparvutile.
  • Sisendandmed on valideeritud.

Mittefunktsionaalsed nõuded

  • API-le on lisatud Swagger fail.
  • Teenus ja tehniline lahendus on dokumenteeritud.
  • Süsteemi kood on vabalt ligipääsetav, täiendatav ning allalaetav.

Esitame töö etappidena, kus kõigepealt defineerime ära Minimum Viable Producti [1] ehk selle miinimumi, mis pakub kasutajale maksimaalset väärtust. See tähendab API loomist ning esmase kliendi loomist kõigile MVP-s defineeritud kasutajagruppidele. Seejärel realiseerime projekti inkrementide ehk tootearenduse sammudena, milles lisame uusi funktsionaalsusi. Ideaalis jõuame realiseerida kõik sammud, kuid kuna teeme nimetatud tööd antud ajaraamis ja koosseisus esmakordselt, siis võtame vabaduse ka realiseerida korralikult niipalju, kui jõuame projekti käigus ära teha.

Võimalik, et meie lõpptulemus erineb mõnes osas esitatud analüüsist. Seda olenevalt sellest kui edukalt meil õnnestub plaanitud funktsionaalsusi realiseerida. Erisusi plaanime käsitleda vastavas 'post production' jaotuses, kus loendame ka projektist kogunenud peamised õppetunnid.

Analüüs valmis (1) metoodika abil, vt lähemalt projekti metoodikate sektsioonist.

Minimum Viable Product definitsioon

Kasutaja

  • Kasutaja saab süsteemi logida ning välja logida
  • Kasutaja saab lisada uue kellakarbi
  • Kasutaja saab lisada uue kella kellakarpi
  • Kasutaja saab muuta kella infot kellakarbis
  • Kasutaja saab näha ülevaadet oma karpidest
  • Kasutaja saab muuta kellakarbi seadeid
  • Kasutaja saab kustutada kella karbis
  • Kasutaja saab kustutada kellakarbi

Administraator

  • Näeb autoriseeritult logisid

Tootja

  • Saab lisada kontaktandmeid
  • Saab kontaktandmeid muuta

Inkrement #1

Kasutaja

  • Näeb karbilt saabunud veateateid.
  • Saab kellakarpe juhtida.

Administraator

  • IP-aadresside järgi ülevaadet, millistes riikides karpe kasutatakse (üldised arvandmed).
  • Karpidesse paigaldatud kellade nimistut (ilma muude kasutajaandmeteta).
  • Näeb kasutajate statistikat.

Tootja

  • Saata personaalseid teateid kellakollektsionääridele.

Inkrement #2

Kasutaja

  • Saab saata teate tootjale.
  • Näeb tootja vastust.
  • Näha karbi seisundit, kas karp on hetkel wifiga ühendatud või mitte.

Administraator

  • Näha kellade üleskeeramise mehhanismide arvnäitajaid (madalaim kiirus, keskmine kiirus, tippkiirus; samuti liikumise suundade arvnäitajaid).
  • Näeb tegevuste ajalugu kliendist.

Tootja

  • Saab lugeda kasutaja teadet.
  • Saab vastata kasutajale.

Hooldaja

  • Saab info hooldamist vajavate kellade kohta.

Inkrement #3

Kasutaja

  • On ehitatud keeletugi.

Tootja

  • On ehitatud keeletugi.

Hooldaja

  • On ehitatud keeletugi.

Inkrementid valmisid (1) metoodika abil, vt lähemalt projekti metoodikate sektsioonist.

XML ülesanne

Järgnevalt esitame oma XML-ülesande tulemused.

IT Management Transformation XML To XML

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl">
    <xsl:output method="xml" indent="yes"/>
    <xsl:template match="/">
      <unhandledDevices>
        <xsl:for-each select="customers/customer/locations/location/items/item[@isInWorkingCondition='false']">
          <device>
            <name>
              <xsl:value-of select="manufacturer"/>
              <xsl:text> </xsl:text>
              <xsl:value-of select="model"/>
            </name>
            <ticket>
              <xsl:for-each select="tickets/ticket[@isHandled='false']">
                <ticket_id>
                  <xsl:value-of select="@id"/>
                </ticket_id>
                <issued_in>
                  <xsl:value-of select="@issuedIn"/>
                </issued_in>
                <description>
                  <xsl:value-of select="description"/>
                </description>
              </xsl:for-each>
            </ticket>
          </device>
        </xsl:for-each>
      </unhandledDevices>
    </xsl:template>
</xsl:stylesheet>

IT Management Transformation 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="/">
    <!--This transformation displays all laptops under our maintenance.
    Laptops are sorted by 'isActive' parameter, inactive items are at the of the list.
    If there are any tickets issued to the related laptop, they are displayed as well.
    We use colorcode - light green for handled and light red for unhandled tickets.-->
    <html>
      <head>
        <title>ItManagement Laptops</title>
      </head>
      <body style="background-color:#F7F7F7">
        <h1>Computers</h1>
        <xsl:for-each select="/customers/customer/locations/location/items/item[@type='laptopComputer']">
          <xsl:sort select="@isActive='false'"/>
          <hr />
          <div>
            <h3>
              <xsl:value-of select="manufacturer"/>
            </h3>
            <b>Model: </b>
            <xsl:value-of select="model"/>
            <br />
            <b>Serial number: </b>
            <xsl:value-of select="serialNumber"/>
            <br />
            <b>Date of purchase: </b>
            <xsl:value-of select="dateOfPurchase"/>
            <br />
            <b>Operating system: </b>
            <xsl:value-of select="operatingSystem"/>
            <br />
            <b>Is on active list: </b>
            <xsl:value-of select="@isActive"/>
            <br />
          </div>

          <div style="background-color:#E3FAEE" >
            <i>
              <xsl:for-each select="tickets/ticket">
                <xsl:choose>
                  <xsl:when test="@isHandled='true'">                    
                    <h3>Handled tickets</h3>
                    <ul>
                      <b>Issued in: </b>
                      <xsl:value-of select="@issuedIn"/>
                      <br />
                      <b>Ticket ID: </b>
                      <xsl:value-of select="@id" />
                      <br />
                      <b>Description: </b>
                      <xsl:value-of select="description"/>
                      <br />
                      <b>Solution: </b>
                      <xsl:value-of select="solution"/>
                      <br />
                    </ul>
                  </xsl:when>
                  <xsl:when test="@isHandled='false'">
                    <div style="background-color:#FDE5E5">
                      <h3>Unhandled tickets</h3>
                      <ul>
                        <b>Issued in: </b>
                        <xsl:value-of select="@issuedIn"/>
                        <br />
                        <b>Ticket ID: </b>
                        <xsl:value-of select="@id" />
                        <br />
                        <b>Description: </b>
                        <xsl:value-of select="description"/>
                        <br />
                        <b>Solution: </b>
                        <xsl:value-of select="solution"/>
                        <br />
                      </ul>
                    </div>
                  </xsl:when>
                </xsl:choose>
              </xsl:for-each>
            </i>
          </div>
        </xsl:for-each>
      </body>
    </html>
  </xsl:template>
</xsl:stylesheet>

IT Management 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="customers">
    <xs:complexType>
      <xs:sequence>
        <xs:element maxOccurs="unbounded" name="customer">
          <xs:complexType>
            <xs:sequence>
              <xs:element name="customerName" type="xs:string" />
              <xs:element minOccurs="0" name="contactPerson" type="xs:string" />
              <xs:element name="e-mail" type="xs:string" />
              <xs:element name="phoneNumber" type="xs:string" />
              <xs:element name="locations">
                <xs:complexType>
                  <xs:sequence>
                    <xs:element maxOccurs="unbounded" name="location">
                      <xs:complexType>
                        <xs:sequence>
                          <xs:element name="city" type="xs:string" />
                          <xs:element name="address" type="xs:string" />
                          <xs:element name="comment" type="xs:string" />
                          <xs:element minOccurs="0" name="localContactPerson" type="xs:string" />
                          <xs:element minOccurs="0" name="localPhone" type="xs:string" />
                          <xs:element name="items">
                            <xs:complexType>
                              <xs:sequence>
                                <xs:element maxOccurs="unbounded" name="item">
                                  <xs:complexType>
                                    <xs:sequence>
                                      <xs:element name="manufacturer" type="xs:string" />
                                      <xs:element name="model" type="xs:string" />
                                      <xs:element name="serialNumber" type="xs:string" />
                                      <xs:element name="dateOfPurchase" type="xs:date" />
                                      <xs:element minOccurs="0" name="operatingSystem">
                                        <xs:complexType>
                                          <xs:simpleContent>
                                            <xs:extension base="xs:string">
                                              <xs:attribute name="version" type="xs:string" use="required" />
                                            </xs:extension>
                                          </xs:simpleContent>
                                        </xs:complexType>
                                      </xs:element>
                                      <xs:element minOccurs="0" name="tickets">
                                        <xs:complexType>
                                          <xs:sequence>
                                            <xs:element maxOccurs="unbounded" name="ticket">
                                              <xs:complexType>
                                                <xs:sequence>
                                                  <xs:element name="description" type="xs:string" />
                                                  <xs:element name="solution" type="xs:string" />
                                                </xs:sequence>
                                                <xs:attribute name="id" type="xs:string" use="required" />
                                                <xs:attribute name="issuedIn" type="xs:date" use="required" />
                                                <xs:attribute name="isHandled" type="xs:boolean" use="required" />
                                              </xs:complexType>
                                            </xs:element>
                                          </xs:sequence>
                                        </xs:complexType>
                                      </xs:element>
                                    </xs:sequence>
                                    <xs:attribute name="id" type="xs:unsignedInt" use="required" />
                                    <xs:attribute name="type" type="xs:string" use="required" />
                                    <xs:attribute name="isActive" type="xs:boolean" use="required" />
                                    <xs:attribute name="isUnderWarranty" type="xs:boolean" use="required" />
                                    <xs:attribute name="warrantyInMonths" type="xs:unsignedByte" use="required" />
                                    <xs:attribute name="isInWorkingCondition" type="xs:boolean" use="required" />
                                  </xs:complexType>
                                </xs:element>
                              </xs:sequence>
                            </xs:complexType>
                          </xs:element>
                        </xs:sequence>
                        <xs:attribute name="type" type="xs:string" use="required" />
                      </xs:complexType>
                    </xs:element>
                  </xs:sequence>
                </xs:complexType>
              </xs:element>
            </xs:sequence>
            <xs:attribute name="id" type="xs:unsignedInt" use="required" />
            <xs:attribute name="type" type="xs:string" use="required" />
            <xs:attribute name="isActive" type="xs:boolean" use="required" />
            <xs:attribute name="hasManyLocations" type="xs:boolean" use="required" />
          </xs:complexType>
        </xs:element>
      </xs:sequence>
    </xs:complexType>
  </xs:element>
</xs:schema>

IT Management XML

<?xml version="1.0" encoding="utf-8" ?>
<customers>
  <customer id="33" type="company" isActive="true" hasManyLocations="true">
    <customerName>Elamustevaba OÜ</customerName>
    <contactPerson>Juhan Pühamees</contactPerson>
    <e-mail>juhan@evaba.com</e-mail>
    <phoneNumber>+37256565656</phoneNumber>
    <locations>
      <location type="office">
        <city>Tallinn</city>
        <address><![CDATA[Pikk 7, 56401]]></address>
        <comment><![CDATA[3. korrus -> paremat kätt]]></comment>
        <localContactPerson>Maiu Piimaauto</localContactPerson>
        <localPhone>+3726510510</localPhone>
        <items>
          <item id="1" type="laptopComputer" isActive="true" isUnderWarranty="true" warrantyInMonths="36" isInWorkingCondition="false">
            <manufacturer>Dell</manufacturer>
            <model>Inspiron 3558</model>
            <serialNumber>6FGH5JN</serialNumber>
            <dateOfPurchase>2017-07-22</dateOfPurchase>
            <operatingSystem version="1709">Windows 10 Home</operatingSystem>
            <tickets>
              <ticket id="HWI001" issuedIn="2017-11-21" isHandled="true">
                <description><![CDATA[Wi-Fi is not working]]></description>
                <solution><![CDATA[Wi-Fi card replaced (Under the warranty)]]></solution>
              </ticket>
              <ticket id="SWI256" issuedIn="2018-05-05" isHandled="false">
                <description><![CDATA[Computer keeps showing login screen]]></description>
                <solution></solution>
              </ticket>
            </tickets>
          </item>
          <item id="55" type="laptopComputer" isActive="true" isUnderWarranty="true" warrantyInMonths="60" isInWorkingCondition="true">
            <manufacturer>Dell</manufacturer>
            <model>Latitude E7550</model>
            <serialNumber>8JGR78U</serialNumber>
            <dateOfPurchase>2016-06-15</dateOfPurchase>
            <operatingSystem version="1803">Windows 10 Home</operatingSystem>
          </item>
        </items>
      </location>
      <location type="store">
        <city>Tallinn</city>
        <address><![CDATA[Lai 17, 56401]]></address>
        <comment><![CDATA[keldrikorrusel]]></comment>
        <localContactPerson>Tanja Õielemb</localContactPerson>
        <localPhone>+3726510520</localPhone>
        <items>
          <item id="83" type="desktopComputer" isActive="true" isUnderWarranty="true" warrantyInMonths="24" isInWorkingCondition="true">
            <manufacturer>Lenovo</manufacturer>
            <model>ThinkCentre M715</model>
            <serialNumber>IO25689AD54KL</serialNumber>
            <dateOfPurchase>2017-12-01</dateOfPurchase>
            <operatingSystem version="1803">Windows 10 Pro</operatingSystem>
          </item>
          <item id="12" type="monitor" isActive="true" isUnderWarranty="false" warrantyInMonths="24" isInWorkingCondition="true">
            <manufacturer>AOC</manufacturer>
            <model>L456</model>
            <serialNumber>9803987YE54</serialNumber>
            <dateOfPurchase>2015-05-03</dateOfPurchase>
            <tickets>
              <ticket id="HWI126" issuedIn="2016-01-18" isHandled="true">
                <description><![CDATA[Doesn't work, no picture.]]></description>
                <solution><![CDATA[Wasn't plugged in. Technician did it - works again!]]></solution>
              </ticket>
            </tickets>
          </item>
        </items>
      </location>
    </locations>
  </customer>
  <customer id="02" type="private" isActive="true" hasManyLocations="false">
    <customerName>Kessu Sisselasi</customerName>
    <e-mail>kessu@tripp.org</e-mail>
    <phoneNumber>+372555555</phoneNumber>
    <locations>
      <location type="home">
        <city>Saue</city>
        <address><![CDATA[Kooli 4-8, 78965]]></address>
        <comment><![CDATA[2. korrus. Kitsad koridorid.]]></comment>
        <items>
          <item id="98" type="laptopComputer" isActive="true" isUnderWarranty="true" warrantyInMonths="24" isInWorkingCondition="true">
            <manufacturer>Acer</manufacturer>
            <model>Aspire 5</model>
            <serialNumber>475YRT89K4</serialNumber>
            <dateOfPurchase>2018-03-25</dateOfPurchase>
            <operatingSystem version="1803">Windows 10 Home</operatingSystem>            
          </item>
          <item id="4" type="laptopComputer" isActive="false" isUnderWarranty="false" warrantyInMonths="36" isInWorkingCondition="true">
            <manufacturer>Lenovo</manufacturer>
            <model>IdeaPad L530</model>
            <serialNumber>U89TRY765ME</serialNumber>
            <dateOfPurchase>2007-08-14</dateOfPurchase>
            <operatingSystem version="">Windows XP</operatingSystem>
            <tickets>
              <ticket id="SWI069" issuedIn="2010-01-28" isHandled="true">
                <description><![CDATA[Blue screen.]]></description>
                <solution><![CDATA[Video driver update.]]></solution>
              </ticket>
            </tickets>
          </item>
        </items>
      </location>
    </locations>
  </customer>
  <customer id="26" type="company" isActive="true" hasManyLocations="true">
    <customerName>Aktsiaselts OÜ</customerName>
    <contactPerson>Gunnar Kuningas</contactPerson>
    <e-mail>gunnar@kunn.ee</e-mail>
    <phoneNumber>+3726985236</phoneNumber>
    <locations>
      <location type="office">
        <city>Loksa</city>
        <address><![CDATA[Kalda 14, 78965]]></address>
        <comment><![CDATA[Sissepääs Rimi poolt, helesinine uks]]></comment>
        <items>
          <item id="26" type="laptopComputer" isActive="true" isUnderWarranty="false" warrantyInMonths="24" isInWorkingCondition="true">
            <manufacturer>Apple</manufacturer>
            <model>Macbook Air</model>
            <serialNumber>258JUY369</serialNumber>
            <dateOfPurchase>2017-03-01</dateOfPurchase>
            <operatingSystem version="High Sierra">OS X</operatingSystem>
          </item>
          <item id="59" type="laptopComputer" isActive="true" isUnderWarranty="false" warrantyInMonths="24" isInWorkingCondition="false">
            <manufacturer>Acer</manufacturer>
            <model>Chromebook 11</model>
            <serialNumber>U89TRY765ME</serialNumber>
            <dateOfPurchase>2016-04-23</dateOfPurchase>
            <operatingSystem version="">Chrome OS</operatingSystem>
            <tickets>
              <ticket id="HWI046" issuedIn="2010-02-28" isHandled="false">
                <description><![CDATA[Stripes on screen]]></description>
                <solution><![CDATA[]]></solution>
              </ticket>
            </tickets>
          </item>
        </items>
      </location>
    </locations>
  </customer>
</customers>

Link klientrakendusele ja API-le

https://drive.google.com/file/d/1FU3XGGVHbn4BNNHAsOs7DQ4CzpEDwZUt/view?usp=sharing (nagu logidest näha lisasime selle lingi 08.06.2018, kuid siin on mõned üleliigsed kataloogid)
https://drive.google.com/file/d/1A7cxeK15xlWSww_CPxpksSfj0m10qFQV/view?usp=sharing (09.06.2019 laadisime üles paki, kus on kustutatud kaks ebavajalikku kataloogi, mis võivad tekitada segadust)

[ failid eemaldatud seoses ärilise konfidentsiaalsuse nõudega 19.06.2018; failide eemaldamine kooskõlastatud õppejõuga ]

Palun loe seda "readme" asemel

1. meie rakenduse implementeeritud skoop lähtub siin lehel (ülal), analüüsi osas defineeritud "Minimum viable product" kriteeriumitest
2. lase API käima, tee andmebaaside migratsioonid ja update-database
3. implementeeritud on kõik teenused, mis on nähtaval Swaggeris aadressil: "localhost:[pordinumber]/Swagger"
4. kliendirakendus asub kataloogis 'WatchWindersClient' ja tegemist on solutioniga, mille võiks käivitada teises Visual Studio aknas
5. klientrakenduse käivitumiseks tuleks muuta App.config failis muuta ära localhost portide numbrid asendades need enda arvutis oleva localhost pordiga ning lisada kehtiv JWT AccessToken, mille saab Postmaniga aadressilt api/Security/getToken "Email" ja "Password" abil pärast seda kui kasutaja on loodud
6. küsimuste ja raskuste korral kirjuta tiit.kuuskmae@gmail.com (API) ja toomas.juhkov@gmail.com (klient). Vastame esimesel võimalusel.

Retsensioonid

1. Veebiteenuse retsensioon grupile "Kuldne Loojang". Kättesaadav: https://drive.google.com/file/d/1OtzOCPiWixsrbHcGCcdHgXIgz3_fsC7h/view

Kasutatud kirjandus ja allikad

1. Albahari, Joseph, Albari, Ben 2017. C# 7.0 in A Nutshell. Sebastopol: O’Reilly
2. MariaDB Foundation. MariaDB. Kättesaadav:https://mariadb.org/
3. Martin, Robert 2018. Clean Architecture. Boston: Pearson Education