Aplica ție web pentru evaluarea profesori lor de către studenți Absolvent, Neac șu Vlad-Florin Conduc ător științific, Prof. Dr. Ing. Ene Alexandru… [621992]

UNIVERSITATEA DIN PITEȘTI
FACULTATEA DE ELECTRONICĂ, COMUNICAȚII ȘI CALCULATOARE
DEPARTAMENTUL ELECTRONICĂ, CALCULATOARE ȘI INGINERIE ELECTRICĂ

PROIECT DE DIPLOMĂ

Absolvent: [anonimizat],
Prof. Dr. Ing. Ene Alexandru

Pitești
Sesiunea iulie 2017

UNIVERSITATEA DIN PITEȘTI
FACULTATEA DE ELECTRONICĂ, COMUNICAȚII ȘI CALCULATOARE
DEPARTAMENTUL ELECTRONICĂ, CALCULATOARE ȘI INGINERIE ELECTRICĂ

PROIECT DE DIPLOMĂ

Aplica ție web pentru evaluarea profesori lor de
către studenți

Absolvent: [anonimizat],
Prof. Dr. Ing. Ene Alexandru

Pitești

Sesiunea iulie 2017

Cuprins

1. Introducere ………………………….. ………………………….. ……………….. 7
2. Elemente de Teorie ………………………….. ………………………….. ……. 10
2.1 Protocolul HTTP ………………………….. ………………………….. ……… 10
2.1.1 Verbele HTTP ………………………….. ………………………….. ……. 11
1.1.2 Codurile de Status HTTP ………………………….. ……………………. 12
2.2 Formatul de date JSON ………………………….. ………………………. 12
2.3 Sistemul de baze de date MySQL ………………………….. …………. 14
2.3.1 Elemente d e limbaj ………………………….. ………………………… 15
2.3.2 Tipuri de date ………………………….. ………………………….. …… 15
2.3.3 Proceduri Stocate ………………………….. ………………………….. . 15
2.4 Limbajul de programare JavaScript ………………………….. ……… 16
2.5 Arhitectura Model -View -Controller ………………………….. ……… 17
2.6 Pattern -ul ActiveRecord ………………………….. …………………….. 19
3. Prezentarea Aplicatiei ………………………….. ………………………….. .. 20
3.1 Descrierea componentelor ………………………….. ……………………. 21
4. Fundamente Teoretice ………………………….. ………………………….. .. 22
4.1 Protocolul JSON -RPC ………………………….. ………………………….. .. 22
4.1.1 Remote Procedure Call ………………………….. ……………………. 22
4.1.2 RPC + JSON ………………………….. ………………………….. ………. 22
4.2 Principiul de statelessness pentru servicii web ………………….. 23
4.3 Tehnica ORM ………………………….. ………………………….. ……….. 24
4.4 Scalabilitatea serviciilor web ………………………….. ……………… 24
5. Proiectarea solu țiilor adoptate in cadrul lucrării …………………….. 26
5.1 Diagrama rolurilor si cazurilor de utilizare ………………………….. 26
5.2 Entități și arhitectura bazei de date ………………………….. …….. 28
5.2.1 Identificarea entitatilor ………………………….. …………………… 28
5.2.2 Arhitectura bazei de date ………………………….. ………………… 31

5.3 Implementarea protocolului JSON -RPC ………………………….. …. 33
5.3.1 Modele de Cerere – Raspuns (Request, Response) …………….. 35
5.4 Framework -ul Web ………………………….. ………………………….. . 36
5.4.1 Scheletul suitei Sails.js ………………………….. ……………………. 37
5.4.2 Comunicarea cu baza de date prin modele Active Record ….. 38
6. Implementarea soluției adoptate ………………………….. ……………… 42
6.1 Implementarea actorilor din sistem si mecanismul de autentificare
……………………………………………………………………………………… 42
6.1.1 Gestionarea profesorilor ………………………….. …………………. 45
6.2 Adaugarea chestionarelor si intrebarilor ………………………….. 50
6.3 Crearea studenților si serviciu de email ………………………….. .. 53
6.4 Evaluarea chestionarelor ………………………….. …………………… 55
6.4.1 Interfata pentru utilizator adaptabila rezolutiei ………………. 55
6.4.1 Logica evalu ării ………………………….. ………………………….. … 57
6.5 Vizualizarea rapoartelor ………………………….. ……………………. 59
7. Rezultate experimentale ………………………….. …………………………. 62
7.1 Tratarea erorilor ………………………….. ………………………….. …….. 62
7.2 Testare functionala ………………………….. ………………………….. . 63
8. Concluzii ………………………….. ………………………….. …………………. 65
9. Bibliografie ………………………….. ………………………….. ……………… 67
10. Anexe ………………………….. ………………………….. …………………….. 68

Lista figurilor

Figura 1 – Model de cerere HTTP ………………………….. …………………….. 10
Figura 2 – Model de raspuns HTTP ………………………….. ………………….. 11
Figura 3 – Obiect JSO N ………………………….. ………………………….. ……… 13
Figura 4 – Array JSON ………………………….. ………………………….. ………. 13
Figura 5 – Valuare JSON ………………………….. ………………………….. …….. 14
Figura 6 – Diagrama generala de implementare ………………………….. .. 20
Figura 7 – Sistemul de roluri ………………………….. ………………………….. 26
Figura 8 – Diagrama UML pentru roluri si entitati ………………………….. 27
Figura 9 – Tabelul cu utilizatori ………………………….. ………………………. 29
Figura 10 – Tabelul cu profesori ………………………….. ………………………. 29
Figura 11 – Tabele pentru logica chestionarelor ………………………….. …. 29
Figura 12 – Tabelul de legatura student – chestionar ……………………….. 30
Figura 13 – Diagrama bazei de date ………………………….. ………………….. 31
Figura 14 – Tabelul profesorilor ………………………….. ………………………. 33
Figura 15 – Diagrama unui modul JSON -RPC ………………………….. ……… 33
Figura 16 – Diagrama UML a secventei unei metode JSON – RPC ………… 34
Figura 17 – Model de cerere HTTP folosind JSON -RPC ……………………… 35
Figura 18 – Pagina de login ………………………….. ………………………….. … 43
Figura 19 – Pagina de gestionare a profesorilor ………………………….. …. 45
Figura 20 – Formular de creare a profesorilor cu validare ………………. 46
Figura 21 – Formular de asociere materii vizualizat pe un smartphone 49
Figura 22 – Pagina de editare a intrebarilor ………………………….. ……… 50
Figura 23 – Formular de adaugare a unui student ………………………….. . 55
Figura 24 – Chestionar vizualizat pe o tableta ………………………….. ……. 56
Figura 25 – Chestionar vizualizat pe un smartphone ……………………….. 57
Figura 26 – Lista chestionarelor ………………………….. ………………………. 57
Figura 27 – Vizualizarea rapoartelor ………………………….. ………………… 61
Figura 28 – Validarea email -ului ………………………….. ……………………… 63

7 1. Introducere

Aplicația de evaluare a profesorilo r din cadrul facultății își propune
realizarea unui sistem web interactiv si atractiv pentru studenți și profesori
prin care se facilitează realizarea unor chestionare personalizate pentru fiecare
materie prin care stundenții pot da feedback cadrelor didac tice intr -un mod
anonim. Acest proiect își propune încurajarea studenților in a -și exprima
opiniile privitoar e la modul de desfă surare a activitatil or didactice din cadrul
facultăț ii oferind o interfată de utilizator care nu stă in calea functionării
optim e a aplicației care este acesibilă si optimizată in special pentru
smartphone -uri si tablete. Alt rol pe care și -l insușește aplicați a este acela de
îmbunatatire a calităț ii didactice a profesorilor prin i ntermediul feedback -ului
studenț ilor, practic facil itând o cale de comunicare a opiniilor asupra programei
si a procesului de predare.
Utilizatorii aplicatiei vor fi de 3 tipuri : student , profesori , administrator ,
fiecare din acestia avand ac ces la doar o parte din informaț ie (cu excep ția
administratorulu i) si la interac țiuni diferite cu sistemul, astfel ca vom defini
cele 3 roluri de utilizator astfel :
• Administrator: Acces direct prin interfata web la baza de date si
configuratii , putand accesa si modifica toate resursele stocate in sistem.
• Profesor: Acce s read -only la interfața de rapoarte unde se pot vizualiza
toate materiile care sunt asociate cu profesorul și punctajul obținut în
urma evaluării studenților cât si alte date importante pentru a ap recia
evaluăr ile
• Student: Acces la toate chestionarele cre ate si asociate materiilor din
cadrul facultatii de unde poate evalua printr -un sistem standardizat
întrebarile personalizate pentru fiecare materie.
Datorita necesitatii de interconectivitate ridicata intre agentii
participanti in acest sistem, vom propun e o implementare cu tehnologii web,
permitand astfel accesul la acest sistem de pe o varietate de dispozitive si
indiferent de locatie, atat timp cat bineinteles este prezenta o conexiune la
Internet.
Crestea rapida a tehnologiei din ultimii ani a condus la o serie in continua
evolutie a dispozitivelor mobile, iar diferentele tehnologice dintre acestea au

8 dus la un singur numitor comun: Browser -ul. Acest software pe langa faptul ca
ofera user -ului posibilitatea de a naviga internetul, indiferent de impleme ntare
(Firefox , IE, Chrome ) are proprietatea speciala de a face asta folosind limbaje
comune, non -dependente de platforma (ele fiind interpretate si transformate in
cod ce ruleaza pe platforma respectiva ) si anume, dintre cele m ai cunoscute:
HTML , CSS, JavaScript .
O a doua mare problema ce a aparut odata cu aparitia multiplelor tipuri
de dispozitive mobile este cea a dimensiunii fizice a ecranelor , cu rezolutii
diferite , variind de la 640*480 pana la 3840 x 2160 si aspect -ratio diferit ,
factori ce influent eaza in mod evident modul in care se afiseaza o pagina web,
asa ca am decis sa implementam elemente din Bootstrap (unul dintre cele mai
cunoscute framework -uri pentru dezvolta rea interfetelor web adaptabile )
pentru aceas ta parte de dispozitive mobile.
Pentru stocarea informaț iei generat e din procesul de gestiune a
resurselor (profesori , studenti , materii , chestionare, intrebari, rapoarte ) si
nevoia de filtrare pentru afisare in functie de rolul in sistem (student , profesor ,
administrator ) am ales cea mai ra spandita baza de date open -source, si anume
MySQL. Ace sta comunica cu interfata web (Html + CSS + JS ) folosind un server
web dezvoltat in limbajul de programare JavaScript compilat cu ajutorul
compilatorului Google V8. Pentru simplificarea lucrului cu baza de date , am
folosit o librărie ce facilitează scrierea limbajului SQL într -un mod familiar
limbajului javascript si un ORM pus la dispoziție de framework -ul web . Cu
ajutorul acestui ORM am construit un serviciu web api s tandardizat ce livreaza
JSON , raspunsul lui f iind interpretabil de orice limbaj de programare (C, C#,
JavaScript , Python etc.). Pe langa avantajul ca acest serviciu foloseste un limbaj
standard de comunicare (JSON ), ne ofera posibilitate a de a fi instalat pe mai
multe servere in acelasi timp , fiecar e request al utilizatorului fiind stateless ,
astfel ca in cazul in care prin absurd unul dintre servere ar ceda , cererea va fi
instant redirectionat ă catre un server activ, si indiferent in ce pas ar fi fost
utilizatorul nu depinde de nici o variabila pastrat ă pe server.
Datorita dimensiunii datelor ce trebuie stocat e si distribui te intre
studenti si profesori, si posibilitatea de utilizare a aceluia si sistem pentru mai
multe facultati, am decis decuplarea completa a interfetei de accesorul de date.
Implementand arhitectura MVVM din KnockoutJS si elemente din jQuery pentru
accesul de date livrate de WebAPI , se poate obtine o interfata complet
independ enta de serverul web .

9 Lucrarea de fata este dedicata detalierii modului prin care s -a
implementat aplicatia cat si a deciziilor arhitecturale care au condus la un
rezultat final de succes.
Notiunile utile ce intervin in dezvoltarea acestui sistem prezenta t mai sus ,
sunt definite in lucrarea de fata la nivel teoretic, dar si pra ctic, dupa urmatoarea
structura :
• Prima parte teoretica a lucrarii (Capitolul 2) cuprinde elemente de teorie
generale despre infrastructura web , protocoalele de trasfer , sistemul de
baze de date si limbajul de programare JavaScript .
• Capitolul 3 prezinta o descriere detaliata a sistemului si interactiunilor din
sistem.
• Capitolul 4 este dedicat detalierii tehnicilor de programare si a tehnologiilor
folosite pentru a implementa sistemul .
• Capitolul 5 va prezenta cercetarea si deciziile luate in proiectarea unei
solutii pentru tema aleasa .
• Capitolul 6 cuprinde detalii privind implementarea propriu -zisă a aplicatiei.
Aici se va explica concret demersul luat pentru a pune in practica notiuni le
prezentate in capitolele anterioare
• Capitolul 7 prezinta modalitati de testare pentru functionarea corecta a
implementarii

