UCanDo

From ICO wiki

Meeskond

  • Mart Raus
  • Ando Kiidron
  • Liina Saar

Idee

Tööriist suurtele ettevõtetele, kellel on kontaktikeskused ja/või esindused. Klienditeenindaja saab rakenduse kaudu sisestada klientide pöördumisi ning tootejuhid pöördumiste sisu analüüsida.

Tehnoloogia

Projekt koostatakse aine raames omandatavate tehnoloogiate põhjal. Selleks kasutame:
REST veebiteenus

  • .NET Core 2.0

Veebiteenusega suhtlev kliendirakendus

  • WPF

Analüüs ja rakenduse sisu

1. Mis on teenuse eesmärk?
Selliste ettevõtete tootejuhtidel on tihti eesmärgiks kliendipöördumisi vähendada, et arendada tooted ja juhendid selliseks, et klientidel tekib võimalikult vähe küsimusi ja et nad suudaks ennast ise aidata. Kliendipöördumiste suur hulk tähendab ettevõttele vastavalt kas suurt tööjõukulu või halvendab kliendikogemust pika ooteaja tõttu.

2. Kasutajad
Teenindaja - saab sisestada kliendipöördumise.
Tootejuht - pöördumiste otsing/analüüs.
Admin - kasutajate haldamine.

3. Nimekiri funktsionaalsustest, mida plaanitakse kindlasti teostada (Must have):
Funktsionaalsed nõuded
*Teenindaja saab kliendipöördumise sisestada.
*Tootejuht saab otsida kindlas ajavahemikus toimunud pöördumisi.
*Tootejuht näeb, kui palju antud perioodil oli pöördumisi vastavalt otsingule.
*Tootejuht saab pöördumiste sisu lahtritest otsida tulemusi ühe või mitme märksõna järgi (OR), mida eraldatakse koma(de)ga.
*Tootejuht saab mitme märksõna otsingu salvestada ja panna sellele päringule nime.
*Tootejuht saab salvestatud päringu nimele vajutades lihtsasti otsingu uuesti teostada.
*Teenindaja ja tootejuht saavad sisse logida ja näha ainult neile relevantseid funktsionaalsusi. Teenindajale jääb vaid pöördumiste sisestamise õigus. Tootejuhil on õigus pöördumisi sisestada ja neid otsida.
*Tootejuht saab teenindajaid ja andmebaasi lisada, muuta ja kustutada.
*Admin saab tootejuhte ja teenindajaid andmebaasi lisada, muuta ja kustutada.

Mittefunktsionaalsed nõuded
*Teenuse kasutajaid peab tuvastama enne rakenduse funktsionaalsuse kasutamist ning kasutajaid peab saama hallata.

4. Nimekiri funktsionaalsustest, mis võiks olla, aga mida ei pruugita ajapuuduse tõttu realiseerida (Nice to have):
*Tootejuht näeb märksõna otsingule näha võrdlust eelnevate perioodidega.
*Tootejuht näeb dashboardil automaatselt arvutatud tabelit kuude lõikes kõige enam kasutatud sõnadest.
*Tootejuht näeb dashboardil automaatselt arvutatud tabelit eelmise kuuga võrreldes kasutatavuselt kõige enam tõusnud märksõnadest
*Tootejuht näeb dashboardil automaatselt arvutatud tabelit eelmise kuuga võrreldes kasutatavuselt kõige enam langenud märksõnadest
*Tootejuht saab lisada sõnu blacklisti nähes eelmistes tabelites ainult teda huvitavaid märksõnu.
*Tootejuht saab märksõna otsides seada filtreid (ainult kindla klienditüübi, algtunnuse, valdkonna vms järgi otsing)
*Võimalus otsida, millist tüüpi kliendid kõige enam pöörduvad ja kuidas see ajas muutub (vajalik tekitada kliendi andmebaas ja pöördumisel sisestada kliendi tunnus)

Andmebaasi skeem

UPCSA-2018-03-28 09 19.png

Workload

24.03.2018 - Idee paikapanemine ja Wiki lehe loomine. Esimesed läbirääkimised analüüsi kirjutamiseks.

26.03.2018 - Analüüsi kirjutamine.

27.03.2018 - Andmebaasi mudeli koostamine, analüüsi kirjutamine jätkub.

31.03.2018 - Analüüsi lõpetamine.

01.04.2018 - 08.06.2018 - Koodi kirjutamine.

XML

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

