Emaganda 2.0 IN3405 Bachelorproject
Daniel Hulst Arian van Gend
IN3405 Bachelorproject - Emaganda 2.0
Voorwoord Dit is het eindverslag van het bachelorproject IN3405, uitgevoerd door Daniel Hulst en Arian van Gend. Het project is het afrondingsvak van de Bachelor Technische Informatica aan de universiteit Delft. Het project is uitgevoerd voor NICE International in samenwerking met ICT4Schools. Het betrof het complete herontwerp en de implementatie van een vervangende versie van Emaganda, hun eigen internetcafésoftware, voor gebruik in ontwikkelingslanden. Op het moment van schrijven beperkt dit zich tot verscheidene cafés in Gambia. Wij zijn veel dank verschuldigd aan de volgende mensen en instanties: NICE International, in het bijzonder Jan Willem Langeraar, Lonneke Craemers en Ties Kroezen, voor het opstellen en begeleiden van de opdracht en voor de reis naar Gambia en het testen van tussentijdse resultaten. ICT4Schools, in het bijzonder Ruud Bredewold en Michiel Eghuizen, voor het bieden van technische ondersteuning en het uitvoeren van testen. Matthijs Sepers voor het begeleiden van het hele project.
ii
IN3405 Bachelorproject - Emaganda 2.0
Samenvatting De opdracht voor dit bachelorproject betrof een herontwerp en implementatie van de internetcafésoftware, Emaganda, zoals gebruikt door NICE International in Gambia. Deze software beheert de tijd die klanten op het internet doorbrengen en welke producten ze kopen. Daarnaast biedt het systeem een onderwijsmodus en het bijbehorende beheer, waarbij leerlingen het internet en bepaalde programma's mogen gebruiken tijdens de les. Tot slot biedt het systeem ook de optie om bepaalde producten te verkopen, zoals koekjes en water, maar ook bioscoopkaartjes. Allereerst werd het bestaande systeem geanalyseerd, inclusief de tekortkomingen. Op grond van gesprekken met werknemers van NICE in het hoofdkantoor te Utrecht en ter plekke in Gambia en daarnaast, voor de technische kant met ICT4Schools te Zwolle, werd een herontwerp gemaakt, zoals te vinden in appendices B en C. Zoals daar te lezen is betrof dit een compleet vernieuwde applicatie met sterk uitgebreide en verbeterde functionaliteit. Uit de gesprekken, door ons gemaakte voorbeelden en gebruikersanalyses kwamen duidelijke functionele en technische eisen naar voren. Deze elementen waren de basis voor het gehele ontwerp, wat daarna ook geïmplementeerd is.
iii
IN3405 Bachelorproject - Emaganda 2.0
Verklarende woordenlijst PHP Een veelgebruikte scripttaal die bijzonder geschikt is voor Webontwikkeling en die in HTML ingebed kan worden. MySQL Een open-source database LDAP Een netwerkprotocol dat beschrijft hoe gegevens uit een hiërarchisch geordende directoryservice wordt benaderd. CodeIgniter Een compacte PHP framework die de MVC-werkwijze gebruikt. jQuery Een compacte Javascript framework die onder andere AJAX-functionaliteit biedt en waar veel plugins voor beschikbaar zijn. cron Een Unix-daemon die het mogelijk maakt op gezette tijden een proces te starten. AJAX AJAX is geen losstaande techniek maar een toepassing van een Javascriptfunctie die het mogelijk maakt data op te halen van een server en die in een pagina kan plaatsen zonder de hele pagina opnieuw te verversen. MVC Model-View-Controller – een werkwijze waarbij de data wordt gescheiden van de verwerking en de presentatie ervan.
iv
IN3405 Bachelorproject - Emaganda 2.0
Inhoudsopgave Voorwoord..........................................................................................................................ii Samenvatting.....................................................................................................................iii Verklarende woordenlijst...................................................................................................iv 1 Introductie.........................................................................................................................1 1.1 NICE.........................................................................................................................1 1.2 ICT4Schools..............................................................................................................1 1.3 Emaganda..................................................................................................................1 1.4 Opbouw document....................................................................................................2 2 Ontwerp.............................................................................................................................3 2.1 Vooronderzoek..........................................................................................................3 2.2 Functioneel Ontwerp.................................................................................................3 2.2.1 Het ontstaan.......................................................................................................3 2.2.2 Procesontwerp...................................................................................................3 2.2.3 Functionele en niet-functionele eisen................................................................3 2.2.4 Knelpunten........................................................................................................4 2.3 Globaal Technisch Ontwerp......................................................................................4 2.3.1 Gebruikte technologieën....................................................................................4 2.3.2 Datamodel..........................................................................................................5 2.3.3 MySQL Database .............................................................................................5 2.3.4 Veranderingen aan de LDAP database..............................................................7 2.3.5 Toelichting bij het MySQL-diagram.................................................................9 2.3.6 Klassendiagrammen..........................................................................................9 2.3.7 Models...............................................................................................................9 2.3.8 Controllers.........................................................................................................9 Figuur 3: Controllerstructuur.......................................................................................11 2.4 Views.......................................................................................................................11 2.5 Interface Ontwerp....................................................................................................13 3 Gedetailleerd Procesontwerp en Implementatie.............................................................14 3.1.1 Inloggen...........................................................................................................14 3.1.2 Algemene gebruikerslogin...............................................................................15 3.1.3 Staflogin..........................................................................................................15 3.1.4 Paginaopbouw.................................................................................................16 3.1.5 Browsen...........................................................................................................18 3.1.6 Het menuview..................................................................................................19 3.1.7 Login-/Navigatieoverzicht...............................................................................20 3.1.8 Transacties.......................................................................................................21 3.1.9 Bonussysteem..................................................................................................23 3.1.10 Gebruikersbeheer...........................................................................................24 3.1.11 Usertypes.......................................................................................................25 3.1.12 Systeemconfiguratie......................................................................................26 v
IN3405 Bachelorproject - Emaganda 2.0 3.1.13 Change-administratie.....................................................................................27 3.1.14 Demographics................................................................................................27 3.1.15 Het Salessysteem ..........................................................................................28 3.1.16 Orders............................................................................................................30 3.1.17 Deliveries.......................................................................................................31 3.1.18 Shiftsysteem..................................................................................................32 3.1.19 Educatiesysteem............................................................................................33 3.1.20 Deficiencies...................................................................................................35 3.1.21 Systeem Performance....................................................................................36 3.1.22 Timer..............................................................................................................37 3.1.23 Internetprijslijst..............................................................................................38 3.2 Testen en evaluatie..................................................................................................40 4 Aanbevelingen.................................................................................................................41 Referenties........................................................................................................................43 Appendices........................................................................................................................44
vi
IN3405 Bachelorproject - Emaganda 2.0
1
Introductie
1.1
NICE
De omschrijving van NICE International is als volgt (afkomstig van de website): 'NICE International makes utility services accessible for people at the lower end of the economic ladder. NICE is building a franchise network of solar powered ICT-based service centres in developing countries. Using the infrastructure of computers with Internet-access and a cinema with satellite TV, NICE adds value by offering services aimed at improving and developing the lives of customers living at the Base of the Pyramid. The NICE multi-services concept is developed with the support of the Energy4All Foundation. Energy4All is embraced by Essent (the largest utility of the Netherlands), Econcern and a network of people from society at large.'
1.2 ICT4Schools NICE maakt gebruik van de ICT-diensten die ICT4Schools biedt. ICT4Schools 'is een bedrijf dat zich richt op scholen en andere onderwijsinstellingen in Nederland en in het buitenland. Het ontwikkelen en implementeren van webgebaseerde applicaties zien wij als onze kerntaak. Kwaliteit, veiligheid en een goede ondersteuning zijn daarbij uitgangspunten.' ICT4Schools handelt de hard- en softwarekant van de internetcafés af. Zij hebben de oorspronkelijke Emagandasoftware ontworpen, geïmplementeerd en geïnstalleerd.
1.3 Emaganda Emaganda is een softwarepakket dat controle en administratieve functies biedt, op maat gesneden voor de NICE internetcafés. Het bestaat uit een Java-serverapplicatie die caféklanten toegang verschaft tot de juiste accounts en hun internettijd beheert. Wanneer een klant door zijn internettijd heen is zal deze applicatie de klant van het systeem uitloggen. Wij zullen deze applicatie voortaan de JavaTimer noemen. Deze applicatie bestaat reeds en werkt goed. Wij hebben ons hier niet mee beziggehouden, maar wel de ontwikkelaar ervan (werkende bij ICT4Schools) gewezen op nodige veranderingen. Deze worden waar van belang besproken in de lopende tekst. Daarnaast is er een webapplicatie die het volgende kan:
1
•
financiële overzichten creëren;
•
gebruikersbeheer;
•
krediet- en tijdinformatie bieden aan klanten;
IN3405 Bachelorproject - Emaganda 2.0 •
stafroosters genereren;
•
educatieve roosters genereren;
•
balieverkoop mogelijk maken;
•
diagnostische overzichten genereren.
De balieverkoop omvat naast fysieke producten als koekjes, water en pen-drives ook de verkoop van bioscoopkaarten (voetbalwedstrijden, educatieve documentaires en films) en cursussen.
1.4 Opbouw document Dit document is als volgt opgebouwd. In hoofdstuk 2 wordt het globale ontwerp besproken. Dat omvat het vooronderzoek, het functioneel ontwerp en het technisch ontwerp. Hoofdstuk 3 bespreekt in detail de interessantere code en oplossingen die zijn toegepast in het project. Hoofdstuk 4 tot slot bespreekt aanbevelingen die de opdrachtgever kan gebruiken om het project voort te zetten.
2
IN3405 Bachelorproject - Emaganda 2.0
2 Ontwerp In dit hoofdstuk wordt het ontwerp besproken (vooronderzoek, functioneel ontwerp en technisch ontwerp), alsmede de globale opbouw van de nieuwe software.
2.1 Vooronderzoek Voordat een functioneel ontwerp opgesteld kon worden, moest er onder andere vooronderzoek worden gedaan naar de door ICT4Schools gebruikte technologieën. Dit was nodig om te bepalen of deze geschikt zijn voor de nieuwe implementatie en om te bepalen welke delen hergebruikt kunnen worden. De uiteindelijke keuzes die we op grond hiervan hebben gemaakt zijn te lezen in het Technisch Document.
2.2 Functioneel Ontwerp 2.2.1
Het ontstaan
Het vooronderzoek werd gevolgd door uitgebreid overleg met de opdrachtgever in Utrecht en met ICT4Schools. Hierbij werd vastgesteld wat de functionele eisen zijn en mogelijke toekomstplannen met het oog op uitbreidingen van het systeem. Aangezien de software echter het meest gebruikt gaat worden door de werknemers van NICE in Gambia, bleek het ook al snel noodzakelijk om ook in Gambia interviews te houden om de precieze functionele eisen vast te stellen. Uiteindelijk ontstond zo de definitieve versie van het Functioneel Ontwerp zoals hieronder verder toegelicht. 2.2.2
Procesontwerp
Omdat de nieuwe Emagandasoftware door veel mensen gebruikt zal worden die allemaal een andere toegang tot het systeem vereisen en ook allemaal andere data gebruiken, werd eerst gespecificeerd welke data er in Emaganda aanwezig is en wie waar toegang toe heeft. Na veel overleg en evenveel aanpassingen hieraan is besloten de precieze toegang aanpasbaar te maken per gebruikerstype en het aanmaken van nieuwe gebruikerstypes mogelijk te maken, maar de lijst geeft een uitgangspunt. Zie ook het stuk over knelpunten hieronder voor meer informatie. 2.2.3
Functionele en niet-functionele eisen
Het uiteindelijke functionele ontwerp omvat de volgende functionaliteit:
3
•
Een kassa waarmee de NICE producten verkocht worden;
•
Userbeheer voor klanten en stafleden, waarmee ook werkuren, demografische statistieken en accountactiviteit bijgehouden wordt;
IN3405 Bachelorproject - Emaganda 2.0 •
Productbeheer, waaronder ook de prijzen en voorraden en leveringen geregeld worden;
•
Educatiebeheer dat de te geven cursussen en hun prijzen bevat, alsmede een rooster en studentenlijsten;
•
Verkoopoverzichten en kassadeficienties bijhouden;
•
Systeemprestaties en configuratie overzichten genereren.
2.2.4 Knelpunten Een belangrijk knelpunt tijdens het ontwerpproces waren de verschillende prioriteiten en wensen die de opdrachtgevende partijen hadden. Zo was het voor het in Nederland gevestigde NICE International belangrijk dat de cafésoftware de mogelijkheid bood om de Gambiaanse markt zo goed mogelijk af te romen. Zij vonden de mogelijkheid om verscheidene bonussen aan te kunnen bieden en de realisatie van een variabele internet prijs dan ook noodzakelijk. In de gesprekken die wij eerder met het bestuur van de Gambiaanse tak van NICE hadden, bleek echter dat het lokale bestuur vooral eenvoud wenste daar juist zaken als bonussen en variabele prijzen tot verwarring bij gebruikers leidden. Vooral omdat wij wanneer wij eenmaal terug uit Gambia waren, meer contact hadden met NICE International, hebben wij er voor gekozen om hun functionele wensen te implementeren ten koste van de eenvoud van het systeem. Hiernaast is een aantal weken voor de ingebruikname van het systeem, aan de hand van een gesprek met een vertegenwoordiger van NICE Gambia (die met de kerstdagen even in Nederland was), een aantal functies aan het systeem toegevoegd die men in Gambia graag gerealiseerd zag, het gaat hier onder andere om de mogelijk de snelheid van de internetverbinding tussen de Gambiaanse cafés en de buitenwereld te testen. Dit is iets dat de ontwikkeling van het gehele project toch wel vertraagd heeft. Een ander punt dat vertraging op heeft geleverd (en samenhangt met het bovenstaande) is dat ook NICE international tot ver in het implementatieproces nieuwe functionele wensen had. Zo hebben wij een week voor de ingebruikname nog een bonusfunctie geïmplementeerd.
2.3 Globaal Technisch Ontwerp 2.3.1
Gebruikte technologieën
Allereerst zullen we in dit hoofdstuk uitleggen waarom is gekozen voor de technologieën die nu toegepast zijn in de implementatie. Al snel werd besloten Emaganda in de browser te blijven draaien. De voordelen van serverbenadering en de flexibiliteit die websites bieden, gepaard met de eenvoud deze te bouwen en onderhouden gaven de doorslag inzake. Omdat de servers en cliënten al draaiden op Linux is al snel daarop besloten te kiezen voor de vertrouwde website opbouw met PHP en MySQL, aangeboden via Apache server, de zogenaamde LAMP-opstelling.
4
IN3405 Bachelorproject - Emaganda 2.0 Hierna werden de beslissingen echter aanzienlijk moeilijker, daar er voor PHP diverse frameworks te vinden zijn die het ontwikkelen ondersteunen en richting geven. We hebben na uitgebreid onderzoek naar de verschillende mogelijkheden gekozen voor CodeIgniter. De redenen hiervoor zijn simpel. Het biedt een gestructureerde manier van werken aan, en dwingt die zelfs af, met het MVC model. Dit houdt code overzichtelijk en onderhoudbaar. Daarnaast is het een niet te uitgebreid framework. Dit lijkt een grote beperking, maar in de praktijk zijn alle handige functies aanwezig en wordt het eenvoudig het overzicht te bewaren en de weg te kunnen vinden binnen het framework. Hierdoor is spoedig starten mogelijk. Daarnaast maakt het het onderhoud aanzienlijk eenvoudiger voor ICT4Schools, aangezien de leercurve klein is. Tijdens het ontwerp van de SalesWindow werd duidelijk dat een efficiënte interface voor alle receptionist-handelingen ondersteund zou kunnen worden door de flexibiliteit die een AJAX-aanpak biedt. Daarom is toen besloten een Javascript framework te zoeken om die flexibiliteit te kunnen bieden. Ook hier is op dezelfde criteria gelet als de PHP framework: eenvoud, overzicht en handige functionaliteit. Dit alles en meer werd gevonden in jQuery, dat niet alleen zeer nette code oplevert (gescheiden van de HTML), maar ook nog eens uitbreidbaar is indien extra functionaliteit gewenst is. Voor de communicatie van data met de server werd voor AJAX-verzoeken gekozen voor het JSON dataformaat, omdat dit direct door PHP en Javascript ondersteund wordt en omdat jQuery dit ingebouwd heeft. Tot slot werd duidelijk dat een aantal taken, zoals het bijhouden van verbindingskwaliteit en beschikbaarheid, alleen mogelijk was via een zogeheten batch-script dat periodiek draait via cron. Ook dit werd daarom toegepast bij de implementatie. 2.3.2
Datamodel
Voor dit project hebben wij gebruik gemaakt van een MySQL-database en een LDAPdatabase. Ook het oude Emagandasysteem maakte gebruik van deze twee soorten databases, de eerste database is geheel hergestructureerd, de tweede, de LDAP-database, slechts op enkele punten aangepast. Er is om verschillende redenen gekozen voor het gebruik van een LDAP-database naast de MySQL-databases. Ten eerste gebruikte het oude Emagandasysteem ook twee databases. De stukken code die de LDAP-database manipuleerden waren er dan ook al en konden wij zonder al te veel aanpassingen hergebruiken. Verder werd de LDAP-database ook door de Javatimer gebruikt, dus wanneer wij deze database zouden laten vallen moest dit programma op veel meer punten aangepast worden dan nu het geval was. Tot slot bied LDAP gewoon een zeer goede database (namelijk directory georiënteerde) structuur om gebruikersgegevens in op te slaan. 2.3.3
MySQL Database
De MySQL database die grafisch is weergegeven op pagina 8 bevat de volgende tabellen: Product: bevat alle producten die (mogelijkerwijs) in het café verkocht kunnen worden, plus het aantal dat van ieder product aanwezig is (het stock-veld). Het toevoegen van een extra tabel speciaal voor voorraadregistratie is op deze manier niet nodig en zou logisch een stuk ingewikkelder zijn. Hiernaast bevat deze tabel als eerste rij altijd de hoeveelheid geld die in de kas zit.
5
IN3405 Bachelorproject - Emaganda 2.0 Sale: registreert de verkoop van een product plus het aantal dat in één keer van het product verkocht is. Hiernaast bevat deze tabel nog velden waarin opgenomen is wie de verkoop heeft gedaan en wanneer de verkoop is gedaan. Registercheck: vat samen welke producten er op een bepaald tijdstip (bij het inloggen als shift leader) in de kas en op voorraad waren. RegistercheckItem: voor ieder product behorende tot een registercheck wordt een registercheck item aangemaakt. In deze tabel wordt per item opgeslagen tot welke registercheck het behoort, om welk product het gaat en wat het verschil was tussen het aantal hiervan in het systeem en op voorraad. Transaction: een transactie representeert de koop van credits door een klant. Wanneer een transactie geaccepteerd wordt, wordt het Accepted veld op true gezet en worden Amount credits aan het account van ReceivingUserId toegevoegd. InternetPrice: deze tabel bevat periodes (day, startTime, endTime) waarbinnen de kosten van het internet berekenend worden op basis van een bepaalde CreditMultiplier. Priority geeft de prioriteit van een periode aan, day de dag waarop de periode geldt (0=zon, 6=zat, 7=alle dagen). Verder kan de periode uitgezet worden door het enabled veld op false te zetten. Course: een course representeert een vak zoals gegeven in de cafés. Alle velden spreken voor zich, behalve het EndDate veld dat aangeeft tot wanneer een course uiterlijk aangeboden mag worden. CourseInstance: een ingeroosterde instantie van een course. CourseInstances hebben een IsActive veld dat op true wordt gezet wanneer een staflid de courseInstance activeert op het moment dat de les begint. CourseSubmission: dit is een aanmelding voor een les. Het combineert een gebruiker (UserId) en een courseInstance. Verder wordt hier bijgehouden hoeveel een bepaalde gebruiker reeds betaald heeft voor een les. Shift: een shift is een dienst. Het bevat het id van een staflid (UserId) en de tijd/datum gegevens met betrekking tot het beginnen en eindigen van de dienst. ShiftActivity: wanneer een staflid aan een dienst begint, activeert hij deze op de ShiftList pagina. Wanneer dit gebeurt wordt er een rij in de ShiftActivity tabel aangemaakt met daarin het id van de betreffende shift, en wanneer deze door het staflid geactiveerd (of gedeactiveerd) is. Order: een order is een bestelling als gedaan door een staflid van het café. Het bevat een product id en een gewenste hoeveelheid. Verder wordt er nog bijgehouden wie de bestelling deed en wanneer deze is gedaan. Delivery: in deze tabel wordt bijgehouden welke bestellingen (orders) er aangekomen zijn, 6
IN3405 Bachelorproject - Emaganda 2.0 wanneer deze aangekomen zijn, of er verschil is tussen bestelde hoeveelheid en aangekomen hoeveelheid en wie de bestelling heeft aangenomen (en geregisteerd). UserType: een gebruikerstype. GroupId is het id van de LDAP group die dit gebruikerstype representeert. Privelige: een privilege dat gebruikers van een bepaald type kunnen hebben. PrivilegeName is het veld dat gecontroleerd wordt door andere controllers wanneer een gebruiker een actie wil ondernemen waarvoor een bepaald privilege vereist is. PrivilegeList: deze tabel koppelt privileges aan usertypes. SystemProperty: deze tabel houdt een lijst bij van systeemeigenschappen die door stafleden (met de juiste privileges) veranderd kunnen worden. Type is een enumeratie bestaande uit de waardes email, bonus, defaults en misc. Bonus: deze tabel bevat rijen waarmee kredietbonussen berekend kunnen worden. Zie het hoofdstuk bonussen voor uitleg. Change: een change representeert een verandering in een tabel. Zie het kopje changes voor meer uitleg. SystemPerformanceEntry: hierin wordt de performance van het systeem op een bepaald moment bijgehouden. DownloadPerformanceEntry: hierin wordt de downloadsnelheid van het systeem op een bepaald moment bijgehouden. ApplicationListItem: iedere rij in deze tabel kan ofwel een programma dat door een gebruiker opgestart kan worden vanuit zijn application list pagina, of een les die door een gebruiker geactiveerd kan worden, of een map waarin programma's of lessen zich bevinden, voorstellen. Het typeId veld geeft aan om welk van de drie genoemde opties het gaat. Het parentId geeft aan in welke map het item hoort. Command bevat het commando dat uitgevoerd moet worden in het geval van een programma of een les.
2.3.4
Veranderingen aan de LDAP database
De voornaamste verandering is hoe de verschillende UserTypes die het systeem onderscheidt worden weergegeven. Dit hebben wij van een type (een ‘directory’) met een numeriek UserType veld veranderd in een aantal generieke types die een hardgecodeerde verbinding (in de vorm van een GroupId nummer) met de usertypes in de MySQL UserType tabel hebben. Om het grootste deel van deze Gebruikerstypen is nadrukkelijk gevraagd door medewerkers van NICE uit Gambia (al was dit al tijdens het implementatieproces). Een aantal ervan kan later door administrators zelf benoemd en ingevuld worden. Hiernaast is een aantal onnodig geworden velden verwijderd.
7
IN3405 Bachelorproject - Emaganda 2.0
Figuur 1: MySQL database structuur
8
IN3405 Bachelorproject - Emaganda 2.0 2.3.5
Toelichting bij het MySQL-diagram
Over de onderstaande grafische weergave van de database kan ter verduidelijking het volgende gezegd te worden. De stippellijnen geven tussen de tabellen geven een has-a verbinding aan in de richting van de pijl, bv. CourseInstance heeft een Course als lid. Dit wordt in de tabel zelf gerepresenteerd met het veld CourseId. De has-a verbinding met de tabel LDAPUser (de LDAP tabel waarin gebruikers bij gehouden worden) wordt in dit diagram enkel voorgesteld door het veld UserId, en niet meer door een stippellijn. Voor dit laatste is gekozen om een al te chaotisch diagram te voorkomen. Verder bevat het ParentId veld van ApplicationItem wederom een ApplicationItem, iets wat niet met een stippellijn aangegeven kon worden. 2.3.6
Klassendiagrammen
Daar we gebruik maken van CodeIgniter dat het MVC-patroon implementeert, is het voor het overzicht (en ook logisch gezien) voor de hand liggend om een klassendiagram in tweeën te splitsen in een diagram dat overzicht geeft van alle Models die de data structuur van het systeem modelleren en in een diagram dat een overzicht geeft van alle Controllers en andere klassen die programmalogica bevatten. Views worden niet uiteengezet in een klassendiagram aangezien zij geen klasse zijn, maar code die wordt gegenereerd door controllerfuncties. 2.3.7
Models
In principe is ieder model in dit diagram de PHP-versie van een MySQL tabel waarmee via PHP-functies gecommuniceerd kan worden. Er zijn echter wat uitzonderingen. Shift: via dit model wordt zowel gecommuniceerd met de shift-tabel als (inwendig) met de shiftactivity tabel. Er zijn geen functies om direct shiftactivities aan te maken of op te vragen daar dit allemaal binnen het model zelf gebeurt ten gevolge van het aanroepen van de activateShift en deactivateShift functie. LDAPUser: is een model dat geen MySQL-tabel modelleert, maar communiceert met de LDAP-database via de usersystem library. Deze bibliotheek is een zogenaamde façade voor het (even eens in PHP geschreven) bestaande LDAP-database systeem. Time: deze klasse is geen model maar een zogenaamde library, die vanwege het feit dat hij geen plaats inneemt binnen de Controllerlogica en net als een model door controllers geladen kan worden, een plek in dit diagram gekregen heeft. Deze library bevat functies die het werken met data en tijden makkelijker maken. 2.3.8
Controllers
De meeste controllers zijn afgeleid van de MY_CustomerPage en MY_StaffPage controllers en zijn dan ook pagina's voor klanten of voor stafleden. Een uitzondering is de main controller die alle logica rond het inloggen op zich neemt en het menu dat wel een controller is, maar niet als dusdanig geladen, hierover meer onder het kopje Browsen.
9
IN3405 Bachelorproject - Emaganda 2.0
Figuur 2: Hiërarchie van de models
10
IN3405 Bachelorproject - Emaganda 2.0
Figuur 3: Controllerstructuur
2.4 Views Het grafische gedeelte van de webapplicatie, d.w.z. de daadwerkelijk pagina’s worden door CodeIgniter gemodeleerd als views die door controllers aangeroepen kunnen worden. Aangezien vrijwel iedere controller een pagina representeert, roept ook iedere controller tenminste een view aan, deze ‘standaard’ view heeft de naam controllerNaam_View. Een aantal controllers kan echter nog een of twee alternatieve of speciale views aanroepen. In dit hoofdstuk behandelen we deze views. Ten eerste zijn er de Main- en Parent-controllers:
11
IN3405 Bachelorproject - Emaganda 2.0
Figuur 4: Main en Parent-controllers
login_view: it is het loginscherm dat aangeroepen wordt wanneer er geen NX-sessie aanwezig blijkt. rejectTransaction_view: dit view wordt aangeroepen wanneer men tijdens de inlogprocedure een transactie verwerpt. Het view toont een tekst die de gebruiker maant een staflid in te schakelen. header_view: iedere pagina die door de applicatie gegenereerd wordt begint met het het header view, hierin zijn de benodigde scripts en metadata voor een pagina opgenomen. noTimer_view: dit view biedt in zeer zeldzame gevallen de mogelijkheid om zonder dat de HTML-structuur in de war raakt als gebruiker ingelogd te zijn zonder dat er een grafische timer actief is. Verder zijn er nog de alternatieve views die door de normale pagina’s aangeroepen kunnen worden:
Figuur 5: Alternatieve views
12
IN3405 Bachelorproject - Emaganda 2.0 courseInstanceAll_View: geeft een overzicht van alle ingeplande courses. courseInstanceDetails_view: geeft een overzicht van de details van een bepaalde ingeplande course (bv tijd, aantal deelnemer enz.) changeListByUser_view: laat gemaakte veranderingen zien per gebruiker. allDeliveries_view/allOrders_view: laten een overzicht zien van respectievelijk alle deliveries en orders binnen het systeem. staffListNewAccount_view: het venster waarbinnen de details van een nieuw stafaccount opgegeven worden. staffFull_view/customerFull_view: toont de details van respectievelijk een stafaccount en een klantaccount. salesListTransactions_view/ salesListEducation_view/salesListTotals_view: tonen respectievelijk de inkomsten uit transacties, lesgelden en de totalen van alle drie vormen van inkomsten (het standaard view laat de verkoop aan balie-inkomsten zien).
2.5 Interface Ontwerp Deze paragraaf bespreekt enkele bijzonderheden die opkwamen bij het ontwerpen van de interface van de nieuwe Emaganda. Allereerst is een kleurschema gekozen dat past bij NICE als organisatie en tegelijkertijd goed contrast mogelijk maakt in de website. Daarnaast moest natuurlijk rekening worden gehouden met de culturele significantie van de gebruikte kleuren. De keuze viel uiteindelijk op de kleuren van NICE zelf, aangezien deze zowel esthetisch als cultureel voldeden. Ook sloot dit aan bij de marketingstrategie van NICE. De tekstkleur is zwart met wit als achtergrondkleur. Dit verzekert goed contrast voor de tekst. De volgende stap was het kiezen van een navigatiestructuur. Omdat er vrij veel pagina's tot de website behoren en zij een ondiepe structuur hebben (dieper dan 3 niveaus is ongebruikelijk) is gekozen voor een dropdown-menu. Hierbij zijn de categorieën zo gekozen dat ze goed aansluiten bij iemands werkwijze en taken. Zo vallen bijvoorbeeld alle verkoopgerelateerde schermen onder Sales, omdat die het meest door receptionisten zal worden gebruikt in de uitvoering van hun dagelijkse taken. Uiteraard is rekening gehouden met vaste plaatsing en maximum hoeveelheden opties per niveau. Een receptionist die meer rechten krijgt zal nog steeds dezelfde menuvolgorde gepresenteerd krijgen, alleen zullen die meer opties bevatten. Zo blijft de interface vertrouwd aanvoelen. Twee pagina's vallen, vanuit bruikbaarheidsperspectief gezien, op. Dit zijn het SalesWindow en de Timer. De SalesWindow gebruikt als een van de weinige pagina's AJAX om data naar de server te sturen. De redenen hiervoor zijn als volgt. Ten eerste voorkomt het gebruik van AJAX dat er te veel data over het netwerk moet worden gestuurd. De SalesWindow staat namelijk de hele dag open bij de receptie. Ten tweede zorgt het ervoor dat, in combinatie met de splitsing van directe verkopen en services, het mogelijk is om iemand in te schrijven voor een cursus, wat een relatief lang proces is, omdat er veel informatie moet worden ingevoerd, en ondertussen een flesje water te verkopen. De informatie blijft op deze manier bewaard en wordt pas 13
IN3405 Bachelorproject - Emaganda 2.0 weggehaald bij een succesvolle actie (waarna de data ook is opgeslagen). Tot slot maakt AJAX de overgangen tussen de verscheidene stappen soepeler en efficiënter, omdat er geen complete pagina van de server hoeft te worden gehaald. Een heikel punt bij Emaganda 1 is de Timerweergave geweest. Zoals het functioneel ontwerp ook noemt, was het vaak onduidelijk hoeveel tijd nog beschikbaar was, hoeveel geld dat omgerekend was en zelfs hoe snel de Timer afliep (hier zaten nog wel eens plotselinge overgangen in). Om dit probleem op te lossen is in Gambia uitgebreid overlegd met daadwerkelijke klanten. Een aantal ontwerpen zijn ze voorgelegd en met behulp van hun commentaar is een uiteindelijke opzet geformuleerd. Ten eerste wordt met verschillende kleuren aan de klant getoond hoeveel tijd ze kunnen internetten op hun krediet op ieder gegeven moment van de dag. Dit stelt ze in staat zelf controle uit te oefenen op hoeveel waar ze voor hun geld krijgen. Tegelijkertijd werkt dit als een subtiele reclame voor de daluren, zodat ook op rustige momenten klanten worden gemotiveerd om langs te komen. Het is immers in hun eigen voordeel. Daarnaast kan een klant in één oogopslag zien hoeveel tijd en krediet hij nog heeft. Dit wordt iedere halve minuut bijgewerkt en getoond in tekst. Ook wordt het aangegeven met een omlijsting in een van drie kleuren: groen, oranje en rood. Dit geeft ook informatie over de overgebleven hoeveelheid tijd. Groen betekent grofweg: 'genoeg tijd over'. Oranje en rood respectievelijk; 'de tijd begint op te raken, maar je kan nog eventjes vooruit' en 'de tijd is bijna op, sla je werk rustig op en schaf eventueel nieuw krediet aan om verder te gaan.' Bij de overgang van een kleur naar een andere wordt een pop-up getoond om de klant op de hoogte te stellen van die gebeurtenis. Zo worden klanten niet verrast door een plotselinge afloop van hun sessie. Tot slot nog een opmerking over het aankopen van krediet terwijl een klant is ingelogd. In het oude systeem ging het oude krediet eerst op en werd de sessie beëindigd voordat nieuwe krediet inging. In het nieuwe systeem kan een nieuwe transactie worden aangemaakt om krediet toe te voegen. Deze moet door de klant worden geaccepteerd alvorens het krediet wordt toegevoegd aan zijn account. Om te voorkomen dat een klant deze stap vergeet (en dan alsnog tegen een beëindigde sessie aanloopt), wordt iedere 2 minuten gecontroleerd of er een transactie klaar staat. Is dit het geval dan wordt de klant naar de acceptatiepagina doorgestuurd.
3 Gedetailleerd Procesontwerp en Implementatie In dit hoofdstuk wordt de implementatie in detail besproken. Er worden delen van de code getoond en toegelicht om zo een indicatie te geven van de interessantere oplossingen in het project. 3.1.1
Inloggen
Inloggen op het systeem is een procedure die meerdere stappen kent, hieronder vallen het bepalen van de gebruikers rechten en het verwerken van transacties, wanneer we al deze stappen tekstueel behandeld hebben, zullen we met een tweetal diagrammen een overzicht schetsen.
14
IN3405 Bachelorproject - Emaganda 2.0 Wanneer een klant of werknemer in een café een computer opstart, dient hij in eerste instantie in te loggen via het inlogscherm van NoMachine. In dit scherm dient de gebruiker zijn gebruikersnaam en wachtwoord op te geven waarna NoMachine de gebruiker in de LDAP-database opzoekt en het wachtwoord verifieert. Vervolgens start het besturingssysteem op. Nu zal het besturingssysteem automatisch een browservenster openen met als initiële pagina de Emaganda 2.0 hoofdpagina. 3.1.2
Algemene gebruikerslogin
Als de main controller van de emaganda interface eenmaal is geladen gebeuren er een aantal zaken. Ten eerste wordt gecontroleerd of de gebruiker waarmee Emaganda geladen is wel bestaat. Als dit niet het geval is (iets wat voor kan komen als het fout gaat in het opstartscript of als Emaganda voor testdoeleinden zonder gebruiker wordt geladen) wordt een additioneel loginscherm geladen. Dit scherm wordt verzorgd door de loginfunctie van nog steeds de main controller zodat alle initiële loginacties die normaal gesproken door het systeem zelf geregeld worden door een controller (main) afgehandeld worden. Nu wordt gekeken of de gebruiker nog transacties open heeft staan. Als dit zo is wordt de transactionConfirmation view geopend waarin de gebruiker deze kan accepteren. Na acceptatie wordt gekeken of de gebruiker nog meer transacties open heeft staan waarna hij ook deze kan accepteren. Als laatste wordt in de main controller een nieuwe sessie voor de gebruiker aangemaakt. Deze sessie bevat ten eerste de naam van de gebruiker. Ook het gebruikerstype wordt opgevraagd en in de sessie opgenomen. Als er geen gebruikerstype bepaald kan worden wordt de gebruiker weer naar het initiële inlogscherm doorverwezen. Dit is een zeer zeldzame situatie die kan optreden wanneer gebruikers informatie foutief in de LDAPdatabase staat. Maar in de praktijk zal een gebruiker met een verkeerd gebruikersid allang door de JavaTimer van het systeem zijn verwijderd en is de laatste check er dus alleen voor debugredenen. Verder wordt een tweetal initiële privileges die alle gebruikers hebben opgenomen in een lijst met privileges die vervolgens ook aan de sessie wordt meegegeven. Tot slot worden de systeemeigenschappen opgehaald en aan de sessie meegegeven. Als de gebruiker een staflid is wordt hij nu (nadat hij tot zodanig in zijn sessievariabele is bestempeld) doorverwezen naar een speciale inlogpagina voor stafleden. Als hij geen staflid is, wordt hij doorverwezen naar zijn My_Account pagina. 3.1.3
Staflogin
Als een door het systeem ingelogde gebruiker een staflid is dan heeft hij de mogelijkheid om zichzelf in te loggen. Dit doet hij in de staffLogin controller/pagina die in het menu verschijnt voor gebruikers wiens basicType (in de sessievariabelen) staff is. In dit scherm vult het staflid twee dingen in. Ten eerste zijn paswoord en ten tweede of hij wil in loggen als shift leader. Wanneer het paswoord correct is, wordt het gebruikerstype opgehaald tezamen met de bijbehorende privileges en wordt het staflid doorverwezen naar de pagina die in de systeemeigenschappentabel in de sessievariabele aangeduid is als defaultStaffPage en wanneer deze pagina niet bestaat naar de SalesWindow, een pagina die voor ieder staflid toegankelijk is. 15
IN3405 Bachelorproject - Emaganda 2.0 Wanneer het staflid ervoor kiest om in te loggen als shift leader wordt hij, nadat zijn paswoord op geldigheid is gecontroleerd, doorverwezen naar de addRegisterCheck controller die hem een scherm voorschotelt dat de huidige inventaris van het café laat zien. Deze wordt vervolgens bijgesteld of geaccepteerd door het staflid. De verzameling van producten en hun hoeveelheden wordt een registerCheck genoemd en is opgebouwd uit registerCheckItems. Het toevoegen van een nieuwe registerCheck verloopt als volgt: Eerst wordt een nieuwe registerCheck in de database aangemaakt met daarin het id van de gebruiker die hem toevoegt. Nu volgt een ietwat ongebruikelijke stap. De Submit variabele wordt uit de $_POST array verwijderd zodat er nu alleen maar product-hoeveelheid paren in deze array aanwezig zijn. Nu kan over deze array geïtereerd worden en wordt voor ieder paar een registerCheck-item aan de juiste registerCheck toegevoegd waarin het productId en het verschil in hoeveelheid met de voorraad als verwacht gedefinieerd worden. Verder wordt voor ieder paar de voorraad geactualiseerd om deze weer kloppend te maken. Dit is als volgt geïmplementeerd: 1. unset($_POST['Submit']); 2. 3. foreach($_POST AS $productId => $amount) { 4. 5. $difference = $amount - $this->product>getStock($productId); 6. 7. $this->registercheck->addItem( $registerCheckId, 8. $productId, 9. $difference 10. ); 11. 12. $this->product->addToStock($productId,$difference); 13. } Tot slot wordt de gebruiker door verwezen naar de defaultUserPage zoals eerder beschreven. 3.1.4
Paginaopbouw
Aangezien wij werken met het CodeIgniter framework en diens implementatie van het MVCpatroon, is iedere pagina een controller. Wij onderscheiden twee subklassen hiervan, de MY_CustomerPage en de MY_StaffPage (het MY_ voorvoegsel is nodig om een aantal (niet alle) CodeIgniterfuncties te kunnen gebruiken binnen subklassen van CodeIgniterklassen). Alle controllers die alleen voor stafleden zijn, zijn subklassen van MY_StaffPage en alle controllers die voor gebruikers zijn zijn subklassen van MY_CustomerPage. Deze twee superklassen zorgen ervoor dat de privileges van de gebruikers gecontroleerd worden, dat het menu en in het geval van niet stafleden, dat de Timer die aangeeft hoeveel tijd een gebruiker nog heeft, geladen wordt. Ook zorgen zij ervoor dat de pagina's grafisch goed opgebouwd worden door de juiste header toe te voegen.. Logisch gezien is dit een zeer fijne oplossing met het oog op het hergebruik van code, maar wat implementatie betreft zaten er 16
IN3405 Bachelorproject - Emaganda 2.0 wat haken en ogen aan. Het probleem is namelijk dat een aantal belangrijke CodeIgniterfuncties niet meer werken in klassen die van CodeIgniterklassen erven. Het gaat hier met name om de functies die models en libraries laden. Dit probleem hebben wij opgelost door in de constructor van ieder controller de constructor van de superklasse aan te roepen met daarin een lijst van te controleren privileges en te laden controllers, scripts en libraries. Deze code komt bijvoorbeeld uit de constructor van een controller (Deficiencies) die van MY_StaffPage erft: 1. 2. 3. 4. 5. 6. 7. 8. 9. 10.
$title = "Deficiencies"; $models = array('registercheck','shift','product'); $scripts = array( 'jquery-1.2.6.min.js', 'jquery.livequery.min.js', 'date.js', 'jquery.datePicker.js', 'salesList.js', 'jquery.tablesorter.min.js'); $libraries = array(); $css = array('datePicker.css','tablesorter.css');
De superconstructor wordt aangeroepen met als eerste argument het benodigde privilege om deze pagina te kunnen openen: 11. parent::__construct('viewDeficiencies', $models, $libraries, $scripts, $css, $title); In de superconstructor in MY_StaffPage worden vervolgens eerst alle gevraagde entiteiten geladen: 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. 22. 23.
foreach($models as $model){ $this->load->model($model); } foreach($libraries as $library){ $this->load->library($library); } $data['cssFiles'] = array(); foreach($css as $cssItem){ $data['cssFiles'][$cssItem] = $cssItem; }
Scripts en de titel hoeven niet geladen te worden, maar worden via <script> en
tags aan de header toegevoegd: 24. 25. 26. 17
$data = array(); $data['scripts'] = $scripts; $data['title'] = $title;
IN3405 Bachelorproject - Emaganda 2.0 Nu wordt gekeken of de gebruiker correct is ingelogd en of hij de juiste privileges heeft om de pagina te gebruiken: 27. 28. 29. 30. 31. 32. 33. 34. 35.
$sessionId = $this->session->userdata('session_id'); if(!$sessionId || $sessionId === null){ redirect('/main'); } $privileges = $this->session->userdata('privilegeList'); if(!isset($privileges[$pagePrivilegeName])){ redirect('/myAccount'); }
Hierna worden de header en het menu geladen. In de MY_CustomerPage supercontroller worden deze eenvoudig als view geladen, in de MY_StaffPage controller echter niet. Eerst wordt hier gekeken of de functie die werd aangeroepen, werd aangeroepen als onderdeel van een AJAX request (meer hierover in de Views paragraaf hierboven). Als dit namelijk het geval is, dient er geen header geladen te worden, maar dient enkel de uitvoer van de functie zelf (die geen pagina laat zien) geretourneerd te worden. Dit is als volgt geïmplementeerd: 36. if (strpos($_SERVER['REQUEST_URI'], 'ajax') == false) 37. $this->load->view('header_view',$data); 38. 39. if ((strpos($_SERVER['REQUEST_URI'], 'ajax') == false) && $showMenu){ 40. $privileges = $this->session>userdata('privilegeList'); 41. $menu = new Menu(); 42. $menu->show($privileges); 43. } Wat hier gebeurt is dat gekeken wordt of de $_SERVER['REQUEST_URI'] (die de naam van de aangeroepen functie bevat), met het woord 'ajax' begint, als dit zo is gaat het (volgens afspraak) om een AJAX request en dienen header en menu niet geladen te worden. In de MY_CustomerPage supercontroller wordt verder ook nog het status-/Timerscherm geladen, dit bespreken we in een later hoofdstuk. 3.1.5
Browsen
Het wisselen tussen de Emagandapagina's verloopt via het menu. De controller die hier verantwoordelijk is, heet zelf ook menu. Het menu heeft tot een klein designdilemma geleid daar het zowel een controller moest zijn, omdat het een view moest kunnen laden en het ten behoeve van codehergebruik geen aparte methode van van MY_staff of MY_CustomerPage mocht zijn. Wij hebben gekozen om in beide controllers het menu te laden via de reguliere 18
IN3405 Bachelorproject - Emaganda 2.0 PHP syntax om nieuwe objecten aan te maken: 1. 2. 3.
$privileges = $this->session->userdata('privilegeList'); $menu = new Menu(); $menu->show($privileges);
Dit had enkel tot gevolg dat de sessievariabele van CodeIgniter onbruikbaar werd binnen het menu, vandaar dat de show-functie nu de privileges als argument mee krijgt. Binnen het menu wordt een array opgebouwd met daarin de menu's en sub menu's die een gebruiker volgens zijn privileges te zien krijgt: Eerst wordt een array om het menu in te plaatsen aangemaakt: 1. 2.
$menu = array(); if(isset($privileges['viewServiceControl'])){
Deze array vormt het submenu: 3. 4.
$menuItem = array(); $menuItem['name']='Services';
Ieder menu item bestaat uit de naam van een controller die direct aangeroepen kan worden als URL en de naam die in het menu moet worden laten zien: 5. if(isset($privileges['viewInternetControl'])) 6. $menuItem[1]=array( 'page' => 'internetControl', 7. 'name' => 'Internet Rates' ); 8. if(isset($privileges['viewProgramControl'])) 9. $menuItem[2]=array( 'page' => 'applicationControl', 10. 'name' => 'Application List' ); 11. … 12. 13. $menu[2] = $menuItem; 14. } Dan roepen we het view aan die het menu daadwerkelijk laat zien: 15. 16.
$data['menu'] = $menu; $this->load->view('menu_view.php',$data);
3.1.6
Het menuview
In het menu_view wordt deze array vervolgens weer ontleed en in HTML-formaat omgezet volgens het volgende algoritme: 19
IN3405 Bachelorproject - Emaganda 2.0 1.
Het menu zelf is een lijst: 2.
Hier komt de naam van het submenu 3.
echo $menuPage['name'];
Als het submenu geen pagina's bevat die de gebruiker mag zien dan wordt het in ieder geval niet toegevoegd: 4.
if(count($menuPage)>1){
Aangezien iedere $menuPage array begint met de naam van het submenu, schiften we deze eerst uit: 5. 6. 7. 8.
array_shift($menuPage); ?>
Tot slot voegen we de link naar de juiste pagina toe: 9. 10. 11. 12. 13. 14.
- =anchor($page['page'],$page['name']);?>
De CSS en Javascriptcode die deze statische lijst tot een dynamisch menu omtovert wordt verderop in dit document besproken. 3.1.7
Login-/Navigatieoverzicht
Nu we het gehele logingedeelte besproken hebben, geven we een beknopt overzicht in de vorm van een sequence diagram:
20
IN3405 Bachelorproject - Emaganda 2.0
Figuur 6: Sequence diagram van het aanmaken van een sessie
3.1.8
Transacties
Verkoop via het SalesWindow Het SalesWindow biedt de functionaliteit om krediet te verkopen aan klanten. Een zo aangemaakt kredietverzoek heet een transactie. Met Javascript wordt een formulier getoond waarin de klant wordt geïdentificeerd met zijn accountnaam of met zijn voor- en achternaamcombinatie. In het laatste geval wordt een dropdown getoond waaruit gekozen kan worden, aangezien er meerdere klanten kunnen zijn met eenzelfde naampaar. Vervolgens wordt een betaling gemaakt en wordt de transactie aangemaakt. Op dat moment is het krediet nog niet toegevoegd aan het account van de klant. Deze dient dit eerst te accepteren. Accepteren van transacties Transacties kunnen op twee manieren geaccepteerd worden. Ten eerste zal een klant worden geattendeerd op openstaande transacties wanneer hij inlogt (zoals beschreven in het hoofdstuk inloggen). Ten tweede zal een Javascript dat door MY_CustomerPage geladen wordt mensen automatisch naar de MyTransactions-pagina doorsturen alwaar men een lijst krijgt van openstaande transacties. De acties die het systeem uitvoert wanneer op de accept 21
IN3405 Bachelorproject - Emaganda 2.0 knop gedrukt wordt is in beide gevallen hetzelfde: •
Er wordt gekeken of de transactie niet al eerder geaccepteerd is (dit sluit fraude door middel van creatief de terugknop gebruiken uit).
•
Krediet wordt toegevoegd aan het juiste gebruikersaccount.
•
De transactie wordt als geaccepteerd opgeslagen.
•
Een eventuele bonus wordt aan de hand van de hoogte van het transactie bedrag berekend en toegekend aan het gebruikersaccount (meer over bonussen in het stukje bonussen).
Een overzicht kan (gecombineerd met overige inlogacties zoals eerder beschreven) gegeven worden in de vorm van een sequence diagram. Aanpassingen aan de JavaTimer De JavaTimer moest aangepast worden op dit punt. De huidige Timer zou iemand die geen krediet meer heeft niet de mogelijkheid geven in te loggen op het systeem om een transactie te accepteren. Na de aanpassing controleert de timer eerst of de gebruiker transacties open heeft staan en wanneer dit het geval is geeft hij de gebruiker 60 seconden (deze seconden worden door de pagina zelf ook weergegeven) om de transactie te accepteren alvorens hij de NX-sessie sluit.
22
IN3405 Bachelorproject - Emaganda 2.0
Figuur 7: Sequence diagram van het accepteren van een transactie
3.1.9
Bonussysteem
Het Emagandasysteem kent een tweetal bonussen beide bestaand uit extra krediet dat aan het gebruikersaccount van de bonusgerechtigde toegevoegd worden. Ten eerste is er een bonus voor klanten die een transactie van een vooraf bepaald aantal credits aanvragen, deze bonus noemen we van af nu transactiebonus. Hiernaast is er nog een bonus voor mensen die een aantal velden van hun gebruikersprofiel in laten vullen bij de aanmaak van hun account (of eventueel later nog), dit noemen we de informatiebonus. De hoogte van beide bonussen, als wel de voorwaarde waaraan voldaan moet worden, kunnen worden ingesteld door stafleden in de BonusControl-pagina. Voor de velden en hun betekenis in die pagina verwijs ik u door naar de handleiding (Appendix D). Transactiebonus De transactiebonus wordt berekend wanneer een transactie geaccepteerd wordt aan de hand van het volgende commando: 1.
$bonus = $this->bonus->getBonus($amount);
Waarin $this->bonus het bonusmodel is. Deze functie werkt als volgt.
23
IN3405 Bachelorproject - Emaganda 2.0 De lijst van krediethoeveelheden waaraan voldaan moet worden om voor een bepaalde bonus in aanspraak te komen wordt in aflopende volgorde opgehaald waarna over deze lijst wordt geïtereerd en de eerste bonus die een kleinere of evengrote hoeveelheid aangeschafte credits nodig heeft wordt teruggegeven als bonus van toepassing: 1. 2. 3. 4.
foreach($allbonus as $bonus){ if($credits >= $bonus['CreditsBought']) return $bonus['BonusCredits']; }
Informatiebonus Als een klant een account aanmaakt is het mogelijk voor hem om extra krediet te ontvangen door in een keer een bepaalde hoeveelheid krediet in te kopen en wat persoonlijke gegevens op te geven, zoals geboortedatum, telefoonnummer en e-mailadres. Het formulier toont realtime welke bonus potentieel verkregen kan worden en of deze inderdaad is verkregen, op grond van ingevulde gegevens. Na het invullen van alle informatie wordt het account aangemaakt met een transactie voor het daarbij betaalde bedrag, infobonus incluis. Clubmemberbonus Een derde bonus die gegeven wordt is de zogenaamde clubmemberbonus. Een clubmember is een speciaal soort gebruiker die door van deze bonus gebruik te maken langer achter een computer kan zitten voor het zelfde geld als een niet-clubmember. Over deze bonus meer in het hoofdstuk over de Timer. Aanpassingen aan de JavaTimer De JavaTimer is op een aantal plekken aangepast om de verscheidene bonussen te verwerken. 3.1.10 Gebruikersbeheer Het beheer van gebruikers is functioneel gezien verdeeld over drie plekken binnen het systeem. In het SalesWindow kunnen nieuwe klanten aangemaakt worden, in de StaffList kunnen stafgebruikers aangemaakt en beheerd worden en in de CustomerList kunnen klanten beheerd worden. Technisch gezien is het proces van het toevoegen of veranderen van een gebruiker op iedere plek ongeveer hetzelfde, daarom behandelen we hier enkel het gebruikersbeheer zoals aangeboden door de StaffList. Ook kunnen we zeggen dat het toevoegen van gebruikers in vrijwel niets verschilt van het verwijderen ervan. Wanneer alle waarden voor het nieuwe staflid opgegeven en bevestigd zijn wordt de createStaffMember-functie van StaffList aangeroepen. Dan worden alle waarden gecontroleerd en waar nodig, geformatteerd of aangevuld en vervolgens in een array gestopt. Ook worden er nu waarden toegevoegd die nodig zijn voor onder andere het NoMachine systeem, de JavaTimer en de LDAP-databasehiërarchieën. Dan wordt het opgegeven paswoord gecodeerd met behulp van een functie uit de userSystem library:
24
IN3405 Bachelorproject - Emaganda 2.0 1. 2. 3. 4.
$password = trim($_POST['userPassword']); $repassword = trim($_POST['userPassword2']); $passw_hash = $this->usersystem ->encryptUserPassword($password,"ssha");
En wordt het paswoord gecontroleerd op lengte en op overeenkomst met het als tweede opgegeven paswoord. Tot slot wordt gekeken of er al een gebruiker met de gekozen gebruikersnaam bestaat, en zo niet wordt deze toegevoegd en wordt een message variabele aangemaakt die aan het bevestigingsview kan worden doorgegeven: 5. $userExists = $this->ldapuser->getUser($this->input>post('uid')); 6. $message = ''; 7. if ($userExists == "" && $ok) { 8. if ($this->ldapuser->update($user)) { 9. $message .= "Account created succesfully"; Zoals uit dit het laatste stuk code blijkt wordt het communiceren met gebruikersentiteiten overgelaten aan het ldapuser model. De update functie doet echter niet veel meer dan: 10.
return $this->usersystem->saveUser($userArray);
De usersystem library fungeert als een façadeklasse die een brug vormt tussen het reeds bestaande LDAP-systeem en het nieuwe Emaganda. Het beheer van het oude systeem is in handen van ICT4Schools. 3.1.11
Usertypes
Aangezien het bestaan van verschillende soorten gebruikers zeer belangrijk is binnen het nieuwe Emaganda is het toevoegen en beheren van al deze gebruikerstypen ook van groot belang. Beide zaken worden geregeld in de UserTypeList. Wanneer een nieuw usertype aangemaakt wordt dient eerst verplichte informatie met betrekking tot het type opgegeven te worden zoals de naam en of het een klant of staftype is. Vervolgens dienen er uit de gepresenteerde lijst met privilege/checkbox paren de gewenste privileges aangevinkt worden. Wanneer op New User Type-knop gedrukt wordt maakt de UserTypeList/addUserType het nieuwe type aan. Het verwerken van de type informatie is triviaal, het verwerken van de privilege lijst iets minder. Het is goed te weten dat binnen Emaganda een usertype beschikt over een privilegelijst. Deze lijst wordt in de de database gerealiseerd door een tabel met rijen bestaande uit userTypeId's en PrivilegeId's, als men nu alle rijen met een bepaald userTypeId ophaalt, krijg je de privilegelist voor dat type. Met het bovenstaande in het achterhoofd verloopt het toevoegen van nieuwe privilegelists als volgt: Een lijst van alle mogelijke privileges wordt opgehaald. Over deze lijst wordt geïtereerd en ieder privilege wordt vergeleken met privileges zoals ze in de $_POST variabele voorkomen: 1. 25
foreach($allPrivileges as $privilege){
IN3405 Bachelorproject - Emaganda 2.0 2. 3.
if(isset($_POST[$privilege['PrivilegeId']]) && !$this->privilegelist->contains($_POST['UserTypeId'], $privilege['PrivilegeId'])){
Als het userTypeID/privilege paar nog niet bestond, wordt het nu toegevoegd: 2. privilegelist>add($_POST['UserTypeId'],privilege['PrivilegeId ']))} Voor het updaten van een privilegelist moet ook gekeken worden naar privileges die uitgevinkt zijn ten opzichte van de oude lijst: 3. 4.
else if(!isset($_POST[$privilege['PrivilegeId']]) && $this->privilegelist->contains($_POST['UserTypeId'], $privilege['PrivilegeId'])){ 5. privilegelist>remove($_POST['UserTypeId'],privilege['PrivilegeId'])) Het LDAPUser model bevat ook nog een interessante functie met betrekking tot user types namelijk de getStaffByLetter/getCustomerByLetter-functies welke worden gebruikt in de StaffList en CustomerList om gebruikerslijsten te genereren. 3.1.12 Systeemconfiguratie Door het hele Emagandasysteem heen staan instructies die configuratie afhankelijk zijn. Zo zijn er instellingen als e-mailadressen (die een e-mail ontvangen wanneer bijvoorbeeld een product op begint te raken), standaardpagina's en conditionele code zoals de registratie van veranderingen de database die aan- en uitgezet kan worden. Deze instellingen worden beheerd in de systemConfiguration pagina. Dit beheer is triviaal. De instellingen kunnen wanneer nodig opgehaald worden uit de sessievariabelen. Het volgende stuk code laat zien hoe de verandering van een systemConfiguration table entry wordt opgeslagen wanneer dit gewenst is volgens de systeemconfiguratie: Systeeminstellingen worden opgevraagd uit de sessievariabelen: 1.
$sysprops = $this->session->userdata('systemProperties');
De oude tabel entry wordt opgevraagd om later aan de change tabel toegevoegd te kunnen worden wanneer dit gewenst is: 2. 3. 4.
$oldEntry; if($sysprops['listSystemPropertyChanges'] = "on") $oldEntry = $this->systemproperty->get($_POST['Name']);
De update wordt uitgevoerd: 5. 6. 26
if($this->systemproperty->update($_POST['SystemPropertyId'], $_POST['Value'])){ $message = $_POST['Name'] . " Value succesfully changed";
IN3405 Bachelorproject - Emaganda 2.0 De oude en nieuwe rij worden tezamen met aanvullende informatie (waaronder het id van de gebruiker die de update uitvoerde) aan de change tabel toegevoegd. 7. if($sysprops['listSystemPropertyChanges'] = "on") 8. $this->change->add($this->session->userdata('userId'), 9. "systemproperty", 10. "change", 11. $oldEntry, 12. $this->systemproperty>get($_POST['Name'])); 13. }
3.1.13 Change-administratie Er kan voor gekozen worden om veranderingen in het systeem op te laten slaan in de change tabel. Voor iedere verandering zoals bijvoorbeeld het verwijderen van een gebruiker of het aan passen van de prijs van een product, wordt in dat geval een change entry aangemaakt die informatie bevat als het id van de gebruiker die de verandering deed, de tijd/datum wanneer dit gebeurde, de oude en de nieuwe versie van de veranderde tabel entry. Deze wijzigingen kunnen per gebruiker en per tabel opgevraagd worden in de ChangeList-pagina. Voor details van het toevoegen van een change entry verwijzen wij u door naar het kopje Systeemconfiguratie. 3.1.14 Demographics In de demographicspagina worden tabellen gegenereerd die informatie bieden over de gebruikers die door het Emagandasysteem zijn geregistreerd. Er kan informatie opgevraagd worden over een zestal gebruikerseigenschappen, waaronder geboortedatum, woonplaats en geslacht. Wanneer in de initiële view die deze pagina presenteert een eigenschap is geselecteerd dan stelt de showUserStats-functie de tabel als volgt samen: Eerst wordt de juiste eigenschap bepaalt op basis van de $_POST variabele: 1. 2. 3. 4. 5. 6. 7. 8. 9.
$property = "none"; $properties = array("yearofbirth" => "Year of birth", "credits" => "Credits", "localityName"=> "Locality Name", "gender"=> "Gender"); if (isset($_POST['propertyFilter']) && $_POST['propertyFilter'] != '') $property = $_POST['propertyFilter'];
Als er geen eigenschap is geselecteerd wordt een lege pagina met alleen het selectieformulier weergegeven. Vervolgens worden alle klanten opgehaald en als volgt verwerkt wanneer bijvoorbeeld gevraagd wordt naar hun verdeling over straten: 10. 27
if($property == "street"){
IN3405 Bachelorproject - Emaganda 2.0 We maken een array met de naam stats bestaande uit straatnaam-/aantal gebruikers die in die straat wonen-paren. Wanneer de straatnaam al in de array voorkomt, wordt het aantal verhoogt: 11. 12. 13. 14.
foreach($customers as $customer){ if(isset($stats[$customer->street])){ $stats[$customer->street]['Amount'] += 1; }
Wanneer de straatnaam nog niet in de tabel voorkomt wordt deze toegevoegd en wordt het aantal gelijk aan 1 gesteld: 15. else{ 16. $stats[$customer->street]['Name'] = $customer>street; 17. $stats[$customer->street]['Amount'] = 1; 18. } 19. } 20. $data['stats'] = $stats; Tot slot sorteren we de array (op alfabetische volgorde in dit geval)
3.1.15 Het Salessysteem Het SalesWindow handelt alle directe interacties tussen receptionist en klant af. Dit omvat het verkopen van tastbare en ontastbare producten, zoals koekjes en internetkrediet. Daardoor vraagt de pagina data op bij verschillende models en schrijft hij ook veel data uit. Om dit te stroomlijnen is jQuery gebruikt. Dit beperkt de hoeveelheid communicatie met de server tot een minimum. De pagina verifieert alles wat wordt ingevuld en waarschuwt als data verkeerd is ingevoerd. Ook dit voorkomt serverbelasting. Pagina's worden ook dynamisch aangepast als data wordt ingevoerd. Bijvoorbeeld de informatie of een klant al zijn informatiebonus heeft ontvangen en hoeveel het is, wordt realtime geactualiseerd. Dit wordt gedaan met aparte functies die een bepaalde taak afhandelen. Een voorbeeld is: 1. function updateEarnedBonus() { 2. totalBonus = 0; 3. birthdateBonus = 0; 4. localityBonus = 0; 5. mobileBonus = 0 6. mailBonus = 0; 7. 8. if ( ($("#birthdate").val()).match(/^\s*[1-2][0-9]{3}-[0-1] [0-9]-[0-3][0-9]\s*$/)) 9. birthdateBonus += parseInt($("#birthdateBonus").html(), 10 ); 10. if ( !(($("#locality").val()).match(/^\s*$/)) ) 28
IN3405 Bachelorproject - Emaganda 2.0 11. localityBonus += parseInt($("#localityBonus").html(),10); 12. if ( ($("#mobile").val()).match(/^\s*[0-9]{7,12}\s*$/) ) 13. mobileBonus += parseInt($("#mobileBonus").html(),10); 14. if ( ($("#mail").val()).match(/^[a-z0-9\._-]+@([a-z0-9_-]+\.) +[a-z]{2,6}$/i)) 15. mailBonus += parseInt($("#mailBonus").html(),10); 16. 17. if (birthdateBonus == null) birthdateBonus = 0; 18. if (localityBonus == null) localityBonus = 0; 19. if (mobileBonus == null) mobileBonus = 0; 20. if (mailBonus == null) mailBonus = 0; 21. 22. // Fill in the potential bonus and whether it's been earned 23. totalBonus = birthdateBonus + localityBonus + mobileBonus + mailBonus; 24. $("#earnedInfoBonus").html(totalBonus + ''); 25. if ( parseInt($("#infoBonusThreshold").html(),10) <= parseInt( $("#credits").val(),10) ) 26. $("#bonusEarned").html('Yes'); 27. else 28. $("#bonusEarned").html('No'); 29. } Hierin wordt bepaald of de informatiebonus wordt toebedeeld en wat deze bedraagt. De functie vraagt de ingevulde gegevens op en verifieert hun juistheid. Hierna worden de bonussen opgeteld en weergegegen bij Potential bonus. Hierna wordt bekeken of voldoende krediet wordt ingekocht en indien dat het geval is wordt de status daarvan weergegeven als Yes. Een ander voorbeeld is het realtime weergeven van de productenlijst. 1. 2. 3. 4. 5. 6. 7. 8.
function updateStockDisplay() { $.post("salesWindow/ajaxGetStockAmountsVisual", product, function(data) { jsonObject = eval( '(' + data + ')' ); jQuery.each(jsonObject, function(i, product) { $('#product' + i).html(product); }); }); }
Deze functie vraagt eerst een visuele representatie van de lijst van producten op bij de server met een AJAX-verzoek. Hierna worden de producten uit de lijst geactualiseerd met de verkregen informatie. De eval-functie zet de ontvangen JSON-data om in een direct te gebruiken object. Een laatste voorbeeld is het weergeven van een opgemaakte rekening als de klant tastbare producten koopt.
29
IN3405 Bachelorproject - Emaganda 2.0 1. function showSelectedProducts(totalPrice) { 2. data = {}; 3. $.post("salesWindow/ajaxGetProductPrices", data, function(data) { 4. jsonObject = eval( '(' + data + ')' ); 5. $("#selectedProductsList").empty(); 6. $("#productForm input[type=text]").filter(function() { 7. return this.value.match(/^\s*0*[1-9][09]*\s*$/) }).each(function() { 8. // display a formatted bill 9. productId = this.id 10. $("#selectedProductsList").append(parseInt(this.value, 10) + "x D" + jsonObject[productId] + ' - '); 11. var productName = $("label[for = " + this.id + "]").text(); 12. $("#selectedProductsList").append(productName + "
"); 13. }); 14. $("#selectedProductsList").append('Total price: D' + totalPrice + "
"); 15. $("#productsOverlay").css({ visibility: "visible" }); 16. $("#productsOverlayText").css({ visibility: "visible" }); 17. }); 18. } Deze functie haalt eerst de prijzen op van alle producten en zet deze om tot bruikbaar object. Daarna wordt de rekening klaargemaakt. Van ieder gekocht product (amount > 0) wordt weergegeven hoeveel gekocht is en hoeveel een enkel item kost. Onderaan wordt vervolgens de totale prijs berekent en weergegeven. Als laatste wordt het geheel zichtbaar gemaakt voor de gebruiker. 3.1.16 Orders De orderControl controller is verantwoordelijk voor het in het systeem opnemen (en deels voor de verwerking) van orders. In essentie bestaat een order uit een productId en een te bestellen hoeveelheid van dit product (verder nog informatie over wanneer en door wie de order werd geplaatst) het toevoegen van een order is minder triviaal dan dat het lijkt. Eerst wordt (nadat alle velden zijn geverifieerd en opgeschoond) de order op reguliere wijze aan de database toegevoegd. Vervolgens wordt een e-mail gegenereerd voor de persoon verantwoordelijk voor het verwerken van de orders van dit café. Dit proces start met het in de systeemeigenschappen opzoeken of een e-mail wel gewenst is en wat het adres is waar de e-mail naar gestuurd moet worden waarna de velden van de e-mail worden ingevuld en de email wordt verzonden.
30
IN3405 Bachelorproject - Emaganda 2.0 3.1.17 Deliveries Waneer een levering aankomt in het café dan kan het systeem hiervan op de hoogte gesteld worden met de deliveryControl-pagina. Dit proces wat qua code niet zo interessant is, wordt weergegeven in het onderstaande diagram.
Figuur 8: Sequence diagram van het afhandelen van een gedane levering
Wat hier gebeurt is dat er na het openen van de delivery control pagina een lijst van openstaande orders wordt gegenereerd waaraan ook nog eens een productName aan toegevoegd wordt om deze lijst leesbaarder te maken voor de gebruiker. Dit gebeurt met behulp van een refereerbare foreach constructie: 1. 2. 3. 4.
foreach($data['orders'] as &$order){ $order['ProductName'] = $this->product->getName($order['ProductId']); }
Vervolgens selecteert de gebruiker de aangekomen order waarna er drie dingen gebeuren: er wordt een delivery entry in de database aangemaakt, met daarin het orderId, waneer de delivery binnenkwam, en wie hem aannam. Het aantal binnengekomen producten wordt aan de producttabel (de stock) toegevoegd. De received flag van de order wordt aangevinkt zodat deze niet meer als pending wordt beschouwd door het systeem (en niet meer door de deliveryControl-pagina wordt gezien). Wanneer een levering foutief toegevoegd wordt door een gebruiker, dan kan een bevoegd persoon deze ook weer weghalen. Als dit gebeurt verloopt het hierboven beschreven proces omgekeerd. 31
IN3405 Bachelorproject - Emaganda 2.0 3.1.18 Shiftsysteem Het subsysteem waarmee de dienstregelingen binnen het café gereguleerd kunnen worden bestaat uit twee controllers. De shiftSchedule waar teamleiders diensten kunnen plannen en de shiftList die aangeeft welke gebruikers dienst hebben op de huidige dag en waar deze gebruikers hun dienst ook kunnen activeren. Shift Schedule In het rooster kunnen per week diensten worden in geroosterd. Het toevoegen, veranderen en verwijderen van nieuwe diensten is redelijk triviaal. Het meest complexe aan deze pagina is dan ook de manier waarop het rooster per week wordt samengesteld. Standaard is $date de huidige dag, als er echter een andere dag is geselecteerd wordt deze eerst uit de $_POST variabele gehaald. Dan worden de data van de zondag en zaterdag waartussen de gekozen dag valt bepaald met behulp van de getWeek-functie uit de time library. Aan deze data wordt een tijdsaanduiding toegevoegd en de diensten tussen de data worden uit de database opgehaald: 1. 2. 3. 4.
$startOfSchedule = mdate($week['sunday'] . " 00:00:00"); $endOfSchedule = mdate($week['saturday'] . " 23:59:59"); $data['shifts'] = $this->shift->getAll($startOfSchedule,$endOfSchedule);
Deze diensten worden nu eerst geformatteerd en vervolgens aan het view doorgegeven waar zij op de volgende manier per dag worden gesorteerd en aan de gebruiker gepresenteerd: Ten eerste worden er twee arrays gemaakt die dienen als enumerator om in combinatie te gebruiken met de array van dagnummer-datum paren die de time->getWeek($date)-functie retourneert. We itereren vervolgens over de zeven dagen van de week en drukken telkens de naam van de weekdag (beginnend met een hoofdletter) af op het scherm: 5. 6. 7.
for ($dayOfWeek = 0; $dayOfWeek <= 6; $dayOfWeek += 1){ echo $days[$dayOfWeek].' '. $week[$daysNoCap[$dayOfWeek]].
Dan itereren we over alle diensten en maken een formulier aan voor iedere dienst wiens dagnummer volgens de date()-functie van PHP gelijk is aan de huidige weekdag: 8. foreach($shifts as $shift){ 9. if(date("w",strtotime($shift['StartDate']))==$dayOfWee k){ 10. 11. echo form_open('shiftSchedule/handleForm', $attributes); 12. echo form_hidden('ShiftId',$shift['ShiftId']); 13. …
32
IN3405 Bachelorproject - Emaganda 2.0 Shift List Wanneer een dienst geactiveerd wordt door een gebruiker controleert de shiftList of het opgegeven paswoord van die gebruiker correct was waarna via het shift model de dienst wordt geactiveerd met het volgende commando: 14.
$this->shift->activate($_POST['ShiftId'])
Vervolgens maakt deze functie een rij aan in de shiftActivity tabel. Deze rij bevat de tijd waarop hij aangemaakt werd, dat het een activatie betreft en welke shift het betreft. Wanneer de dienst later weer gedeactiveerd wordt, wordt er net zo'n rij aangemaakt, maar dan van het type deactivatie. Met behulp van deze shiftactivity entries kan de deficiency controller later precies berekenen welke diensten in overeenstemming met het rooster zijn uitgevoerd en welke niet. 3.1.19 Educatiesysteem Het overgrote deel educatie management verloopt via het CourseSchedule. Deze controller genereert een drietal views die hieronder kort beschreven worden. courseSchedule/index: het rooster zelf waar per datum nieuwe cursusinstanties ingeroosterd kunnen worden. courseSchedule/showByCourse: laat per cursus lijsten van alle ingeroosterde instanties zien. courseSchedule/showDetails: laat per cursus de deelnemende studenten zien en hoeveel zij reeds voor de les hebben betaald (en nog moeten betalen). Studenten worden ingeschreven voor cursussen via het SalesWindow. In de SalesWindow verschijnt dan een formulier met de vereiste klantgegevens. Deze zijn de accountnaam of de voornaam/achternaam combinatie. Hierna kan uit een dropdown de juiste persoon worden geselecteerd. Vervolgens wordt gekozen welke cursus gevolgd wordt. Hierna worden realtime de prijs van die cursus en het al voldane bedrag opgevraagd en weergegeven. Hierna kan een deel of het hele bedrag worden voldaan. Teveel betalen is niet mogelijk. Vervolgens wordt de inschrijven finaal gemaakt of geactualiseerd indien deze al bestond. Dit gaat met deze functie: 1. function updateEducationAmountPaid() { 2. // Fetch the amount the customer already paid for this course. 3. data = {}; 4. data['id'] = $("#courseSelection").val(); 5. data['accountName'] = $("#accountName").val(); 6. if ($("#educationAccountNames option:selected").text() != "No user found" && $("#educationAccountNames option:selected").text() != "") 7. data['accountName'] = $("#educationAccountNames option:selected").text(); 8. $.post("salesWindow/ajaxGetPriceAlreadyPaid", data, 33
IN3405 Bachelorproject - Emaganda 2.0 function(data) { 9. jsonObject = eval( '(' + data + ')' ); 10. $("#educationAmountPaid").html(jsonObject.amountPaid), "json" }); 11. } Hierbij worden de accountnaam en het id van de cursus verstuurd en het al voldane bedrag is de reactie. Wanneer een courseinstance verwijderd wordt, dient het geld van alle ingeschreven studenten weer terug gestort te worden op hun Emaganda-accounts. Dit gebeurt met de volgende code: 1. $students = 2. $this->coursesubmission>getAll($_POST['CourseInstanceId']); 3. foreach($students as $student){ 4. if(!$this->usersystem->addCredits($student['UserId'], $student['AmountPaid'])) 5. $message = $message . 6. " could not refund " . 7. $student['UserId']; 8. }} Verder is er nog de studentList-controller die per cursus of gebruikersnaam een lijst van vakken waarvoor de student is ingeschreven genereert. Deze lijst wordt als volgt samengesteld. Eerst worden voor alle courses, alle coursesubmission opgehaald (eventueel gefilterd op student of course): 1. foreach($courses as $course){ 2. $tempSubmissions = $this->coursesubmission 3. ->getAll($course['CourseId'],$userId); 4. $submissions = array_merge($submissions, $tempSubmissions); 5. } Vervolgens wordt er geïtereerd over deze lijst van coursesubmissions: 5.
foreach($submissions as $sub){
Dan wordt de huidige cursus opgehaald om de prijsinformatie te verkrijgen, waarmee berekend kan worden hoeveel een student nog moet betalen voor deelname: 6. 7. 8. 9. 34
$course = $this->course->get($sub['CourseId']); $course = $this->course->get($course['CourseId']); $student['AmountToPay'] = $course['Price'] - $sub['AmountPaid'];
IN3405 Bachelorproject - Emaganda 2.0 Wanneer er alleen maar om studenten werd gevraagd die nog geld moesten betalen wordt een student niet toegevoegd wanneer dit niet het geval is: 10. 11. 12.
if($onlyInDebt && ($student['AmountToPay'] == 0)){ continue; }
Vervolgens wordt een student entry aan de lijst toegevoegd op bases van de attributen van de huidige coursesubmission. Ook wordt voor de leesbaarheid later in de tabel de complete naam van de student uit de LDAP-database gehaald en aan de entry toegevoegd. Tot slot voegen we de student entry op zo'n manier aan de lijst toe dat de index sleutel van de entry de datum waarop de student zich voor de cursus inschreef is. Op deze manier kunnen we de studenten lijst sorteren op datum van inschrijving: 13. 14. 15.
$data['students'][$student['MadeAt']] = $student; } sort($data['students']);
Aanpassingen aan de JavaTimer Op dit punt zijn ook aanpassingen aan de JavaTimer nodig geweest. Wanneer een klant die ingeschreven staan voor een actieve courseinstance, dan hoeft deze geen krediet te betalen tijdens het ingelogd zijn. 3.1.20 Deficiencies Het systeem maakt onderscheid in twee soorten deficiënties (onvolkomenheden): Deficiënties met betrekking tot wat er in voorraad hoort te zitten en wat er daadwerkelijk in zit. Dit soort deficiënties worden ontdekt op basis van wat er bij de registerChecks door shift leaders opgegeven wordt. Als tweede zijn er de afwijkingen van het dienstrooster die worden ontdekt op basis van het activeren en deactiveren van diensten in de shiftList. Register Deficiencies De deficiencies controller genereert register deficiency lijsten met behulp van de registerCheck->getDeficient($startDate, $endDate)-functie, deze werkt als volgt. Eerst worden alle register check entries gemaakt in de juiste periode opgehaald ($all). Hierna wordt er per registercheck het verschil in hoeveelheid over alle registercheckitems behorende tot die check berekend: 1. 2. 3. 4. 5.
$i = 0; foreach($all as &$registerCheck){ $dif = $this->getDifference($registerCheck['RegisterCheckId']); if($dif != false){
Wanneer er verschil is wordt deze registercheck als deficient aan de lijst toegevoegd: 6. 35
$registerCheck['TotalDifference'] = $dif;
IN3405 Bachelorproject - Emaganda 2.0 7. 8. 9. 10. 11. 12.
$result[$i] = $registerCheck; $i++; } } return $result;
Shift Deficiencies Diensten zijn deficiënt als zij na hun geplande tijd starten, voor hun geplande tijd eindigen of wanneer zij helemaal niet beëindigd (gedeactiveerd in de shiftList) zijn. Dergelijke diensten worden gezocht door de shift->getDeficient($startDate, $endDate, $userId)-functie. Dit gaat als volgt. De getDeficient-functie haalt alle gevraagde diensten op, itereert hierover en gaat met de isDeficient($shiftId)-functie na of een dienst deficiënt is: 13. 14. 15. 16. 17. 18. 19. 20. 21. 22.
$all = $this->getAll($startDate, $endDate, $userId); $i = 0; $result = Array(); foreach($all as $shift){ if($this->isDeficient($shift['ShiftId'])) { $result[$i] = $shift; $i++; } }
Deze isDeficient(..)-functie werkt zo, dat eerst de (gebruikelijk beide) geregistreerde shiftactivities voor de dienst opgehaald worden. Vervolgens wordt de dienst deficiënt bevonden als er minder dan twee shiftactivities voor geregistreerd zijn, als de dienst niet bestaat of als de eerste shiftactivity entry (de activatie) is aangemaakt voor na geplande shift tijd of als de de tweede activity (de deactivatie) is aangemaakt voor de geplande eindtijd. 3.1.21 Systeem Performance Van het systeem wordt gemeten hoe de hardware presteert, maar vooral hoe het gesteld is met de internet snelheid. Periodiek wordt een tweetal Linux batch scripts gedraaid. Het eerste (download) download een bestand van een Gambiaanse server en een van een internationale service en slaat de resultaten op in een bestand genaamd downlog. Het tweede (dat ongeveer hetzelfde werkt als het eerste) vraagt een aantal systeemeigenschappen op en slaat deze op in een bestand met de naam perflog. De eerste eigenschap is de cpu load. 1.
cat /proc/loadavg > perflog
De tweede zijn de resultaten van een ping naar een buitenlands (Nederlands) adres en een binnenlands adres.
36
IN3405 Bachelorproject - Emaganda 2.0 2. 3.
ping ict4schools.nl -n -c 1 >> perflog ping ict4schools.nl -n -c 1 >> perflog
Dan nog wat andere eigenschappen en tot slot wordt een PHP-script aangeroepen: 4.
php ./addPerformance.php
Dit script parset vervolgens het perflogbestand en voegt performance entries toe aan de database die op de performance pagina weer te bezichtigen zijn. Dit alles is samen te vatten in het volgende diagram.
Figuur 9: Systeemprestaties opslaan middels scripts
3.1.22 Timer De timer die aangeeft hoeveel tijd een gebruiker nog heeft wordt zoals al eerder genoemd, geladen door de MY_CustomerPage controller. Daar hier nog wel wat bij komt kijken, lopen we de stappen die nodig zijn voor het correct laden van dit belangrijke onderdeel hier een voor een na. Als eerste wordt de huidige internetprijs opgevraagd in de vorm van een internetPrice entry. Deze entry bevat onder meer een float genaamd creditMultiplier, waarmee het aantal credits dat een gebruiker heeft, omgezet kan worden (door middel van vermenigvuldiging) naar het aantal minuten dat deze gebruiker nog heeft. 1.
$data['currentPrice'] = $this->internetprice->getCurrent();
Dan wordt een lijst van alle internetPrice entries die deze dag relevant zijn in geformatteerde toestand opgehaald. Deze functie is zeer complex en wordt aan het eind van dit hoofdstuk besproken. Ook worden hier nog alle tijden omgezet in minuten sinds 0:00 uur om weergegeven te kunnen worden in de timer:
37
IN3405 Bachelorproject - Emaganda 2.0 ... 3. $price['startMin'] = $this->time>timeToMinutes($price['StartTime']); ... Nu worden de credits en tijd die de gebruiker heeft op het moment dat de Timer voor het eerst geladen wordt opgehaald en wordt de gekeken of de gebruiker een clubmember is en zodoende recht heeft op bonusminuten. Als dit het geval is, wordt de bonusMultiplier opgehaald uit de systeemeigenschappen: 5. $data['bonusMultiplier'] = 0; 6. if($this->ldapuser->isClubmember($this->session>userdata('userId'))){ 7. $sysprops = $this->session>userdata('systemProperties'); 8. $data['bonusMultiplier'] = $sysprops['clubMemberCreditMultiplier']; 9. } Tot slot wordt, mits de gebruiker een klant (en geen staflid dat nog niet als staflid is ingelogd) is, het timer_view geladen. 3.1.23 Internetprijslijst De lijst van internetprijzen die voor de huidige dag aan gebruikers getoond wordt, zodat zij kunnen zien op welke tijdstippen hun krediet de meeste tijd oplevert, dient op zo'n manier uit de internetPrice entries gegenereerd te worden dat in het geval van overlappende periodes telkens enkel de periode met de hoogste prioriteit zichtbaar is. Dit wordt verwezenlijkt in de MY_CustomerPage->getFormattedPriceList()-functie. Deze complexe functie wordt hieronder gedetailleerd besproken. Als eerste worden alle internetPrice entries die vandaag relevant zijn opgehaald: 1.
$prices = $this->internetprice->getAllToday();
Dan starten we een loop waarmee we over deze entries itereren. Dit kan niet met de foreachconstructie daar we gebruik gaan maken van de splice()-functie van PHP die de interne pointer binnen het array zou resetten, waarna foreach telkens weer opnieuw zou beginnen. 2. 3. 4.
$fprices = array(); $arrayIndex = 0; while($arrayIndex < count($prices)){
We maken nu zelf een referentie aan naar de relevante array entry: 5.
$newPrice = &$prices[$arrayIndex];
Ook maken we een referentie aan naar het laatste element van de lijst die we zelf gaan genereren (de geformatteerde lijst), mits dit element er is natuurlijk: 6. 38
if(count($fprices) != 0){
IN3405 Bachelorproject - Emaganda 2.0 7. 8.
$lastPrice = &$this->last($fprices); }
Wanneer er geen laatste element is kunnen we het huidige element aan de geformatteerde lijst toevoegen en de loop opnieuw beginnen voor het volgende element van de ongeformatteerde lijst: 7. 8. 9. 10. 11.
else{ array_push($fprices,$newPrice); $arrayIndex++; continue; }
Nu we de nieuwe en de oude prijsperiode hebben geselecteerd kunnen we deze gaan aanpassen op een aantal typen van overlap. Ten eerste behandelen we de situatie wanneer de nieuwe periode een hogere prioriteit heeft en begint tijdens de eerdere periode, in dit geval wordt de eindtijd van de oude periode vervangen door de starttijd van de nieuwe. 12. 13. 14. 15. 16.
if($newPrice['StartTime'] < $lastPrice['EndTime']){ if($newPrice['Priority'] > $lastPrice['Priority']){ if($lastPrice['EndTime'] <= $newPrice['EndTime']){ $lastPrice['EndTime'] = $newPrice['StartTime']; }
Wanneer echter de latere periode (nog steeds met lagere prioriteit) ook nog eens langer duurt dan de periode met de hogere prioriteit, dan dient deze in tweeën gesplitst te worden, en dient het nieuwe stuk (dat een starttijd heeft gelijk aan de eindtijd van de periode met de hogere prioriteit) opnieuw (doch op de correcte plek) aan de lijst van tijd-prijs periodes (waarover we itereren) toegevoegd te worden, zodat deze bij een volgende iteratie behandeld kan worden. Dit opnieuw toevoegen doen we met de splice-functie die een deel van een array vervangt voor een andere array (we laten een deel met lengte nul vervangen): 17. else{ 18. $lastPrice2 = $lastPrice; 19. $lastPrice2['StartTime'] = $newPrice['EndTime']; 20. $lastPrice['EndTime'] = $newPrice['StartTime']; 21. array_splice($prices, $arrayIndex + 1, 0, array($lastPrice2)); Nu behandelen we het geval waarbij de lagere prioriteit periode compleet in de hogere periode valt. Hij kan gewoon verwijderd worden, maar er treedt nu wel een ongewenste situatie op in de geformatteerde lijst (de laatste prijs is corrupt daar eindtijd en begintijd omgedraaid zijn). Dit wordt hier recht gezet. 22. 23. 24. 25. 26. 39
if(($lastPrice['StartTime'] >= $lastPrice['EndTime'])){ array_pop($fprices); $lastPrice = &$this->last($fprices); $lastPrice['EndTime'] = $newPrice['StartTime']; }}}
IN3405 Bachelorproject - Emaganda 2.0 Nu kijken we naar de situatie waarin de nieuwe prijs-tijd periode de lagere prioriteit heeft, ten eerste wordt deze dan helemaal niet aan de geformatteerde lijst toegevoegd als hij compleet binnen de eerdere periode valt. 27. 28. 29. 30. 31.
else{ if($lastPrice['EndTime'] >= $newPrice['EndTime']){ $arrayIndex++; continue; }
Wanneer dit niet het geval is dient de begintijd van de nieuwe periode te worden veranderd in de eindtijd van de oude: 32. 33. 34.
else $newPrice['StartTime'] = $lastPrice['EndTime']; }}
Tot slot mag de nieuwe periode zonder verdere controle aan de geformatteerde lijst toegevoegd worden wanneer zijn begintijd groter is dan de eindtijd van de vorige periode. De mogelijkheid dat er gaten vallen in de dag wordt opgevangen doordat de JavaTimer een default creditmultiplier in acht neemt: 35. 36. 37.
array_push($fprices,$newPrice); $arrayIndex++; }
3.2 Testen en evaluatie Er zijn al vroeg in het implementatieproces uitgebreide gebruikers testen gedaan. Voor dit doeleinde heeft ICT4schools een website gereserveerd waarop gebruikers in Gambia bugs of onduidelijkheden konden posten die zij tegenkwamen bij het werken met de reeds geïmplementeerde onderdelen op de testserver. Halverwege het implementatieproces is er een uitgebreide evaluatie vergadering gehouden waarbij alle partijen betrokken waren (ook die in Gambia via Skype). Op deze vergadering zijn alle pagina’s van de webapplicatie doorgelopen en zijn suggesties met betrekking tot de functionaliteit van de webapplicatie gedaan. Het grootste deel van deze suggesties is ook daadwerkelijk in de webapplicatie verwerkt.
40
IN3405 Bachelorproject - Emaganda 2.0
4 Aanbevelingen Natuurlijk laten we NICE niet zomaar achter met dit systeem zonder enkele aanbevelingen over onderhoud en toekomstbestendigheid. Qua onderhoud is niet veel vereist. CodeIgniter en jQuery maken kleine bugfixes eenvoudig aan te pakken door simpliciteit en overzicht te bieden. Alle code is voorzien van duidelijk commentaar om de werking te benadrukken. Het is aan te raden te blijven werken met een acceptatieserver waar nieuwe code wordt getest en pas na goedkeuring deze over te zetten naar de live cafés. Dit is ook de werkwijze die nu wordt gehanteerd. Met het oog op de toekomst is het volgende te zeggen: CodeIgniter is een stabiel ontwikkelingsplatform. Derhalve is het niet noodzakelijk over te schakelen naar iets anders. Het systeem wordt ook goed onderhouden en uitgebreid. Updates zijn vrijwel allemaal backwards compatible. Het zou echter een idee kunnen zijn eens te kijken naar zijn jongere broertje, Kohana. Kohana biedt, in tegenstelling tot CodeIgniter, een totaal Objectgeoriënteerde werkwijze en dit maakt sommige uitbreidingen eenvoudiger en de code nog overzichtelijker. Ook Kohana is in volle ontwikkeling en daarom toekomstbestendig. Mogelijke nadelen aan Kohana zijn het soms ontbreken van documentatie en het soms breken van backwards compatibility met updates. Een tweede technische toevoeging kan bestaan in het toepassen van SmartyPHP in de Views. Deze framework biedt geavanceerde dataopmaak en filtersystemen en maakt het gebruik van templates eenvoudiger. Hierdoor wordt de interface zowel vanuit technisch als gebruikersoogpunt gestroomlijnder en efficiënter. Tot slot nog een overzicht van mogelijke aanpassingen en toevoegingen die aan het systeem kunnen worden toegevoegd. Sommige pagina's met grote tabellen kunnen, na nadere feedback van gebruikers qua opmaak worden verbeterd en overzichtelijker worden gemaakt. Met name de tijdzonebeheerpagina kan eenvoudiger en overzichtelijker worden gemaakt als eenmaal wat meer feedback is ontvangen op het gebruik. Mogelijke toevoegingen kunnen eruit bestaan nieuwe diensten aan te bieden en deze te integreren in het systeem. Zo zou er voor bepaalde geavanceerde applicaties extra krediet kunnen worden gevraagd. Ook zouden computertutorials beschikbaar kunnen worden gemaakt via Emaganda, eventueel ook tegen betaling. Dit zou een soort zelfstudiesysteem kunnen worden. Er worden nu enkele statistieken bijgehouden van gebruikers en van het systeem. De tijd zal uitwijzen welke andere statistische informatie van belang kan zijn. De fundamenten die nu gelegd zijn daarvoor zijn eenvoudig uit te breiden. Op die manier kan een gedetailleerd beeld ontstaan van het gebruik van alle diensten en welke groepen hierdoor worden aangesproken. Op grond hiervan kan de dienstverlening worden verbeterd. Verder kan, naarmate de internetbetrouwbaarheid en snelheid in Gambia toenemen, worden gedacht aan een centraal beheer van gebruikers en andere data. Op die manier kan een klant in een café een account aanmaken en zijn account overal in Gambia gebruiken. Ook zijn krediet zou dan overal beschikbaar worden. Dit maakt ook het inloggen van beheerders 41
IN3405 Bachelorproject - Emaganda 2.0 vanuit het hoofdkantoor op ieder café mogelijk voor een directe update in de status van het café. Al met al bevordert dit de integratie van het systeem en worden meer uitbreidingsopties en klantvriendelijkheid mogelijk. Mogelijk zou het hele systeem in de toekomst moeten draaien op het Windows besturingssysteem. Dit is redelijk eenvoudig te realiseren. De voor de hand liggende verandering in de systeemcode is de vervanging van de Linux Shellscripts die de internet- en systeemprestaties naar logbestanden wegschrijven. Deze scripts kunnen in principe geheel herschreven worden als Windows batch scripts. Wanneer hiervoor wordt gekozen moeten de PHP-scripts AddPerformance.php en AddDownload.php ook herschreven worden daar de inhoud van de gegenereerde logbestanden die zij parsen anders zal zijn. Hiernaast hoeft er in principe niets veranderd te worden aan het systeem. Als laatste zouden wij graag de aambeveling willen doen om de JavaTimer te herschrijven in C++. Tijdens ons verblijf in Gambia viel ons al op hoeveel geheugen en processortijd de Java Virtual Machine die nodig is voor het uitvoeren van Javaprogramma's innam. Helemaal gezien de redelijk lichte aard van de hardware waar het systeem op draait is het aan te raden om de Timer te implementeren in een taal die geen Virtual Machine nodig heeft en die daarnaast ook nog eens bekend staat om zijn goede mogelijkheden wat betreft geheugenbeheer en codeoptimalisatie.
42
IN3405 Bachelorproject - Emaganda 2.0
Referenties Boeken PHP4, Converse, Park Running Linux 5th edition, Dalheimer & Welsh PHP5 Power Programming, Gutmans/Rethans/Bakken Applied Java patterns, Stelting/Maassen Human-Computer Interaction, Dix/Finley/Abowd/Beale Fundamentals of Database Systems, Elmasri/Navathe Don't Make Me Think! 2nd edition, Krug Websites http://www.codeigniter.com Jquery in Action, Bibeault,Katz http://docs.jquery.com/Main_Page https://help.ubuntu.com/ http://useit.com
43
IN3405 Bachelorproject - Emaganda 2.0
Appendices [A – Vooronderzoek B – Functioneel Ontwerp C – Technisch Ontwerp D – User Manual]
44