10 2. Elemente de Teorie

2.1 Protocolul HTTP

HyperText Transfer Protocol este un protocol la nivel de aplicatie , pentru
sisteme distribuite , colaborative , de hypermedia. Este un protocol generic , fara
stare , care poate fi folosit pentru multe scopuri pe langa cel de hypertext , ca si
name servers si sisteme distribuite prin extensii la metodele de request, coduri
de eroare si heade re. HTTP este de asemenea folosit si ca suport pentru alte
protocoale ca SMTP,FTP,JSON -RPC,etc. HyperTextul este text structurat care
foloseste legaturi logice (hyperlink -uri ) intre noduri ce contin text. Dezvoltarea
acestui protocol a fost coordonata de IETF (Internet Engineering Task Force ) si
de W3C (World Wide Web Consortium ), ce a condus la publicarea unei serii de
RFCs (Requests for Comments ) , cea mai notabila dintre acestea , cea care
defineste HTTP/1.1 (versiunea cea mai comun folosita ) a fost l ansata in Iunie
1999 sub denumirea RFC2616 (http://tools.ietf.org/html/rfc2616 ).
Protocolul HTTP , implementeaza comunicarea de tip cerere -raspuns ,
astfel ca se disting 2 parti ale completarii une i cereri catre un server HTTP :
1. Clientul face o cerere :

Figura 1 – Model de cerere HTTP

11 Aceasta cerere este procesata de catre soft -ul existent pe serverul http , si
se intoa rce la client conform schemei :

Figura 2 – Model de raspuns HTTP

2.1.1 Verbele HTTP

O sesiune HTTP este o secventa de cereri si raspuns uri peste o retea. Un
client initializeaza o cerere catre un server care mai departe proceseaza cererea
si returneaza un raspuns. Sunt mai multe metode de request definite in
protocolul HTTP , printre care putem enumera GET, HEAD,POST,OPTIONS,PUT
si multe al tele . Acestea sunt numite “verbe” si indica actiunea ce se doreste a
fi initiata pe resursa identificata de server.
GET → Cere o reprezentare a resursei specificate. Cererile folosind verbul
GET trebuie doar sa returneze date , fara nici un fel de side -effect pe server.
POST → Cere serverului sa accepte entitatea incapsulata in cerere pentru
procesare. Un exemplu ar f i : postarea unui comentariu pe un forum
PUT → Cere ca entitatea incapsulata in cerere sa fie interpretata de server
conform datelor din ea (folosita foarte mult in arhitecturi RPC , si serivcii web
stateless )

12 OPTIONS → interogheaza host -ul facand o cer ere la care se va raspunde
doar cu headerele HTTP ce definesc verbele permise pe acest webserve. Ex. :
“Allow : GET,POST”

1.1.2 Codurile de Status HTTP

Începând cu HTTP/1.0 , prima linie a unui raspuns HTTP se numeste linia
de status , si contine un cod numeric (ex : “404” ) si o reprezentare text a
motivului (ex : “Not Found” ) . Modul in care clientul gestioneaza acest raspuns
depinde in primul rand de acest cod , si mai apoi de header -ele raspunsului . Pot
fi implementate si coduri noi pentru status , iar in cazul in care clientul care a
facut cererea nu poate distinge codul de eroare , foloseste doar prima cifra a
acestuia pentru a il incadra intr -o clasa generala de erori (ex. 4xx → Client error ,
5xx → Server error ).

Exemplu de cerere HTTP :
POST /r equest HTTP/1.1
Accept: application/json
Content-Type: application/json
Host: api.upit.ro
method=getuser

2.2 Formatul de date JSON

JSON (Javascrip t Object Notation ) este un format de transfer al datelor.
Este un format “human -friendly” , astfel ca este uso r de citit si scris manual.
Este foarte usor sa fie parsat automat, si este independent de limbaj, dar
foloseste conventii care sunt familiare pentru programatorii din familia C , C++,
C#, Java, Javascript. Aceste proprietati fac JSON sa fie formatul ideal de
interschimb al datelor. JSON este alternativa mai simplă, mai facilă decât
limbajul XML. Eleganța formatului JSON provine din faptul că este un subset al
limbajului JavaScript (ECMA -262 3rd Edition), fiind utilizat alături de acest
limbaj. Formatul JSON a fost creat de Douglas Crockford și standardizat prin

13 RFC 4627. Tipul de media pe care trebuie să îl transmită un document JSON este
application/json.
JSON este construit pe doua tipuri de structuri:
• colectie de perechi nume/valoare. (eg. Dictionary in c#)
• lista ordonata de valori (eg. List<T> in C# )
Acestea sunt structuri de date universale , aproape toate limbajele de
programare existente la momentul de fata le suporta intr -un fel sau altul. In
JSON , aceste structuri de date sunt formate :
Obiectul : un set neordonat de perechi nume/valoare. Un obiect incepe cu
“{“, si se termina cu “}” , fiecare nume este urmat de “:”, iar perechile sunt
separate de virgula .

Vector: un set ordonat de valori , incepe cu [ , se termina cu ] , valorile sunt
separate de virgula.

Figura 3 – Obiect JSON
Figura 4 – Array JSON

14 Valoare: poate fi un string intre ghilimele , un numar , o valoare booleana
un obiect sau un vector . Aceste structuri pot fi incapsulate.
In JSON, un string este o secventa de zero sau mai multe caractere
Unicode, intre ghilimele, folosi nd escape characters. Un caracter este
reprezentat ca string.
Un numar poate fi reprezentat doar zecimal , formatele octale si
hexadecimale nu sunt suportate
Exemplu de obiect JSON complex :
{
“user”: {
“nume” : “ Vlad Neacsu ”,
“varsta” : 22,
“id” : 1,
“idCerere” : 506567058
}
}

2.3 Sistemul de baze de date MySQL

MySQL este unul dintre cele mai populare SGBD -uri (Sistem de gestiune a
bazelor de date ) open -source. Este produs de MySQL AB , companie dedicata
acestui proiect. MySQL asigura gestiu nea colectiilor structurate de date, este un
sistem de gestiune relational astfel ca datele sunt organizate in tabele si intre
tabele se pot defini constrangeri de integritate si relatii. Pentru gestionarea
acestora se foloeste limbajul SQL (Structured Que ry Language) care a devenit
Figura 5 – Valuare JSON

15 un standard in acest domeniu inca din 1986 (ANSI) si adoptat si de ISO in 1987.
Acesta este un limbaj declarativ , care insa poate contine si elemente
procedurale. Este un limbaj flexibil care a fost supus la modificari dealungul
timpului, ramanand insa un standard definitoriu pentru SGBD -uri.
Poate fi folosit impreuna cu majoritatea limbajelor de programare , nativ
sau folosind third -party APIs, cum ar fi C , C++, C#, Java, Perl, PHP, Python ,
FreeBasic.

2.3.1 Elemente de limbaj

Limb ajul SQL este divizat in mai multe elemente, ce includ :
• Clauze (componente ale interogarilor si instructiunilor)
• Expresii (ce prin evaluare genereaza valori scalare sau tabele )
• Predicate (conditii ce sunt evaluate de SQL conform logicii booleene )
• Intero gari (regasirea datelor dupa criterii )
• Instructiuni (pot controla datele , pot include logica aplicatiei in ele ,
pot controla tranzactii si conexiuni )

2.3.2 Tipuri de date

In un tabel SQL , oricarei variabila ii este asignat un anumit tip de date :
CHAR (sir de caractere ), INT, FLOAT, REAL, NUMERIC, DATE, TIME . La fel ca
in orice alt limbaj de programare , aceste tipuri de date pot fi folosite cu
operatori de aditie , multiplicare , concatenare , samd.

2.3.3 Proceduri Stocate

Sunt elementele cele mai puternice de co ntrol a datelor dintr -un SGBD ,
acestea pot incapsula atat elemente procedurale cat si declarative , intr-o
“metoda” ce poate fi apelata parametrizat , din care pot rezulta unul sau mai
multe seturi de rezultate si poate determina side -effects ce manipuleaza datele
prezente in sistem.

16 2.4 Limbajul de programare JavaScript

JavaScript (adesea redus la JS) este un limbaj ușor, interpretat, orientat
pe obiecte, cu funcții de primă clasă, și este cel mai bine cunoscut sub numele
de limbaj de scripting pentru paginile Web, dar este folosit și în multe medii
non-browser. Este un limbaj de scripting bazat pe prototip uri, multi -paradigmă,
care este dinamic și susține stiluri de programare orientate spre obi ect,
imperative și funcționale.
JavaScript rulează pe partea clientul ui web, care poate fi folosit pentru a
proiecta / programa modul în care paginile web se comportă la apariția unui
eveniment , este un limbaj ușor de învățat și puternic, de asemenea folosit
pentru controlare a comportamentului paginii web.
Contra r ideilor greșite, JavaScript nu este "Interpretare Java". Pe scurt,
JavaScript este un limbaj de scripting dinamic care susține construcția de
obiecte bazate pe prototipuri. Sintaxa de bază este intenționat si milară atât cu
Java cât și cu C ++ pentru a re duce numărul de concepte noi necesare pentru a
învăța limba. Construiți le limbaj ului, cum ar fi conditionale , for-uri și while, try
… catch , etc. funcționează la fel ca în aceste limbaje.
JavaScript poate func ționa atât ca limbaj procedural cât și orientat pe
obiecte. Obiectele sunt create în mod programat în JavaScript, prin atașarea de
metode și proprietăți altor obiecte goale la timpul de execuție, spre deosebire
de definițiile clasei sintactice comune în limbile compilate precum C ++ și Java.
Odată ce un obiect a fost construit, acesta poate fi folosit ca un model (sau
prototip) pentru crearea unor obiecte similare.
Capabilitățile dinamice ale JavaScript includ construirea obiectelor
runtime, listele varia bile ale parametrilor, variabilele funcțiilor, crearea de
script -uri dinamice (via eval), introspecția obiectelor (prin intermediul
comenzi i in) și recuperarea codului sursă (programele JavaScript pot decompila
corpuril e de funcții în textul sursă ).
Primul Ja vaScript a fost creat de Brendan Eich la Netscape și de atunci a
fost actualizat pentru a se conforma ECMA -262 Edition 5 și versiunilor
ulterioare. Acest motor, nume de cod SpiderMonkey, este implementat în C / C
++. Motorul Rhino, creat de No rris Boyd (de asemenea, de la Netscape) este o

17 aplicație JavaScript scrisă în Java. Ca și SpiderMonkey, Rhino este compatibil
cu EC MA-262 Edition 5.
Mai multe optimizări majore de execuție, cum ar fi TraceMonkey (Firefox
3.5), JägerMonkey (Firefox 4) și Io nMonkey, au fost adăugate în motorul
JavaScript SpiderMonkey de -a lungul timpului. Lucrul este mereu în desfășurare
pentru a îmbunătăți pe rformanța execuției JavaScript.
Pe lângă implementările de mai sus, există și alte motoare JavaScript
populare, cum ar fi:
Google V8, folosit în browserul Google Chrome și în versiunile recente ale
browserului Opera. Acesta este, de asemenea, motorul utilizat de Node.js.
JavaScriptCore (SquirrelFish / Nitro) folosit în unele browsere WebKit,
cum ar fi Apple Safari. Caraka n în versiuni vechi ale Opera.
Motorul Chakra folosit în Internet Explorer (deși limba pe care o
implementează este denumită în mod oficial "JScript" pentru a evita problemele
legate de mărcile comerciale).
Fiecare dintre motoarele JavaScript de la Mozilla expune un API public pe
care dezvoltatorii de aplicații îl pot utiliza pentru a integra JavaScript în
software -ul lor. De departe, cel mai comun mediu gazdă pentru JavaScript este
browserul web. Navigatoarele web folosesc de obicei API -ul public pentru a crea
obiecte gazdă responsabile pentru reflectarea DOM în JavaScript.
O altă aplicație obișnuită pentru JavaScript este ca limbaj de scripting al
serverului (Web). Un server web JavaScript ar expune obiecte gazdă
reprezentând o cerere de HTTP și obiecte de răspuns, care ar putea fi apoi
manipulate de un program JavaScript pentru a genera dinamic pagini web.
Node.js este un exemplu popular pentru acest lucru.

2.5 Arhitectura Model -View -Controller

Model -View -Controller este un model de programare care a fost cr eat
pentru a rezolva problema de proiectare a unei aplicații destul de frecvente:
Afișarea datelor către utilizator (și eventual manipulare a intrărilor de la
utilizator).

18 Ideea de bază a acestui model este de a împărți programul (sau modulul)
în trei modul e principale: modelul, vizualizarea și controlerul. În forma sa cea
mai pură, m odelul MVC funcționează astfel:
• Modelul este, pur și simplu, pus în aplicare modulul care
gestionează datele programului. Ar putea fi, de exemplu, un modul
care interacționează cu o bază de date. În forma sa cea mai simplă
ar putea fi pur și simplu un fel de container de date (cum ar fi o
listă), în funcție de aplicație.
• Modulul Model nu are nicio funcționalitate ca atare decât ca o
interfață între date și restul programului. Ac easta gestionează
datele numai în măsura în care este necesară pentru a fi stocată și
restaurată, dar altfel nu ia decizii cu privire la ce să facă cu datele.
• Vizualizarea este modulul a cărui sarcină este de a afișa date către
utilizator. Nu are nicio fun cționalitate în sine, altul decât ceea ce
este necesar pentru transformarea și întocmirea datelor după cum
este necesar în formatul de afișare. Cu alte cuvinte, vizualizarea
funcționează ca o interfață între afișare (care poate fi un afișaj fizic
sau, de e xemplu, un format de document) și restul programului și
nu are nicio funcție legată de nimic altceva decât afișarea datelor.
• Controlorul este modulul principal care face toate deciziile. Are
toate funcționalitățile relevante ale programului și interacțione ază
cu modelul și vizualizarea, comandându -i și transmitând datele
între ele după cum este necesar.
Există două școli de gândire cu privire la faptul dacă vizualizarea
interacționează direct cu modelul sau nu. Într -o școală de gândire, Vederea și
Modelul s unt complet separate și nu au nici o legătură. Toate datele între cele
două sunt gestionate de controlor. În cealaltă școală de gândire, controlorul
poate comanda vizualizarea pentru a extrage date din model, astfel încât datele
să nu trebuiască să fie tra nsferate de controler. (Acest lucru creează în mod
natural o dependență între vizualizare și model.) Cu alte cuvinte,
funcționalitatea vizualizării este îmbunătățită la fel de mult ca și cum ar fi
necesar ca aceasta să interacționeze cu modelul pentru a pr elua date (dar în caz
contrar funcția View nu are funcționalitate Pe cont propriu.)

19 2.6 Pattern -ul ActiveRecord

In ingineria software , de-a lungul timpului a u fost definite mai multe
modele arhitecturale de a interacti ona cu baze de date relational , ActiveRe cord
fiind unul dintre ele. O abordare comuna a managementului logicii unei aplicatii
este impartirea ei in 2 straturi , un service layer ce incapsuleaza business data
si logica. In ActiveRecord , fiecare rand din un tabel din baza de date este tradus
intr-o instanta a unei clase care contine atat datele din baza de date cat si partea
de logica de acces + metodele afiliate entitatii programatice mapata la tabelul
din baza de date.
Astfel , folosind acest pattern , trebuie sa creem obiecte care contin
operatiun ile de baza (insert, update, delete ) + proprietatile care corespund mai
mult sau mai putin direct cu coloanele din baza de date.
Active record este o abordare de accesare a bazei de date , definita de
faptul ca un tabel sau view este incapsulat intr -o clas a, astfel ca o instanta a
unui obiect este strans legata cu un singur rand din tabelul din baza de date .
Dupa crearea unui obiect , un nou rand este inserat in baza de date la salvare .
Orice obiect incarcat in memorie isi ia informatiile automat din baza de date.
Clasa pentru obiect implementeaza metodele accesoare si proprietatile pentru
fiecare coloana din tabel/view .
Acest pattern este folosit in mare parte de ORM -uri, tipic acestea
expunand relatiile straine ca o proprietate sau o lista de obiecte in entitatea
curenta, simplificand enorm lucrul cu baza de date, majoritatea interogarilor
fiind generate automat (CRUD ).
Se regasesc o multitudine de implementari ale acestui pattern intr -o
varietate de limbaje de programare ca JavaScript, PHP, Ruby , Java , C# devenind
astfel o parte foarte importanta a web development -ului din ziua de azi , datorita
simplificarii accesului la baza de date , si oferind dezvoltatorului un mediu
familiar , non-intrusiv si orientat pe date.

20 3. Prezentarea Aplicatiei
Pentru dezvoltar ea aplicatiei web de evaluare a profesorilor din cadrul
facultatii , luand in considerare cerintele sistemului, am implementat o
arhitectura scalabila, non -dependenta de platforma, bazata pe tehnologii open –
source. Sistemul prezentat in aceasta figura este compus dintr -o ferma de
servere ce host -eaza serviciul web prezentat in aceasta, toate conectate la un
server de baze de date comun. Este irelevant daca aceste web servere sunt in
reteaua locala cu serverul mysql, insa pentru o si mai mare scalabilitate se pot
folosi si servere de baze de date diferite, cu auto -replicatie.

Figura 6 – Diagrama generala de implementare

Diagrama generala de implementare a aplicatiei prezinta conexiunile
dintre modulele ce comunica pentru a permite utilizatorului sa interactioneze
cu baza de date. Astfel ca serviciul web ce urmeaza a fi implementat va fi
compus din o serie de servicii (module ) , ce vor avea roluri diferite in procesul
de procesare si generare a resurselor facultatii . Fiecare din aceste module
comunica cu baza de date f olosind libraria KnexJS , si implementeaza atat logica
de business cat si logica de acces la date. Pentru a comunica cu aplicatia client
ce foloseste serviciul web, modulele ofera raspunsuri folosind protocolul JSON –
RPC care pot fi interpretate de orice lim baj de p rogramare sau platforma client.
Software -ul client trebuie sa contina o metoda de decodare a
raspunsurilor JSON pentru a consuma serviciul web (parte din codul javascript

21 livrat de HTTPServer , parte din codul aplicatiei mobile sau aplicatiei deskt op,
indiferent de platforma ).

3.1 Descriere a componente lor

→ Serverul de baze de date MySQL este responsabil pentru a mentine
datele rezultate din interactiunile cu clientii , precum si un istoric al acestora
(in cazul entitatilor ce necesita ), si de asemenea are rol de pastrare a datelor
despre sesiunile active ale clientilor (astfel ca serviciile ce folosesc baza de date
sunt stateless ) precum si de filtrare a datelor ce sunt returnate, in functie de
rolul in aplicatie al clientului.
→ Logica aplicatiei (API Web Server) este partea din cod, scrisa folosind
limbajul Javascript compilat in Node.js, ce constituie impreuna cu serverul de
baze de date MySQL un serviciu web RPC, c are implementeaza logica de
business a interactiunii cu baza de date folosind un ActiveRecord ORM, si de
asemenea ofera un mod de acces in tuitiv si standardizat , codificat in JSON, la
metodele si datele din sistem, pentru a fi f olosite de aplicatiile client.
→ Interface Layer
Aplicatia client va putea face cereri catre oricare dintre aceste module
pentru a produce schimbari in starea domeniului de date sau pentru a obtine
informatii despre datele din sistem, interfatand astfel atat accesul la baza de
date cat si logica aplicatiei , astfel ca aplicatia client nu va fi nevoita sa faca
decizii privitoare la securitatea si integritatea datelor sau logica de aplicatie.
Aceasta arhitectura orientata pe servicii permite o gama larga de implementari ,
astfel ca servi ciul web evaluare a profesorilor din cadrul facultatii poate folosit
ca un API din orice limbaj de progamare ce poate implementa protocolul HTTP,
si decodifica/construi este un obiect JSON.

22 4. Fundamente Teoretice

4.1 Protocolul JSON -RPC

JSON -RPC este un pro tocol RPC codificat in JSON. Este foarte similar cu
XML-RPC (care este codificat in XML ) , si defineste doar cateva structuri de date
si comenzi . Acest protocol permite trimiterea de “notificari” catre server
(cereri care nu necesita raspuns ) , si de asem enea permi te mai multe apeluri
asincrone.

4.1.1 Remote Procedure Call

Un Remote procedure call este un tip ce comunicatie interprocese care
permite unui program sa acceseze o metoda sau o procedura sa fie executata de
la distanta prin ethernet , fara ca program atorul sa specifice explicit detaliile
aceste interactiuni la distanta . Un RPC este initializat de client , care trimite o
cerere cu un mesaj catre un server cunoscut pentru a executa o procedura
specifica cu parametrii transmisi. Serverul trimite apoi un raspuns clientului,
si aplicatia isi reia cursul normal . Cat timp serverul proceseaza apelul clientul
este blocat (asteapta raspunsul de la server pana la reluarea executiei) , doar
daca clientul nu trimite un request asincron (aplicatia client isi continu a cursul
pana cand raspunsul apelului vine de la server )

4.1.2 RPC + JSON

JSON -RPC este un tip de protocol RPC , bazat pe structura de date
Javascript Object Notation . Clientul de obicei este un software ce intentioneaza
sa apeleze o singura metoda de pe un sistem remote . Mai multi parametri de
intrare pot fi pasati metodei remote ca vectori sau obiecte , si metoda la randul
ei poate returna tipuri de date complexe deasemenea, in functie de
implementare.

23 Toate tipurile de transfer sunt obiecte serializate in JSON . Un request
trebuie sa contina in mod normal 3 proprietati :
• method – Un string cu metoda ce se doreste a fi invocata
• params – Un vector de parametri ce sunt pasati metodei
• id – O valoare de orice tip , care este folosita sa recunoasca raspunsul
(un identificator unic al cererii )
• Raspunsul de la server este si el standardizat , si trebuie sa contina
3 proprietati:
• result – Datele returnate de metoda invocata , daca apar erori
aceasta valoare trebuie sa fie null
• error – Un error code specific , In cazul aparitiei unei erori
• id – Identificatorul cererii la care a raspuns

4.2 Principiul de statelessness pentru servicii web

Este un principiu de design care este aplicat in paradigma de design
service -oriented, pentru a permite serviciilor scalabile sa fie sep arate de starea
generala a domeniului , lucrand independent. Aceasta implementare duce la
reducerea resurselor consumaet de un serviciu , deoarece toata partea de state
management este delegata unui component extern. Prin reducerea consumului
de resurse , acest tip de serviciu poate raspunde la mult mai multe cereri intr –
un timp scurt.
Interactiunea intre orice doua programare implica urmarirea datelor
specifice transactionate intre cereri si raspunsuri , pentru ca defiecare data o
interactiune poate determina schimbarea datelor (poate depinde de o
interactiune anterioara ) .
Asta devine si mai important in arhitecturi distribuite unde clientul si
severul sunt situate la distanta. In arhitecturi 2 -Tier, responsabilitatea de a tine
aceste informatii despre inter actiuni depinde de client , deoarece acestia pot fi
identificati unic (prin adresa IP , Browser ) de web server.
Orice request subsecvent de la acelasi client va avea o “stare” stocata in
mesaj , pentru a permite serverului sa reia procesul de la orice pas i n cazul in
care intervine ceva in procesul de comunicare, si de asemenea permite
implementarea software -ului de webserver pe diferite masini fara nici o

24 conexiune directa ce pot prelua apelurile initiate in cazul in care unul din
servere se opreste.

4.3 Tehn ica ORM

ORM (Object -relational mapping ) este o tehnica folosita in dezvoltarea
software pentru a converti date in tipuri usor de folosit in limbajele de
programare orientate pe obiecte . Asta creaza , o baza de date de obiecte virtuala
care poate fi usor -accesibila dezvoltatorului. In comparatie cu tehnicile
traditionale de schimb de date dintre un limbaj de programare orientat pe
obiecte si o baza de date , tehnica ORM reduce enorm cantitatea de cod necesara
pentru executarea diferitelor interogari.
Dezava ntajele uneltelor de mapare Object -relational apar datorita
nivelului mare de abstractizare fata de ce se traduce in limbaj SQL , ceea ce duce
de multe ori la baze de date gresite din punct de vedere arhitectural, sau cod
ne-optimizat si incet, insa folosin d corect o implementare de ORM prezinta
avantajul vitezei de dezvoltare si a mentenantei usoare.
Unele din cele mai cunoscute ORM -uri sunt : ActiveRecord (php, ruby,
Java) , EntityFramework (C#, VB), Linq2SQL (C#,VB) . Un “active record”
(folosit in apro ape toate implementarile de ORM ) este un obiect care
incapsuleaza un rand din tabelul din baza de date, incapsuleaza accesul la baza
de date si adauga logica d e domeniu la obiectul rezultat -> record -ul activ.
Acesta este un obiect care contine atat date cat si comportament, mare parte
din proprietatile acestuia fiind asocieri 1:1 cu coloanele din baza de date.

4.4 Scalabilitatea serviciilor web

Serviciile web expun aplicatii pe internet pentru acces deschis , sau via
login . Dinamica computationala a unei aseme nea distributii presupune ca
resursele unui server ce hosteaza un endpoint al serviciului sa fie impartite ,
astfel reducand timpul de raspuns al serviciului. In mod evident un design
single -server nu poate fi scalat pentru a acomoda un numar mare de client i, deci
cand scalabilitatea este identificata ca fiind necesara, este crucial ca arhitectura

25 serviciului web sa fie replicabila si capabila de instalari multiple, pe servere
diferite.
Odata cu aceste schimbari arhitecturale , clientii unui asemenea servici u
distribuit vor deveni de obicei nevoiti sa devina mai complicati , deoarece vor
avea nevoie de proceduri prin care sa aleaga endpoint -urile serviciului la care
se pot conecta , inainte de a face o cerere si de a se “lipi” la o instanta a
serviciului si fac e apeluri.

26 5. Proiectarea soluțiilor adoptate in cadrul lucrării

5.1 Diagrama rolurilor si cazurilor de utilizare

Primul pas pe care l -am luat in proiectarea aplicatiei a fost sa determinam
actorii care vor folosii sistemul. In diagrama ce urmeaza putem vedea ce tipuri
de actiuni poate l ua fiecare rol:

Figura 7 – Sistemul de roluri
Astfel in sistemul nostru avem 3 roluri definite, dintre care unul
este pasiv. Administratorul are control total asupra sistemului, doar el poate
administra toate entitatile sistemului. Prin administrare intelegem practic toata
gestionarea bazei de date si entitatile acesteia.

27 Rolul profesorului este acela de a vizualiza rapoartele in urma
evaluariilor de catre studenti. Ei nu pot vedea in ce mod au raspuns studentii,
tot ce pot vizualiza sunt punctaje obtinute si date despre acestea.
Singura entitate din sistem care poate raspunde intrebarilor din
chestionare este studentul. Ei au un rol foarte important in sistem deoarece
corectitudinea si eroare unui chestionar scade pe masura ce mai multi studenti
participa in completarea chestionarului. Deasemenea in urma lor se fac cele mai
multe calcule si se stocheaza cea mai mare parte de informatie.

Figura 8 – Diagrama UML pentru roluri si entitati

28 5.2 Entități și arhitectura bazei de date

Folosind limbajul SQL , se vor defini tabelele si legaturile necesare
implementarii unui sistem de gestiune a studentilor in facultate folosind
descrierea generala a aplicatiei prezentata in Introducere , si urmarind
conven tiile de nume necesare implementarii tehnici ORM folosind
ActiveRecord.
Conventiile de nume folosite de ActiveRecord :
• Fiecare tabel va avea numele pluralizat (entitatea user => tabelul users)
• Fiecare tabel va avea un primary key , numit id+numeTabel , e.g. : idUsers ,
coloana autoincrementabila
• Toate entitatile vor contine o coloana Name (nu este necesara in
ActiveRecord , insa se va dovedi foarte utila in procedurile de filtrare si
user-friendliness )
• Legaturile straine vor fi denumite id + numeTabel_id +
numeTabelReferentiat
• Procedurile stocate fara side -effects (fara modificare a datelor sistemului
vor fi prefixate cu prefixul “Get” , pe cand cele ce modifica starea datelor
domeniului vor avea nume specific actiuni lor ce rezulta in urma apelului )

5.2.1 Identific area entitatilor

Sistemul are nevoie de actori activi, de accea vom avea nevoie de un tabel
pentru utilizatori. Acesta va contine datele esentiale alea unui cont de utilizator
cum ar fi nume, prenume, email. Pe langa aceste campuri mai este nevoie de o
parola pentru accesul in sistem. Pentru a distinge rolurile din sistem, tot in
acest tabel vom gasi o coloana cu id -ul rolului role_id care se va folosi in cod
pentru a limita accesul actorilor din sistem la diferitele pagini si actiuni.

29 Pentru unicitatea
conturilor, emailul este o cheie
unica ceea ce impiedica crearea a
mai multor conturi de utilizatori
cu accelasi email, impiedicand
astfel orice modalitate de trisare
sau pacalire a sistemului.
Odata ce avem tabelul de
useri creat, va trebui sa definim
celelalte entitati din sistem.
Pentru studenti nu este nevoie de coloane aditionale, de accea ei for fi definiti
doar prin id -ul rolului, coloanele de nume, prenume si email fiind suficiente
pentru sistemul nostru, deoarece se va pastra anonimatul celui ce co mpleteaza
chestionarele. Aceste date nu pot fi vazute decat prin interogarea bazei de date.
Pentru profesori am definit mai multe
coloane, intr -un tabel separat professor . Prin
acestea putem identifica mai usor profesorii
si putem adauga informatii aditi onale, care
pot fi vizualizate de studenti sau de
administratori. Legatura cu tabelul de useri
se face prin cheia straina user_id .
Legatura cu chestionarele se va face
prin entitatea de Materie. Aceasta are rolul de
a lega profesorii de chestionare si de
asemenea ajuta studentii in identificarea
chestionarelor.

Figura 11 – Tabele pentru logica chestionarelor
Figura 10 – Tabelul cu
profesori Figura 9 – Tabelul cu utilizatori

30 Fiecare chestionar ar e o relatie de 1:1 cu o materie, definita prin coloana
din tabelul survey, course_id . Pentru fiecare c hestionar in parte, se defineste
setul de intrebari la care studentul poate sa raspunda.
Prin tabelul de normalizare
student_survey, se realizeaza asocierea
punctajului final in urma completarii
chestionarului de catre un student. Pentru
inceput, cand st udentul intra prima oara in
platforma, se face o interogare pentru a
vedea ce chestionare au fost completate si
se exclud din lista prezentata studentului.
In urma completarii se insereaza in tabel
un rand ce va contine id -ul studentului si
id-ul chestiona rului, precum si punctajul
rezultat facand media raspunsurilor.

Figura 12 – Tabelul de legatura
student – chestionar

31 5.2.2 Arhitectura bazei de date

Figura 13 – Diagrama bazei de date

32 Baza de date are o implementare simpla dar suficienta pentru a
acompania l ogica de business a aplicati ei. Ea ne garanteaza consistenta datelor
si returnearea rezultatelor intr -un timp optim prin folosirea de indexi pentru
tabelele ce vor fi interogate cel mai des. Nu necesita o optimizare optima
deoarece datele ce vor fi stocate vor fi putine, aceasta intr and in categoria de
baze de date de dimensiuni reduse.
In continuare prezentam implement area tabelelor cheie din sistem:
User :
• Id – cheie primara, identificatorul unic al
utilizatorilor din sistem.
• Firstname si lastname sunt coloanele
care compun numele u tilizatorilor din
sistem
• Email – coloana in care se stocheaza
emailul utilizatorului. Aceasta are si rolul
de a pastra unicitatea contului, eliminand
erorile conturilor duolicate sau caile de trisare
• Password – coloana de tip VARCHAR unde se stocheaza un h ash al
parolei utilizatorului.
• Role_id – cheie straina ce face legatura cu tabelul de roluri. Rolurile
sunt definite in tabelul de role si au 3 posibile id -uri:

• CreatedAt, updatedAt sunt tabele de tipul data si timp, unde se
stocheaza momentul creeri sau momentul modificarii unui rand din
baza de date. Acestea pot fi folosite pentru sortare sau pentru a
creea rapoarte viitoare.

33 Professor :
• Id – identificator unic pentru utilizatorii
de tip profesor
• Title este coloana in care se stocheaza
titlul profe sorului, folosit la afisarea
numelui. Exemplu: Prof. Dr. Ing.
• Description – Se pot stoca informatii
aditionale, precum email alternativ,
numar de telefon sau o scurta descriere a
profesorului respectiv
• PhotoUrl – in aceasta coloana se retine
numele fisieru lui ce contine poza profesorului. Aceasta ajuta
studentii pentru identificarea profesoriilor.
• User_id – cheie straina care face legatura cu tabelul de user

5.3 Implementarea protocolului JSON -RPC

O alta parte importanta in proiectarea aplicatiei este parte a de
comunicare cu baza de date, folosind protocolul JSON -RPC, ce este bazat pe
protocolul HTTP , cu exceptia ca transferul de date este codificat in format JSON,
si prezinta un model standardizat de cereri remote de executie a procedurilor
pe serverul host .
Figura 15 – Diagrama unui modul JSON -RPC Figura 14 – Tabelul
profesorilor

34
Figura 16 – Diagrama UML a secventei unei metode JSON – RPC
Aplicatia va fi astfel impartita in mai multe module, fiecare reprezentand
un tabel din baza de date ce vor avea rol de provider de date si metode
executabile remote, bazata pe logica de business ce au ca scop manipularea
datelor din baza de date MySQL , in functie de tipul de acces al utilizatorului.
Fiecare modul va prelua o cerere de tipul GET sau POST si in functie de
metoda ceruta si parametrii trimisi codificatii in formatul JSON se va selecta o
actiune prin sistemul de rute implementat in sistem . In urma cererii facute de
client, serverul va executa actiunea respectiva si va returna un mesaj HTTP cu
un anumit cod in functie de negocierea care se produce pe server.

In momentul in care utilizatorul face o cerere catre server prin modul ele
de JSON -RPC, toate datele stocate persistent in browser -ul user -ului (cookies)
sunt trimise automat catre server. Acestea contin si un token unic prin care
utilizatorul este verificat pentru autenticitate in sistemul nostru. In figura de
mai jos se poa te observa campul de cookie ce contine un hash encriptat de catre
server prin care se negociaza autenticitatea userului in sistem. Hash -ul continue
un obiect complex care este encriptat, prin acesta pastrandu -se sesiunea user –

35 ului activa chiar daca server nostru este unul de tip Stateless (acesta nu tine in
memorie date).
5.3.1 Modele de Cerere – Raspuns (Request, Response)

Figura 17 – Model de cerere HTTP folosind JSON -RPC
Nu toate metodele necesita autentificare, exista metode in sist em lasate
intentionat accesibile publicului pentru a facilita integrarea cu alte sisteme
informatice ce pot face apeluri HTTP catre serverul nostru. Acceste metode nu
contin informatii sensibile. Un astfel de apel poate returna spre exemplu o lista
a mater iilor din sistem. Folosind metoda aceasta se pot centraliza datele din
cadrul unei facultati intr -o singura baza de date si se poate interoga sistemul
din afara aplicatiei.
O astfel de metoda este:
GET /allcourses

36
Aceasta metoda nu necesita parametrii aditionali, pentru ca se cer toate
materiile din sistemul nostru. Metoda noastra, ce implementeaza arhitectura
JSON -RPC, va raspunde cererii HTTP cu un obiect de tip JSON, exemplu:
{
"courses": [
{
"id": 7,
"name": "Structuri de Date",
"professor_id": 4
},
{
"id": 6,
"name": "Proiectare Logica",
"professor_id": 4
},
{
"id": 8,
"name": "Programare Orientata pe Obiecte",
"professor_id": 4
}
]
}

Din exemplu anterior se poate v edea ca metoda noastra JSON -RPC
/allcourses ne intoarce un vector de obiecte, ce contin id -ul resursei din baza
noastra de date, numele si id -ul profesorului. Se poate crea si o metoda extinsa,
daca este nevoie, care sa contina un obiect deep nested cu toa te legaturile pe
care le au materiile respective, cum ar fi toate chestionarele si punctajul lor cat
si datele profesorilor care sunt legati prin baza de date de materia respectiva.

5.4 Framework -ul Web

Framework -ul web este partea din aplicație care se ocu pa de toata logica
de server. Mai mult de atat acesta face conexiunea intre toate componentele
aplicatiei noastre web. In urma studiereii multiplelor suite web disponibile, am

37 ales suita Sails.js deoarece este un framework relativ matur, bine documentat
si mai mult decat suficient pentru proiectul descris.
Faptul ca este scris folosind tehnologia Node.Js ne ajuta foarte mult
deoarece foloseste limbajul de programare JavaScript. Toate fisierele de
configurare sunt de fapt, obiecte JavaScript, care se pot co nfunda cu mesaje de
tipul JSON, insa aceste obiecte sunt compilate si pastrate in memoria serverului.
Orice alta tehnologie web ar fi presupus folosirea unui limbaj aditional pentru
server, JavaScript fiind adoptat de toate browsere -le web ca fiind limbaju l de
programare pentru client.
Sails este un backend scris in Javascript ce usureaza construirea solutiilor
web chiar si de tip enterprise folosind tehnologia Node.js. Este conceput pentru
a emula arhitectura de programare MVC care este foarte familiara in lumea web
si de asemenea ofera tot suportul necesar pentru aplicatii web moderne: servere
web cu flux de date foarte mare, scalabile si arhitecturi orientate pe servicii.
Suita este destinata tuturor programatorilor care sunt familiare cu servicii
HTTP si foloseste cele mai familiare biblioteci de lucru cu HTML, CSS si
JavaScript.

5.4.1 Scheletul suitei Sails.js

Folosindu -se de tehnologia Node.js, instalarea si pornirea unei aplicatii in
Sails.js este una triviala. Prin rularea comenzii: npm i sails , manageru l de
pachete Node.js instaleaza tot ce e nevoie ca serviciul de sails sa poata rula o
aplicatie. De asemenea sails.js vine si cu un pachet de generatoare. Acestea sunt
pur si simplu module care primesc parametrii speciali si creeaza diferite fisiere
pentru sistemul nostru. Spre exemplu, prima oara cand se vrea instalarea
aplicatiei Sails, exista un generator ce initializeaza o aplicatie de baza, fara prea
multe configurari, pe care se poate incepe dezvolatarea. Comanda pentru
generarea unei noi aplicatii es te:
sails generate new <appName>
Aceasta comanda creeaza scheletul de baza pentru a incepe dezvoltarea
unei aplicatii web . Structura aplicatiei respecta standardele MVC si este folosita
in felul urmator:

38 • api este folderul unde gasim logica
aplicatiei. API este un acronim ce inseamna
Application Programmable Interface,
insemnand toate functiile de care avem
nevoie pentru a folosi functiile puse la
dispozitie de framwork
o Controllers este folderul unde putem
gasi toata logica pentru metodele
JSON -RPC sau HTTP .
• Models este folderul un se tin toate modelele aplicatiei
• Views – Contine toate fisierele de tip prezentare, de regula HTML,
dar in cazul de fata se foloseste un motor de sabloane care se va
traduce in fisiere HTML dupa ce acestea vor fi prelucrate de se rver.
• Config este un folder important pentru aplicatie deoarece aici se tin
toti parametrii de functionare a aplicatiei, spre exemplu conexiunea
bazei de date care creeaza contextul de acces catre serverul MySQL.
• Assets contine toate fisierele statice: CSS , JavaScri pt pentru client
sau imagini.

5.4.2 Comunicarea cu baza de date pr in modele Active Record

Framework -ul contine informatii in fisierele folosite in logica aplicatiei
sub forma comentarilor in limbajul JavaScript ceea ce ajuta programatorul
pentru a nu fi nevoit sa consulte documentatia pentru configurari minore. Un
exemplu de astfel de fisier este fisierul bootstrap.js , unde se face conexiunea cu
baza de date si se deschide contextul prin care se va interoga serverul MySQL.
/**
* Bootstrap
* (sails.c onfig.bootstrap)
*
* An asynchronous bootstrap function that runs before your Sails app
gets lifted.
* This gives you an opportunity to set up your data model, run jobs, or
perform some special logic.
*
* For more information on bootstrapping your app , check out:

39 *
http://sailsjs.org/#!/documentation/reference/sails.config/sails.config.b
ootstrap.html
*/

module.exports.bootstrap = function(cb) {
var knex = require('knex')({
client: 'mysql2',
connection: {
host: 'localhost',
user: 'root',
password: '',
database: 'lic'
}
});

knex.instanceId = new Date().getTime();

sails.config.knex = knex;

cb();
};

Biblioteca knex se ocupa de contextul bazei de date cat si de a prelua codul
scris in JavaScript si a -l transforma in cod SQL. Limbajul folosit este unul foarte
familiar administratorilor de baze de date, acesta apropiindu -se foarte mult de
SQL, spre exemplu:
knex.select('*').from('course').then(function(result, error) {
if (error) {
return res.negociate(e rror);
}
return res.json({courses: result});
});
Codul de mai sus rezulta intr -un cod SQL de forma:
SELECT * FROM `course`
Modelele ORM se ocupa in principiu de modele simple, usor de interpretat
si de gestionat. In sistemul nostru regasim un altfel de mo del pentru crearea

40 userilor din sistem. De regula modelele au un raport de 1:1 cu tabelele din baza
de date, astfel ca vom avea descris in cod JavaScript, urmatorul model:
module.exports = {

attributes: {
firstname: {
type: 'string'
},
lastname: {
type: 'string'
},
email: {
type: 'email',
required: true,
unique: true
},
password: {
type: 'string',
required: true
},
role_id: {
type: 'int'
},
toJSON: function() {
var obj = this.toObject();
delete obj.password;
return obj;
}
}
};
ORM -ul se va ocupa de campurile pentru stocarea timpului in mod
automat. Deasemenea el ne expune cateva metode pentru gestionarea usoara a
modelului ce va permite progr amatorului o dezvoltare rapida si standardizata.
Spre exemplu avem metoda .create() cu ajutorul careia se insereaza in baza de
date un rand in tabelul de user.
User.create({
firstname: firstname,
lastname: lastname,

41 email: email,
password: hash,
role_id: 2
}, function(error, user) {
// …
});
Trebuie remarcat faptul ca toate functiile prezentate pana acum primesc
ca parametru o alta functie, in cazul nostru anonima, care va fi executata la
sfarsitul functiei apelate initial. Aceasta procedura se nume ste Callback in
JavaScript si este des intalnita in cadrul aplicatiei. Aceastra strategie ne permite
lucrul asincron cu logica de business a aplicatiei noastre, un model ce se
preteaza pe modul de lucru a protocolului HTTP si in general al serverelor web.

42 6. Implementarea soluției adoptate
In acest capitol voi prezenta, in detaliu, pasii luati pentru a implementa
solutia in urma documentarii si a experimentarii. Aplicatia urmareste
standardele de scriere al codului si toate practicele bune care s -au dezvoltat de-
a lungul timpului, in programarea web. Pentru dezvoltarea aplicatiei pasii care
au fost urmariti pentru fiecare modul in parte sunt urmatorii:
• Crearea script -ului SQL pentru tabelul din baza de date
• Descrierea modelul ORM
• Descrierea logicii pentru acti unile din sistem in Controller -ul
modulului
• Crearea paginii care va fi livrata browserului si vizualizata de
utilizator.

6.1 Implementarea actorilor din sistem si mecanismul de
autentificare

Actorii detaliati in capitolele anterioare au fost implementati fol osind
modelul standard pus la dispozitie de framework. Pe langa gestionarea lor, am
implementat de asemenea si mecanismul de autentificare. Contul de
administrator a fost creat folosind script -uri de SQL, de accea nu este nevoie de
o interfata creata in pr ealabil pentru a folosi autentificarea sistemului.
Acesarea paginilor intr -un sistem MVC se face printr -un sistem de rute,
care descrie explicit resursa care se doreste a fi accesata si actiunea din
controller care se executa. Pentru pagina de login avem r uta:
'get /': 'HomeController.index',
Aceasta ruta ne spune ca atunci cand utilizatorul acceseaza resursa ‘/’
insemnand pagina de start a serverului acesta va fi redirectionat catre actiunea
index din controller -ul Home .

43
Aplicatia a fost gandita pentru a fi
privata de folosul public, orice cerere catre
o pagina din sistem fara a avea sesiunea de
autentificare trimisa o data cu cererea va
fi redirectionata catre pagina de login din
sistem. Acest lucru este posibil folosind
politele de securitate sau de g estiune puse
la dispozitie de Sails.Js. Un exemplu de
astfel de polita:
module.exports.policies = {
'UserController': true,
'HomeController': {
'*': 'isAuthenticated'
},
'CourseController': {
'*': 'isAuthenticated'
}
}
Codul de mai sus nu face altceva
decat sa ii comunice framework -ului, ca inainte de a da access mai departe
controller -ului trebuie indelinita functia isAuthenticated , ce executa o functie
de tip callback astfel:
module.exports = function(req, res, next) {
if (req.session. me) {
return next();
}
if (req.wantsJSON) {
return res.send(401);
}

// Otherwise if this is an HTML -wanting browser, do a redirect.
return res.redirect('/login');
};
Se verifica daca avem setata variabila de sesiune me ce se refera la id -ul
utilizatorului autentificat, in caz afirmativ, functia data ca parametru in locul
Figura 18 – Pagina de login

44 variabilei next este executata. In cazul nostru functia urmatoare este defapt
actiunea care a fost ceruta de catre utilizator.
Functia care se ocupa de autentificarea util izatorilor se gaseste in
controller -ul pentru User, conventional denumit UserController.js
login: function (req, res) {
var email = req.param('email');
var password = req.param('password');

User.findOne({
email: email
}).then(fun ction(User) {
if (!User) {
return res.view('user/login', { errors: 'Invalid
username or password', layout: false });
}
if (bcrypt.compareSync(password, User.password)) {
req.session.me = User.id;
req.session.name = Us er.firstname;
req.session.role = User.role_id;
return res.redirect('/');
} else {
return res.view('user/login', { errors: 'Invalid
username or password', layout: false});
}
}).catch(function(error) {
return res.n egotiate(error);
});
},

Toate actiuniile unui controller din Sails, primesc ca parametrii 2
variabile req si res. Aceste 2 variabile vor contine toate metodele de care vom
avea nevoie pentru a negocia un apel catre aplicatia noastra. Folosind functi a
.param() se citesc toti parametrii trimisi prin cererea HTTP care a fost faculta
atunci cand s -a accesat pagina, framework -ul deserializand mesajul de orice tip
ar fi acesta. In continuare se foloseste functia predefinita de ORM findOne() ce
returneaza u n obiect asemenea modelului nostru de User daca se vor gasi
rezultate folosind parametrii ce i se dau. In cazul in care nu a fost gasit nici un
utilizator in baza de date cu email -ul introdus de utilizator in campul de pe

45 prima pagina, acesta este redirect ionat catre pagina de login si se ataseaza
respunsului un mesaj de eroare ce va fi afisat pe ecran.
In cazul in care se gaseste un rand valid in baza de date, se trece la
urmatorul pas in autentificare si anume validarea parolei. Pentru encriptarea
parolel or s-a folosit algoritmul bcrypt cu care se compara hash -ul gasit in baza
de date cu textul introdus de utilizator, in urma creari hash -ului pentru acesta.
Daca parola a fost validata atunci se redirectioneaza utilizatorul in aplicatie.

6.1.1 Gestionarea profes orilor

Figura 19 – Pagina de gestionare a profesorilor
In Figura 18 se exemplifica o captura de ecran care demonstreaza natura
responsiva la rezolutie a aplicatiei. Aceasta captura de ecran a fost facuta
simuland dimensiunile un ei tablete conventionale in raport cu Figura 17 ce
exemplifica vizualizarea paginii de pe un smartphone.
Gestionarea profesorilor se poare realiza numai de catre un utilizator cu
rolul de administrator si se exemplifica pagina vazuta cum este vazuta de
acesta. Din meniu observam ca rolul de administrator are access de a vizualiza,
modifica si sterge orice entitate.

46 Prin actionarea butonului de Adauga un profesor utilizatorului îi este
prezentat un ecran cu un formular prin care se pot introduce toate date le
necesare pentru a crea entitatea de tipul Profesor , cum se poate vedea in Figura
19:

Figura 20 – Formular de creare a profesorilor cu validare
Codul care se ocupa cu prelucrarea datelor introduse de administrator in
acest form ular se poate gasi in controller -ul numit ProfessorController.js . Ruta
care se ocupa cu transmiterea datelor din formular -ul nostru catre controller –
ul aferent este definita astfel:
'post /professors': 'ProfessorController.create',
Se poate observa, din ru ta, ca atunci cand se acceseaza resursa
“/professors” folosind metoda HTTP POST, suntem directionati in aplicatie
catre actiunea de create. Fiind entitatea cu cele mai multe legaturi din sistem,
profesorul are un proces de inserare in baza de date mai comp lex decat celelalte
entitati. Functia create() are urmatorul cod:
create: function(req, res) {
var firstname = req.param('firstname');
var lastname = req.param('lastname');
var email = req.param('email');
var password = req.param('passwor d');

47 var title = req.param('title');
var description = req.param('description');
var photo_url = req.file('photo');
if (photo_url) {
var filename = (Math.random() * (1000 – 1) + 1) +
req.file('photo')._files[0].stream.filename;
photo_url.upload({
dirname: require('path').resolve(sails.config.appPath,
'assets/images/upload'),
saveAs: filename
}, function(error, uploadedFile) {
if (!error) {
var salt = bcrypt.genSaltSync(4);
var hash = bcrypt.hashSync(password, salt);
User.create({
firstname: firstname,
lastname: lastname,
email: email,
password: hash,
role_id: 2
}, function(error, user) {
var knex = sails.config.knex;

knex('professor').insert({
title: title,
description: description,
photoUrl: filename,
user_id: user.id
}).then(function (result, error) {
if (result[0]) {
return res.redirect('/professors');
} else {
return res.negociate(error);
}
});
});
}
});
}
}

48
Prima parte a functiei, se ocupa de citirea datelor din formular care sunt
trimise atunci cand se apasa butonul de Adauga . Singura functie speciala
folosita aici este functia de .file() . Aceasta functie se ocupa cu citirea fisierului
atasat formularului si trans formarea lui intr -un obiect JavaScript prin care
putem accesa metode speciale de lucru cu fisiere. Dupa citirea datelor fisierului
si generarea unui nume aleator pentru unicitate, acesta se copiaza in aplicatie,
folosind metoda de upload() si apoi se creaz a userul folosind datele
receptionate. Inserarea unui rand nou in tabelul professor se face doar daca
codul a trecut de celelalte procese la care este supus, prin metoda:
knex('professor').insert({
title: title,
description: description,
photoUrl: filename,
user_id: user.id
})
Odata creat, noua noastra resursa va aparea in lista profesorilor si putem
incepe asocierea cu una sau mai multe materii. Prin actionarea butonului
Asociaza materii se deschide un nou formular, unde se itereaza mat eriile
existente in aplicatie fiecare avand o bifa de unde se poate face asocierea. Ruta
care ne directioneaza catre modulul JSON -RPC care se ocupa cu crearea legaturii
profesor – materie este descrisa astfel:
'post /professor/course' : 'ProfessorControlle r.associateCourse',
Functia care se ocupa de asociere se gaseste tot in controller -ul pentru
profesori si arata in felul urmator:
associateCourse: function(req, res) {
var professorId = req.param('professor');
var courses = req.param('course');

if (!courses.length || !professorId) {
return res.view('professor/list');
}

var knex = sails.config.knex;

_.each(courses, function(value, key) {

49 knex('course').where('id', '=', value).update('professor_id',
professorId).th en(function() {
return res.view('professor/list');
});
});
}
Parametrul receptionat din cererea HTTP courses este un vector ce contine
toate id -urile selectate din formular. Pentru fiecare id gasit, se face un apel
catre baza de date de tipul Update , care modifica materiile existente din sistem
si completeaza coloana de professor_id cu id -ul profesorului selectat.

Figura 21 – Formular de asociere materii vizualizat pe un smartphone

50 6.2 Adaugarea chestionarelor si intrebarilor

Dupa ce au fost create asocierile profesor – materie, puten trece la
urmatoarele entitati din sistem si anume chestionarele si intrebarile. Pentru
fiecare materie se poate crea un numar nelimitat de chestionare lasand
libertatea profesorilor de a decide folosirea lor. La randul lor chestionarele vor
avea o serie de intrebari personalizate, administratorul putand introduce un
numar nelimitat de texte ce vor fi interpretate ca si intrebari.
Pentru raspunsuri s-a folosit o notatie standard cu nu mere de la 1 la 5,
pentru a simplifica procesul de creare a intrebarilor, astfel administratorul nu
trebuie decat sa introduca textul ce va fi afisat ca si intrebare studentilor fara
sa introduca si raspunsurile ce vor fi asociate cu textul respectiv. Toto data cele
5 valori de raspuns pot fi interpretate intr -un mod general (ex: multumit, foarte
bine, nemultumit, etc.) in functie de intrebare.
Dupa crearea unui chestionar prin apasarea butonului editeaza intrebari
utilizatorul va fi prezentat cu urmatoare p agina:

Figura 22 – Pagina de editare a intrebarilor

51
Pe aceasta pagina avem un exemplu de cod JavaScript care nu se adreseaza
serverului, ci browser -ului (client). Acest cod gestioneaza in timp real lista de
intrebari care este a fisata pe ecran. Acest modul este implementat folosind
tehnica Model – View – View – Model pusa la dispozitie de biblioteca de functii
Knockout JS. Aceasta arhitectura de cod se foloseste de un design pattern
cunoscut in software: Observer Pattern. Practic se creeaza un obiect de tip
Observable care atunci cand este modificat notifica elementele care au fost
inregistrate ca depinzand de obiect, acestea schimband -usi valoarea in timp
real.
surveyQueryString = window.location.href.split("?")[1].split("=") [1];

var Question = function(text) {
this.name = ko.observable(text);
}

var QuestionListModel = function () {
this.surveyId = ko.observable(surveyQueryString);
this.questionText = ko.observable("");
this.allItems = ko.observableArray([]);
this.selectedItems = ko.observableArray([]);
this.addItem = function () {
if ((this.questionText() != "") &&
(this.allItems.indexOf(this.questionText()) < 0))
this.allItems.push(new Question(this.questionText()));
this.questionText("");
};

this.removeSelected = function () {
this.allItems.removeAll(this.selectedItems());
this.selectedItems([]);
};
};

var viewModel = new QuestionListModel();

ko.applyBindings(viewModel);

52 Ca si logica de pe server, acest cod se foloseste de modele pentru a descrie
obiectele cu care se lucreaza. In cazul de fata avem modelul simplu Ouestion
care este f olosit pentru a stoca textul intrebarii. In momentul cand se actioneaza
butonul de adaugare intr -un vector care va tine toate intrebarile se adauga un
nou obiect de tip Question .
Legatura cu elementele HTML din pagina se face cu ajutorul unor atribute
speciale numite bindings (legaturi) . Acestea au o sintaxa speciala prin care se
pot manipula atributele sau valorile elementelor asociate, in timp real. Aceasta
tehnica este folosita in aplicatii web pentru a simula o aplicatie de tip desktop
care face apel la memoria calculatorului pentru a stoca temporar date cu care
se poate lucra in timp real.
<form action="/questions" method="post">
<input type="hidden" name="survey" data -bind="value: surveyId()">
<div data -bind="foreach: allItems">
<input type="hidden" name="questions[]" data -bind="value: name">
</div>
<button type="submit" class="btn btn -success">Salveaza</button>
</form>
In codul de mai sus, avem un formular HTML cu elemente ascunse unde
se tin toate valorile intrebarilor. Se fo loseste si o tehnica de sablon, care ne
permite sa iteram prin -un obiect sau vector si sa afisam o serie de elemente
avand acceasi structura. Acest lucru este posibil legaturii speciale pusa la
dispozitie de KnockoutJS data-bind="foreach: allItems" . Se poa te observa ca
aceasta tehnica reduce considerabil codul care ar fi fost necesar pentru ca logica
sa functioneze in felul acesta. La actionarea butonului de Salveaza , se ajunge in
controller -ul QuestionController.js in actiunea de save() :
save: function(re q, res) {
var survey = req.param('survey');
var questions = req.param('questions');
var knex = sails.config.knex;

_.each(questions, function(value, key) {
knex('question').insert({survey_id: survey, text:
value}).then(function(result, error) {
return res.redirect('/surveys');
});
});

53 }
Ca si in cazul asocierii materiilor, se itereaza prin valorile receptionate
prin cererea HTTP cu mentiunea ca in cazul de fata se genereaza o metoda de
tipul SQL Insert pentru baza de date.

6.3 Crearea stud enților si serviciu de email

Ultimul rol de implementat in aplicatie este acela de student. Pentru
acestia s -a implementat un serviciu de email, folosind un produs gratis Mailgun .
Acest serviciu necesita configurare separata si creearea unui cont in platf orma
Mailgun, in urma careia se va introduce o cheie de autentificare in sistemul
nostru. Modulul de configurare se gaseste in fisierul email.js din folderul config
al framework -ului:
module.exports.email = {
service: 'Mailgun',
auth: {
user: 'postmast er@sandboxc9e9ef50c.mailgun.org',
pass: '7a30fdf1a303bc64b160'
},
templateDir: 'api/emailTemplates',
from: 'portal@upit.ro',
testMode: false,
ssl: true
}
Aici se defineste contul ce va fi folosit pentru autentificarea in sistemul
Mailgun precum si expeditorul care se va introduce in sabloanele de email.
Serviciul de trimitere al email -ulilor utilizeaza sabloane HTML pentru a crea
textul si foloseste variabile pentru a personaliza continutul:
module.exports.studentEmail = function(obj) {
sails.hooks .email.send(
'welcomeEmail',
{
Name: obj.name,
Email: obj.email,
Password: obj.password
},

54 {
to: obj.email,
subject: 'Welcome Email'
},
function(err) {
console.log(err || 'Mail Sent!');
});
}

<p>Salut <%= Name %>,</p>
<br/>
<p>Ti s-a creat un cont pentru platforma de evaluare a profesorilor .
Acest cont va fi valabil o luna dupa care va fi dezactivat. </p>
<p>Te poti loga cu: </p>
<p>User: <%= Email %></p>
<p>Parola: <%= Password %></p>
<p><a href="http://localhost">Login</a>< /p>

Apelarea serviciului:
Mailer.studentEmail({email: email, name: firstname + ' ' + lastname,
password: password});

Serviciul este apelat de functia care creeaza campul in baza de date,
folosind datele din formularul de adaugare a unui student. Fata de restul
actorilor din sistem, studentul este singura entitate a carei parola ramane
anonima, aceasta fiind generata o data cu crearea entitatii si transmisa prin e –
mail. Figura urmatoare evidentiaza acest lucru:

55
Figura 23 – Formul ar de adaugare a unui student

6.4 Evaluarea chestionarelor

6.4.1 Interfata pentru utilizator adaptabila rezolutiei

Un aspect important al aplicatiei de evaluarea a profesorilor de catre
studenti este acela ca interfata este gandita pentru a se adapta rezolutiei d evice –
ului de pe care este folosita. Acest lucru face posibila navigarea in sistem cu
usurinta pe un ecran mic, asemenea unui smartphone tot mai mult folosit in
randul studentilor pentru a naviga pe internet. Pentru a reduce cantitatea de
cod necesara desc rierii unei astfel de interfete, aplicatia foloseste o suita de
elemente puse la dispozitie de biblioteca Twitter Bootstrap 3.
Aceasta suita de elemente predefinite fac posibila refolosirea
componentelor pentru a crea interfete de utilizator foarte eficie nte din punct de
vedere al functionarii cat si al accesibilitatii.
Adaptabilitatea interfetei in functie de rezolutie este posibila prin
utilizarea propietatilor speciale introduse in CSS3 numite media queries . Aceste
functii speciale descriu modul in care se aplica anumite propietati elementelor
de HTML, in functie de parametrii dati. Spre exemplu:
@media (m ax-width: 768px) {
.lead {

56 font-size: 22.5px;
}
}
Acest cod face ca textul aflat in elementul descris de clasa HTML lead sa
isi schimbe dimnesi unea, atunci cand pagina este vizualizata de pe un ecran de
maxim 768px .
Folosind aceasta tehnica s -a implementat o interfata adaptabila ce
usureaza folosirea aplicatiei de pe orice device, dandule posibilitatea
studentilor de a naviga sistemul fara constr angeri.

Figura 24 – Chestionar vizualizat pe o tableta

57
Figura 25 – Chestionar vizualizat pe un smartphone

6.4.1 Logica evalu ării

Dupa crearea contului si primirea email –
ului, studentii se pot autentifica in sistem.
Prima pagina care este prezentata (Figura 25)
contine lista cu toate chestionarele din sistem
la care acestia nu au raspuns. Aceasta lista este
obtinuta printr -o interogare la baza de date ce
exclude din lista materiilor asociate cu unul sau
mai multe chestionare din sistem, toate
chestionarele la care studentul a raspuns
facand apel la tabelul student_survey .
Figura 26 – Lista chestionarelor

58 knex('course')
.innerJoin('survey', 'survey.course_id' , 'course.id')
.whereNotIn('survey.id', surveyIds).then(function(result, error) {
return res.view('student/studentsurveys', {surveys: result});
});

Prin selectia unei materii din lista, studentii sunt prezentati cu un set de
intrabari dupa cum se poate observa in Figura 24. Acest set contine pe langa
intrebari, o bara de numere reprezentand scorul asociat fiecarei intrebari, care
atunci cand este actionat ramane selectat si se trimite mai departe in metoda
JSON -RPC /submitsurvey .
Metoda submitSurvey() va citi un vector de valori ce contin scorul fiecarei
intrebari in parte si va calcula punctajul total facand media tuturor intrebarilor,
astfel:
submitSurvey: function(req, res) {
var questions = req.param('question');
var questionIds = req.param('quest ionId');
var studentId = req.param('studentId');
var surveyId = req.param('surveyId');

var totalRating = 0;
for (var i = 0; i < questions.length; i++) {
totalRating += parseInt(questions[i]);
}
totalRating = totalRating / questions.length;
var knex = sails.config.knex;

knex('student_survey')
.insert({survey_id: surveyId, student_id: studentId, total_rating:
totalRating})
.then(function(result, error) {
return res.redirect('/studentsurveys');
});
}

Datele se salveaza in tabelul student_s urvey si studentul este
redirectionat la pagina principala, de unde se va exclude automat chestionarul

59 completat, bazat pe logica descrisa anterior . Daca studentul nu va mai avea
chestionare de completat acesta va fi avertizat prin -un mesaj.

6.5 Vizualizarea rapoartelor

Odata completate, evaluarile studentilor vor fi stocate in baza de date si
prezentate unui utilizator cu rolul de profesor. Utilizatorul de tip profesor va
avea acces doar la rapoartele chestionarelor asociate cu materiile pentru care
s-au fac ut legaturi prin metoda prezentata in capitolul 6.1.1.
Pentru a obtine aceste rapoarte se face o serie de interogari la baza de
date in urma carora se va obtine un obiect JavaScript ce va contine punctajul
mediu obtinut pentru fiecare materie asociata cu utilizatorul autentificat.
Profesorul nu are acces la datele stocate pe server si nu poate manipula in nici
un fel platforma pentru a obtine informatii la care nu au acces predefinit.
knex('student_survey').innerJoin('survey', 'survey.id',
'student_survey .survey_id').whereIn('survey.course_id',
courseIds).then(function(result, error) {

var courseReport = _(result).groupBy(x => x.course_id).value();
var viewData = new Array();
_.each(courseReport, function(value, key) {
var sum = 0;
var counter = 0;
_.each(value, function(rating, key) {
sum += parseFloat(rating.total_rating);
counter++;
});
viewData.push({courseName: courses[key], courseId: key,
totalQuestions: counter, totalRating: sum / counter});
});

return res.view({reports: vie wData});
});

In prezentul cod putem observa algoritmul prin care se calculeaza
punctajul obtinut pentru fiecare materie cat si numarul de chestionare care au

60 fost evaluate de studenti. Pentru a avea un obiect asupra caruia se poate intera
o singura data f ara a fi nevoie de mai multe bucle, folosim functia predefinita
in Sails .groupBy() . Aceasta functie este asemanatoare functiei de grupare in
SQL, insa foloseste un algoritm recursiv pentru a grupa obiectul dupa filtrul
care este trimis ca parametru. Obiec tul procesat este un obiect de tip JSON care
arata in felul urmator:
[
{
"courseName": "Proiectare Logica",
"courseId": "6",
"totalQuestions": "2",
"totalRating": "4.375"
},
{
"courseName": "Programare Orientata pe Obiecte",
"courseId": "8",
"totalQuestions": "2",
"totalRating": "4.75"
}
]
In partea de interfata cu utilizatorul acest obiect este preluat, se itereaza
elementele vectorului si sunt populate elementele de tip HTML folosind tehnica
de sabloane puse la disp ozitie de framwork -ul Sails.js.
<ul class="list -group">
<% reports.forEach(function(report) {%>
<li class="list -group-item">
<strong><%= report.courseName %></strong>: punctaj <span
class="text -success"><strong><%= report.totalRating %></strong></spa n>
bazat pe <%= report.totalQuestions %> chestionar(e)
</li>
<% }); %>
</ul>

Rezultatul obtinut in urma procesarii informatiei obiectului ce contine
informatia rapoartelor, se poate observa in pagina de rapoarte care ii este
accesibila profesorului au tentificat in sistem in urmatoarea figura:

61
Figura 27 – Vizualizarea rapoartelor

62 7. Rezultate experimentale

7.1 Tratarea erorilor

Pentru a observa functionarea corecta a aplica tiei si pentru a testa
sistemul impotriva efectelor secundare in cod, s -au efectuat verificari folosind
standard -ul care este pus la dispozitie programatorilor de framework -ul
Sails.Js, care populeaza un obiect de tip Exceptie cu erorile aruncate de functiile
folosite. O regula importanta in trata rea erorilor prin parametrii functiei este
consistenta ordinii parametrilor, de accea in mediul JavaScript, de -a lungul
timpului s -a dezvoltat un standard de a pune obiectul ce contine erorile ca prim
parametru, ca de exemplu:
photo_url.upload({
dirname: 'assets/images/upload',
saveAs: filename
}, function( error, uploadedFile ) {
if (error) return res.negotiate(error);
}
Framework -ul se ecupa de negocierea erorilor in felul urmator:
try {
statusCode = err.status || 500;
res.status(statusCode);
} catch (e) {}
if (statusCode === 403) return res.forbidden(body);
if (statusCode === 404) return res.notFound(body);
if (statusCode >= 400 && statusCode < 50 0) return res.badRequest(body);
return res.serverError(body);
Se observa ca se face apel la coduril e HTTP care sunt folosite in mod
standardizat pentru a trata diferitele cazuri de mesaje si sunt deseori foarte
explicite in a da informatii aditionale pentru a depista bug -urile din sistem.
Rolul cel mai important in sistem il detine persistenta datelor, aici facem
referire la sistemul de gestionare a bazelor de date MySQL. Prin arhitectura
bazei de date, s -au realizat legaturi si functii pentru a garanta consistenta
datelor si livrarea optima a acestora, insa datele care vin din partea de interfata

63 cu uti lizatorul nu sunt garantate a fi veridice deoarece poate intervine eroarea
umana.
Codul trateaza si aceste cazuri,
pe cat posibil, pentru a nu primi
formulare goale sau date care nu
sunt potrivite campului sau coloanei
completate. Un astfel de exemplu ar
putea fi o adresa de email invalida.

7.2 Testare functionala

Indata ce utilizatorii posteaza un formular din sistem, cum ar fi spre
exemplu chestionarul , functia JSON -RPC care este apelata se va ocupa de
salvarea datelor in sistemul de gestiune al bazei d e date. Pe langa tratarea
erorilor se verifica daca datele au ajuns catre serverul MySQL si daca acestea
au fost salvate corect.
Pentru a demonstra tehnica
folosita in testarea functionala vom
folosi ca exemplu crearea unei
entitati de tip Profesor . In ac easta
demonstratie am folosit o aplicatie
web pentru a vizualiza baza de date
pentru o testare mai rapida. O alta
metoda de a verifica daca datele
trimise de utilizator catre server au
ajuns in baza de date este crearea de
scripturi SQL ce returneaza datel e
din tabelele urmarite.

Figura 28 – Validarea email -ului

64
Dupa cum se poate observa in capturile de ecran, date ajung corect in
tabele din baza de date. Acest proces s -a efectuat in mod repetat pentru fiecare
entitate care populeaza baza de date.

65 8. Concluzii

Prin lucrarea de față se demonstrează crearea unui sistem informatic web
ce iși propune să faciliteze interacțiunea student – profesor prin implementarea
unei aplicații de evaluare a profesorilor folosind chestionare personalizate prin
care stundenții pot con tribui la perfecționarea programei și a procesului
didactic din cadrul facultății.
Demersul parcurs în dezvoltarea codului începe prin identificarea
scenarilor de utilizare a aplicației ce crează o legătura cu determinarea actorilor
din sistem, realizarea conexiunilor necesare comunicării dintre actori si entități
prin implementarea interfețelor de utilizator specifice fiecărei resurse în parte,
interconectarea diferitelor tehnologii utilizate (exemplu: logica clientului cu
logica de stocare a datelor in s istemul de gestiune a bazelor de date), realizarea
interacțiunilor personalizate in funcție de rolurile asociate fiecărei entități.
Principalele caracteristici care asigură succesul implementării acestui
sistem informatic au fost definite de -a lungul unui proces de cercetare a mai
multor soluții practice posibile și prin respectarea unor standarde formate în
timp prin experiența empirică dobândită în urma utilizării proceselor ce stau la
baza internetului. Aceste caracteristici definitorii pentru aplicația prezentata
sunt:
• Independen ța arhitecturii clientului de aplicaț ia de tip server
• Timpi de încărcare optimi
• Comunicare în timp real cu server -ul
• Design adaptabil dimensiunilor dispozitivului folosit
• Scalabilitatea verticală a serviciului web
• Securizarea și asigurarea confidențialitătii prin limitarea accesului
• Simplificarea gestionarii evaluarilor cadrelor didactice

În urma descrierii detaliate a logicii si interconexiunii serviciilor, am
demonstrat prin rezultatele experimentale obținute că sistemul indepl inește un
standard de definire a unei aplicații web capabilă sa asigure funcționalitatea
codului în mod persistent, fară intreruperi. Aceste aspecte definesc un sistem
complex, care simulează interacțiunea reală a entităților analizate, totodată
modelând r esursele necesare acestei interacțiuni.

66 Eficiența soluției constă în faptul c ă sistemul informatic implementat
reprezintă o alternativă modernă și imperios necesară în soluționarea
interacțiunii de evaluare la modelele tradiționale de creare a statisticilo r. Într –
o eră a vitezei de circulație a informației, aplicația de față constituie un model
eficient de transmitere a datelor care formează un sistem de evaluare in timp
real, ce reduce considerabil efortul depus pentru a susține un astfel de sistem,
totodată garantând o corectitudine sporită în desfăsurarea procesului de
evaluare și statistică.
Aplicația, deși este un sistem suficient pentru a răspunde necisității unei
astfel de platforme, permite posibilitatea dezvoltării soluției implementate prin
integrarea diferitelor sisteme informatice dintr -o facultate, integrare ce se
poate realiza ușor extinzând logica ce pune la dispoziție metodele de gestiune a
resurselor prin module JSON -RPC. Aceste integrări pot folosi și pot implementa
acțiunile prezentate d in afara contextului serverului pe care aplicația rulează.
Pentru ca profesorii să utilizeze optim informațiile pe care evaluarea
chestionarelor de către stundeți le -o poate oferi și pentru ca aplicația să -și
atingă rezultatul in legătura cu mai multe sfer e de interes, se poate recurge la
o extindere a domeniului de rapoarte ce se generează atunci când un chestionar
este completat. O astfel de funcționalitate poate fi atinsă prin crearea unui tabel
adiacent unde se va mentine un istoric al întrebărilor și v aloarea numerică cu
care au fost completate. Logica rapoartelor va face o medie pentru fiecare
întrebare asociată chestionarului și implicit materiei vizualizate de către
profesor. Față de un punctaj total calculat, aceasta tehnică permite profesorului
să vizualizeze direct opinia colectivă a studenților asupra unui aspect singular,
ceea ce permite o reacție mai precisă care să satisfacă standardul urmărit,
îmbunătățirea calității activității didactice.

67 9. Bibliografie

Lucrari de Specialitate
1. Mike McNe il si Irl Nathan , Sails.js in action, ed. Manning Publications
2. Kyle Simpson , "You Don't Know JS" , O'Reilly Media (lansata gratuit in
domeniul public)
3. David Gourley si Brian Totty , HTTP: The Definitive Guide , O'Reilly Media
Surse Internet
1. https://developer.mozilla.org/en -US/docs/Web/JavaScript
2. https://dev.mysql.com/doc/
3. http://sailsjs.com /documentation/reference
4. https://developer.mozilla.org/ro/docs/Web/JavaScript/Reference/Globa
l_Objects/JSON

68
10. Anexe

Configurarea conexi unii la baza de date in framework -ul web:
module.exports.connections = {
mysqlConnection: {
adapter: 'sails -mysql',
host: 'local host',
user: 'root', //optional
password: '', //optional
database: 'lic' //optional
},
};

Rutele folosite in sistem:
module.exports.routes = {
'get /': 'HomeController.index',

'get /courses': 'CourseController.list',
'post /courses': 'CourseController.create',

'get /professors': 'ProfessorController.list',
'post /professors': 'ProfessorController.create',
'post /professor/course' : 'ProfessorController.associateCourse',

'get /surveys' : 'SurveyController.list',
'post /surve ys': 'SurveyController.create',
'get /surveys/edit': 'QuestionController.list',

'post /questions': 'QuestionController.save',

'get /students': 'StudentController.list',
'post /students': 'StudentController.create',
'get /studentsurveys': 'Stude ntController.availableSurveys',
'get /takesurvey': 'StudentController.takeSurvey',
'post /submitsurvey': 'StudentController.submitSurvey',

69
'get /report': 'ReportController.index',

'get /login': {
view: 'user/login',
locals: {
layout: false
}
},

// WebAPI
'get /allcourses': 'ApiController.getCourses',
'get /allprofessors': 'ApiController.getProfessors',
'get /allsurveys': 'ApiController.getSurveys',

// Endpoints
'post /login': 'UserController.login',
'/logout': 'UserController.logout',
};

Controllerele esentiale folosite in aplicatie:
User Controller .js:
var bcrypt = require('bcrypt -nodejs');

module.exports = {
/**
* `UserController.login()`
*/
login: function (req, res) {

var email = req.param ('email');
var password = req.param('password');

User.findOne({
email: email
}).then(function(User) {
if (!User) {
return res.view('user/login', { errors: 'Invalid
username or password', layout: false });

70 }
if (bcrypt.compareSync(password, User.password)) {
req.session.me = User.id;
req.session.name = User.firstname;
req.session.role = User.role_id;
return res.redirect('/');
} else {
return res.view('user/login', { error s: 'Invalid
username or password', layout: false});
}
}).catch(function(error) {
return res.negotiate(error);
});
},

/**
* `UserController.logout()`
*/
logout: function (req, res) {

// "Forget" the user from the se ssion.
// Subsequent requests from this user agent will NOT have
`req.session.me`.
req.session.me = null;

// If this is not an HTML -wanting browser, e.g.
AJAX/sockets/cURL/etc.,
// send a simple response letting the user agent know the y were
logged out
// successfully.
if (req.wantsJSON) {
return res.ok('Logged out successfully!');
}

// Otherwise if this is an HTML -wanting browser, do a redirect.
return res.redirect('/');
},

/**
* `UserController.s ignup()`

71 */
signup: function (req, res) {

// Attempt to signup a user using the provided parameters
User.signup({
name: req.param('name'),
email: req.param('email'),
password: req.param('password')
}, function (err, user) {
// res.negotiate() will determine if this is a validation
error
// or some kind of unexpected server error, then call
`res.badRequest()`
// or `res.serverError()` accordingly.
if (err) return res.negotiate(err);

// Go ahead and log this user in as well.
// We do this by "remembering" the user in the session.
// Subsequent requests from this user agent will have
`req.session.me` set.
req.session.me = user.id;

// If this is not an HTML -wanting browser, e.g.
AJAX/sockets/cURL/etc.,
// send a 200 response letting the user agent know the signup
was successful.
if (req.wantsJSON) {
return res.ok('Signup successful!');
}

return res.redirect('/');
});
}
};

SurveyController.js:
var knexnest = require('knexnest');

module.exports = {

72 list: function(req, res) {
return res.view();
},

create: function(req, res) {
var course = req.param('course');

var knex = sails.config.knex;

knex('survey').in sert({course_id:
course}).then(function(result, error) {
console.log(result);
return res.redirect('/surveys');
});
}
};

StudentController.js:
var bcrypt = require('bcrypt -nodejs');
var _ = require('lodash');

module.exports = {
list: function(re q, res) {

var knex = sails.config.knex;

knex('user').select('firstname', 'lastname',
'email').where('role_id', '=', 3).then(function(result, error){
return res.view({students: result});
});
},

create: function(req, res) {
var firstname = r eq.param('firstname');
var lastname = req.param('lastname');
var email = req.param('email');

var password = (Math.random() * (1000 – 1) + 1) + email;

73 var salt = bcrypt.genSaltSync(4);
var hash = bcrypt.hashSync(password, salt);

User.creat e({
firstname: firstname,
lastname: lastname,
email: email,
password: hash,
role_id: 3
}, function(error, user) {
console.log(password);
Mailer.studentEmail({email: email, name: firstname + ' ' +
lastname, pas sword: password});
return res.redirect('/students');
});
},

availableSurveys: function(req, res) {
var knex = sails.config.knex;

knex('student_survey').select('survey_id').where('student_survey.st
udent_id', '=', req.session.me).then(funct ion(result) {
var surveyIds = new Array();

_.each(result, function(value, key) {
surveyIds.push(value.survey_id);
});

knex('course').innerJoin('survey', 'survey.course_id',
'course.id').whereNotIn('survey.id', surveyIds).then(function(res ult,
error) {
console.log(result);
return res.view('student/studentsurveys', {surveys:
result});
});
});
},

takeSurvey: function(req, res) {

74 var surveyName = req.param('survey_name');
var surveyId = req.query.id;
var knex = sails.conf ig.knex;

knex('question').select('*').where('survey_id', '=',
surveyId).then(function(result, error) {
return res.view({questions: result, surveyId: surveyId,
surveyName: surveyName});
});
},

submitSurvey: function(req, res) {
var questions = req.param('question');
var questionIds = req.param('questionId');
var studentId = req.param('studentId');
var surveyId = req.param('surveyId');

var totalRating = 0;

for (var i = 0; i < questions.length; i++) {
totalRating += parseInt(questio ns[i]);
}
totalRating = totalRating / questions.length;

var knex = sails.config.knex;

knex('student_survey').insert({survey_id: surveyId,
student_id: studentId, total_rating: totalRating}).then(function(result,
error) {
return res.redirect('/s tudentsurveys');
});

}
};

ProfessorController.js:
var knexnest = require('knexnest');
var _ = require('lodash');

75 module.exports = {

list: function(req, res) {
return res.view();
},

associateCourse: function(req, res) {
var professorId = req.param('professor');
var courses = req.param('course');

if (!courses.length || !professorId) {
return res.view('professor/list');
}

var knex = sails.config.knex;

_.each(courses, function(value, key) {
knex('cour se').where('id', '=', value).update('professor_id',
professorId).then(function() {
return res.view('professor/list');
});
});
},

create: function(req, res) {
var firstname = req.param('firstname');
var lastname = req.param('l astname');
var email = req.param('email');
var password = req.param('password');
var title = req.param('title');
var description = req.param('description');
var photo_url = req.file('photo');

if (photo_url) {
var filename = (Math.random() * (1000 – 1) + 1) +
req.file('photo')._files[0].stream.filename;
photo_url.upload({
dirname: require('path').resolve(sails.config.appPath,
'assets/images/upload'),
saveAs: filename

76 }, function(error, uploadedFi le) {
if (!error) {
var salt = bcrypt.genSaltSync(4);
var hash = bcrypt.hashSync(password, salt);
User.create({
firstname: firstname,
lastname: lastname,
email: email,
password: hash,
role_id: 2
}, function(error, user) {
var knex = sails.config.knex;

knex('professor').insert({
title: title,
description: description,
photoUrl: filename,
user_id: user.id
}).then(function(result, error) {
if (result[0]) {
return res.redirect('/professors');
} else {
return res.negociate(error) ;
}
});
});
}
});
}
}
}

Modele ORM:
User.js
bcrypt = require('bcrypt -nodejs');

module.exports = {

77 attributes: {
firstname: {
type: 'string'
},
lastname: {
type: 'string'
},
email: {
type: 'email',
required: true,
unique: true
},
password: {
type: 'string',
required: true
},
role_id: {
type: 'int'
},
toJSON: function() {
var obj = this.toObject();
delete obj.password;
return obj;
}
}
};

Sabloane HTML:
Professor/list.ejs
<div class="page -header">
<div class="col -xs-12 col-sm-4">
<h1> Profesori </h1><small>Lista profesorilor din
sistem</small>
</div>
<div class="col -xs-12 col-sm-8 text-right">
<button type="button" class="btn btn -default" data –
toggle="modal" data -target="#new -professor">
Adauga un profesor
</button>

78 </div>
</div>

<!– Horizontal Line –>
<div class="row">
<div class="col -sm-10 col-sm-offset-1">
<hr>
</div>
</div>

<ul class="list -group" data -bind="foreach: { data: $data.professors, as:
'professor' }">
<li class="list -group-item">
<div class="modal fade" data -bind="attr: { id: 'professor' +
professor.id() }">
<div class="modal -dialog" role="documen t">
<div class="modal -content">
<div class="modal -header">
<button type="button" class="close"
data-dismiss="modal" aria -label="Close"><span aria –
hidden="true">&times;</span></button>
<h4 class="modal -title"
id="myModalLabel">Asociaza materii</h4>
</div>
<div class="modal -body">
<form class="form -horizontal"
method="post" action="/professor/course">
<input type="hidden"
name="professor" data -bind="value: professor.id">
<div class="form -group col -xs-12">
<div data -bind="foreach: {
data: viewModel.courses.courses, as: 'course' }">
<div class="checkbox">
<label>
<input
type="checkbox" name="course[]" data -bind="value: course.id, checked:
course.professor_id() == professor.i d()">
<span data –
bind="text: course.name"></span>
</label>

79 </div>
</div>
</div>
<button type="submit" class="btn
btn-primary">Salveaza</button>
</form>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col -xs-2">
<img class="img -responsive img -circle" data –
bind="attr: { src: '/images/upload/' + professor.photoUrl() }">
</div>
<div class="col -xs-5">
<h4><span data -bind="text:
professor.title"></span>&nbsp;<stro ng data-bind="text:
professor.firstname() + ' ' + professor.lastname()"></strong></h4>
<small data -bind="text:
professor.description"></small>
</div>
<div class="col -xs-5">
<p class="text -right">
<button type="button" class="btn btn -default
btn-xs" data -toggle="model" data -bind="attr: { 'target': 'professor' +
professor.id() }, click: function(data, e) { $('#' +
$(e.target).attr('target')).modal('show'); }">Asociaza Materii</button>
</p>
<p class="text -right">
<button type="b utton" class="btn btn -primary
btn-xs">Editeaza</button>
</p>
<p class="text -right">
<button type="button" class="btn btn -danger
btn-xs">Sterge</button>
</p>
</div>
</div>
</li>

80 </ul>

<!– Modal –>
<div class="modal fade" id="new -professor" tabindex=" -1" role="dialog"
aria-labelledby="myModalLabel">
<div class="modal -dialog" role="document">
<div class="modal -content">
<div class="modal -header">
<button type="button" class="close" data –
dismiss="modal" aria -label="Close"><sp an aria-
hidden="true">&times;</span></button>
<h4 class="modal -title" id="myModalLabel">Adauga un
profesor</h4>
</div>
<div class="modal -body">
<form class="form -horizontal" action="/professors"
method="POST" enctype="multipart/form -data">
<div class="form -group">
<label class="col -sm-2 control -label"
for="name"> Prenume </label>
<div class="col -sm-10">
<input type="text" class="form –
control" id="firstname" name="firstname" placeholder="Nume"
required="true">
</div>
</div>
<div class="form -group">
<label class="col -sm-2 control -label"
for="name"> Nume </label>
<div class="col -sm-10">
<input type="text" class="form –
control" id="lastname" name="lastname" placeholder="Prenume"
required="tr ue">
</div>
</div>
<div class="form -group">
<label class="col -sm-2 control -label"
for="name"> Email </label>
<div class="col -sm-10">

81 <input type="email" class="form –
control" id="email" name="email" placeholder="Email" req uired="true">
</div>
</div>
<div class="form -group">
<label class="col -sm-2 control -label"
for="name"> Password </label>
<div class="col -sm-10">
<input type="password"
class="form -control" id="password" name="password" pl aceholder="Password"
required="true">
</div>
</div>
<div class="form -group">
<label class="col -sm-2 control -label"
for="name"> Titlu </label>
<div class="col -sm-10">
<input type="text" class="form –
control" id="title" name ="title" placeholder="Titlu" required="true">
</div>
</div>
<div class="form -group">
<label class="col -sm-2 control -label"
for="name"> Descriere </label>
<div class="col -sm-10">
<input type="text" class="form –
control" id= "description" name="description" placeholder="Descriere"
required="true">
</div>
</div>
<div class="form -group">
<label class="col -sm-2 control -label"
for="name"> Poza </label>
<div class="col -sm-10">
<input type="file" c lass="form –
control" id="photo" name="photo" placeholder="Poza" required="true">
</div>
</div>
<input type="hidden" name="role" value="2">
<div class="form -group">

82 <div class="col -sm-offset-2 col-sm-10">
<button type="submi t" class="btn
btn-primary"> Adauga </button>
</div>
</div>
</form>
</div>
</div>
</div>
</div>

<script>

var viewModel;

$.getJSON('/allprofessors').done(function(res) {
viewModel = ko.mapping.fromJS(res);
$.getJSON('/allcourses ').done(function(res) {
viewModel.courses = ko.mapping.fromJS(res);
ko.applyBindings(viewModel);
});
});

</script>

83

84

Similar Posts