<referrals>

  <referral id="1" customerType="Era" baseIdentifier="Info" field="Arveldus">
    <date>2017-06-11</date>
    <content><![CDATA[Klient ei saa arvest aru. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aliquam turpis leo, tincidunt a sodales at, bibendum molestie diam. Mauris eget ipsum tempor, hendrerit massa interdum, commodo nisl.]]></content>
    <creator id="3" activeEmployee="true">
      <firstname>Peeter</firstname>
      <lastname>Pakiraam</lastname>
      <department id="2" channel="Esindus">
        <name>Järve esindus</name>
      </department>
    </creator>
  </referral>
  
  <referral id="2" customerType="Äri" baseIdentifier="Rike" field="Tehnoloogia">
    <date>2017-06-11</date>
    <content><![CDATA[Andmeside ei toimi. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aliquam turpis leo, tincidunt a sodales at, bibendum molestie diam.]]></content>
    <creator id="2" activeEmployee="false">
      <firstname>Ants</firstname>
      <lastname>Pakiraam</lastname>
      <department id="1" channel="Telefon">
        <name>Tartu kontaktikeskus</name>
      </department>
    </creator>
  </referral>

  <referral id="3" customerType="Era" baseIdentifier="Rike" field="Tehnoloogia">
    <date>2017-06-15</date>
    <content><![CDATA[Klient kurdab, et kõne on katkendlik. Lorem ipsum dolor sit amet, consectetur adipiscing elit.]]></content>
    <creator id="3" activeEmployee="true">
      <firstname>Peeter</firstname>
      <lastname>Pakiraam</lastname>
      <department id="2" channel="Esindus">
        <name>Järve esindus</name>
      </department>
    </creator>
  </referral>

  <referral id="4" customerType="Äri" baseIdentifier="Info" field="Müük">
    <date>2017-06-16</date>
    <content><![CDATA[Klient ei saa pakkumisest aru.]]></content>
    <creator id="2" activeEmployee="false">
      <firstname>Ants</firstname>
      <lastname>Pakiraam</lastname>
      <department id="1" channel="Telefon">
        <name>Tartu kontaktikeskus</name>
      </department>
    </creator>
  </referral>
  
</referrals>

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="referrals">
    <xs:complexType>
      <xs:sequence>
        <xs:element minOccurs="1" maxOccurs="unbounded" name="referral">
          <xs:complexType>
            <xs:sequence>
              <xs:element name="date" type="xs:date" />
              <xs:element name="content" type="xs:string" />
              <xs:element name="creator">
                <xs:complexType>
                  <xs:sequence>
                    <xs:element name="firstname" type="xs:string" />
                    <xs:element name="lastname" type="xs:string" />
                    <xs:element name="department">
                      <xs:complexType>
                        <xs:sequence>
                          <xs:element name="name" type="xs:string" />
                        </xs:sequence>
                        <xs:attribute name="id" type="xs:int" use="required" />
                        <xs:attribute name="channel" type="xs:string" use="required" />
                      </xs:complexType>
                    </xs:element>
                  </xs:sequence>
                  <xs:attribute name="id" type="xs:int" use="required" />
                  <xs:attribute name="activeEmployee" type="xs:boolean" use="required" />
                </xs:complexType>
              </xs:element>
            </xs:sequence>
            <xs:attribute name="id" type="xs:int" use="required" />
            <xs:attribute name="customerType" type="xs:string" use="required" />
            <xs:attribute name="baseIdentifier" type="xs:string" use="required" />
            <xs:attribute name="field" 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="/referrals">
    <html>
      <head>
        <h2>Pöördumised</h2>
      </head>
      <body>
        <xsl:for-each select="referral">
          <div style="border-style:solid;margin:10px;padding:10px;width:66%">
            <xsl:choose>
              <xsl:when test="creator/department/@channel = 'Esindus'">
                <div style="background-color:blue;width:100%;height:10px">
                </div>
              </xsl:when>
              <xsl:otherwise>
              </xsl:otherwise>
            </xsl:choose>
            <h3>
              <xsl:value-of select="date"/>
              <xsl:text> | </xsl:text>
              <xsl:value-of select="@customerType"/>
              <xsl:text> | </xsl:text>
              <xsl:value-of select="@baseIdentifier"/>
              <xsl:text> | </xsl:text>
              <xsl:value-of select="@field"/>
            </h3>
            <ul>
              <li>
                <xsl:value-of select="creator/firstname"/>
                <xsl:text> </xsl:text>
                <xsl:value-of select="creator/lastname"/>
                <xsl:if test="creator/@activeEmployee = 'false'">
                  <span style="color:red"> Töötaja pole aktiivne!</span>
                </xsl:if>
              </li>
              <li>
                <xsl:value-of select="creator/department/name"/>
              </li>
            </ul>
            <xsl:text>Sisu: </xsl:text>
            <xsl:value-of select="content"/>
          </div>
        </xsl:for-each>
      </body>
    </html>
  </xsl:template>
</xsl:stylesheet>

XSLT > 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="/referrals">
    <poordumised>
      <xsl:for-each select ="referral">
        <poordumine>
          <sisu>
            <xsl:value-of select="content"/>
          </sisu>

          <tootaja>
            <xsl:choose>
              <xsl:when test="creator/@activeEmployee = 'false'">
                <eesnimi>
                  <xsl:value-of select="creator/firstname"/>
                </eesnimi>
                <perenimi>
                  <xsl:value-of select="creator/lastname"/>
                </perenimi>
              </xsl:when>
              <xsl:otherwise>
                <eesnimi>
                  <xsl:text>salastatud</xsl:text>
                </eesnimi>
                <perenimi>
                  <xsl:text>salastatud</xsl:text>
                </perenimi>
              </xsl:otherwise>
            </xsl:choose>

          </tootaja>
        </poordumine>
      </xsl:for-each>
    </poordumised>
  </xsl:template>
</xsl:stylesheet>

Veebiteenuse retsensioon > GoFood

Teenuse kood on struktureeritud ehk jagatud kihtideks ning vastab projekti nõuetele. Selgesti on eristatavad DAL (Data Access Layer) kihid, BL (Business Layer) kihid, sh DTO (Data Transfer Object), Factories ja Services, ning eraldi appi poole peal ka API controllerid, mis suhtlevad Service kihiga. Teenuses on kasutatud ka Unit of Worki.

Samuti on seadistatud swagger, kuid meetodid ja veateated selles on kommenteerimata.

Kogu teenus on loodud ASP.NET MVC Web API tehnoloogiat kasutades. Loodud veebiteenus osutab teenuse pakkumist ning sisaldab mitme kasutaja registreerimise võimalust. Andmebaasis on 12 olemit, mis on piisav hulk, et täita projekti mahu nõudeid. Stringidel on pikkuste limiteerimiseks kasutatud vastavaid annotatsioone [MaxLength()]. Lisaks: Pole kasutatud [Required] atribuut nõuet ühegi objekti elemendi peal. Samuti pole kasutatud [Key] nõuet. UserIDHolder ei vasta objekti elemendi nime heale tavale, peaks olema UserIdHolder. Andmebaasi mudeli olemid on üles ehitatud loogiliselt, foreign key kasutamine on korrektne.

Teenuses on lisatud autoriseerimine nii, et kasutaja näeb ainult enda andmeid ning ei näe kasutaja andmeid ilma sisse logimata. Andmete kuvamine teenuses toimub vastavalt Controllerites seadistatule (näiteks AllowAnonymous annotatsioon, mis lubab ka sisselogimata kasutajal andmetele ligi pääseda). Teistel juhtudel on eelnevalt vajalik kasutaja autoriseerimine (ja ka andmete olemasolu). ApiControlleri meetodid on kenasti kommenteeritud sealhulgas meetodi kirjeldus ja milliseid HTTP response’e teenus väljastab.

Üldiselt järgib teenuse ülesehitus aine raames õpetatud häid tavasid ning funktsioneerib teenuse pakkujana nagu oli ka nõutud.

Klientrakenduse retsensioon > GoFood

Koodi loomisel on kasutatud MVVM mudelit, seega on klientrakenduse kood loogiliselt loetav. Klientrakendusse on loodud BaseService, milles on ära toodud API teenusega suhtlemise loogika, mida omakorda kasutab ära Service kiht. See kiht on omakorda struktureeritud (kaustadesse jagatud), mis teeb koodilugemise lihtsamaks. Kõik Service klassid peale Security kasutavad ära BaseService klassi, milles on ära toodud neli põhiliselt meetodit: andmete küsimine, lisamine, muutmine ja kustutamine. Just neid meetodeid kasutavad need erinevad Service klassid ära. Serviceid kasutavad ka ViewModeleid, mis vahendavad andmeid vaate ja mudeli vahel. Tehnilise poole pealt on klientrakendus viisakalt ja loetavalt üles ehitatud. Kasutaja registreerimine töötab nagu ka rakendusse sisse logimine.

Veateated ei ole handlitud - rakendus ei tee midagi, kui lahtrid on täitmata logimise ekraanil. Registreerimise ekraanil täitmata lahtritega edasi minnes rakendus crashib. Väljalogimine töötab ootuspäraselt.

Nutritionplans paneelil näidatakse järgmisi valikuid: Delete Nutritionplan, Add Nutritionplan, Add Daily Nutritionplan. Nutritionplan lisamisel ei tule mingit teavitust, kas midagi juhtus. Siiski, kui uuesti sisse logida rakendusse, siis kuvab lisatud Nutritionplani. Saab lisada ka mitmeid Nutritionplane. Nutritionplan kustutamisel, kui midagi ei ole valitud, siis rakendus jookseb kokku. Kui Nutritionplan on valitud nimekirjast, siis see kustutatakse. Add Daily Nutritionplan - seda saab lisada, kuvatakse “Daily NutritionPlan” väärtusi: nädalapäevad ja juba lisatud NutritionPlan’e. Lisan nupu vajutamisel ei tule teavitust, mis on juhtunud ja kas teenusesse see salvestati. Account Management paneelil vajutades kuvatakse avakuva, kus on võimalus välja logida. Shoppinglists paneelil vajutades crashib rakendus ja GetAllShoppingLists meetod annab: System.NullReferenceException: 'Object reference not set to an instance of an object.' Isikliku kasutajaga sisse logides ei näe teiste kasutajate andmeid.

Üldine rakenduse kasutajaliidese ülesehitus on mõistlik, natuke on vaeva nähtud ka kujunduselementidega, sealhulgas on lisatud logo. Väga viimistletud kasutajaliides ei ole, aga seda ei olnud ka nõutud.

XMLi retsensioon > Miisiiks

XML fail andmete edastamiseks

Mis on hästi: Vastab XML standardile. Proovitud online valideerimise teenuses https://www.xmlvalidation.com/. Üldine XML-i puu on üles ehitatud loogiliselt. Ülesande tingimustes oli, et XML-il peab olema vähemalt 4 loogilist dimensiooni. See tingimus on täidetud ja kasutatakse isegi 5 loogilist dimensiooni: ApplicationUsers, ApplicationUserDTO, Advertisements, AdvertisementDTO, AdvertisementPictureDTO. String väärtused, kus võib olla keelatud tähemärke on CDATA-ga ümbritsetud

Mida saab paremini: Üheks oluliseks nõudeks oli, et kolmel dimensioonil tuleb kasutada atribuute, mis on enamat, kui lihtsalt ID. See nõue ei ole aga täidetud ja ühelgi dimensioonil ei ole rohkem atribuute, kui ID. Näiteks JobCategory võiks olla atribuudina. Tagide vahel enne ja pärast väärtust on kasutatud tühikut (nt “<ContactTypeName> Skype </ContactTypeName>“). See loetakse XML-is väärtuse osaks, mis tõenäoliselt ei ole soovitud (allikas: http://usingxml.com/Basics/XmlSpace). Samuti oleks soovitatav eemaldada üleliigsed reavahetused.

XML faili skeemifail

Mis on hästi: XML skeemifail vastab on valiidne ja vastab XML failile (proovitud Visual Studios).

Mida saab paremini: CreationDate on märgitud string andmetüübiks, peaks olema date.

XSL transformatsioonifail XML failis olevate andmete transformeerimiseks HTML formaati

Mis on hästi: XSL transformeerib XML-ist HTML-i vigadeta (proovitud: https://www.w3schools.com/xml/tryxslt.asp?xmlfile=cdcatalog&xsltfile=cdcatalog) HTML on väga hästi loetav Transformatsioonis on kasutatud ka choose clauslit.

XSL fail XML faili formaadi muutmiseks

Mis on hästi: XSL fail on valiidne (proovitud: http://www.utilities-online.info/xsltransformation/#.WyAIhYozY2x) XML fail on transformeeritud - muudetud tag’ide nimetusi

Mida saab paremini: Uus XML fail võiks kasutada kasutaja sisestatud stringide puhul ja linkide puhul CDATA-t Kasutatud ei ole keerulisemat klauslit kui foreach


Kokkuvõte: kõik ülesande põhilised osad on tehtud ja enamik nõuetest täidetud hästi. SML on korrektne ja loogiline ning transformeeritud HTML väga hästi loetav. Suuremad puudused on, et atribuute on ei ole kasutatud peale ID-de ja väärtustes on kasutatud tühikuid. Hindame, et ülesanne on täidetud 80% ulatuses.