Kharmah – Aplicație Web pentru prezentarea și analiza opiniilor despre jocurile pe calculator [308882]
Kharmah – Aplicație Web pentru prezentarea și analiza opiniilor despre jocurile pe calculator
Introducere
Istoria jocurilor pe calculator datează încă din anii 1950, [anonimizat], precum și pentru a evidenția puterea pe care o poate avea calculatorul. [anonimizat] 1950 [anonimizat] (Massachussets Institute of Technology) a publicat o lucrare remarcabilă intitulată “Programming a Computer for Playing Chess” (Programarea unui calculator pentru a juca sah). Această lucrare descrie modul în care un calculator poate fi programat astfel încat să poată juca o [anonimizat]-se de o procedura de tip minimax pentru a [anonimizat] o funcție de evaluare a unei pozitii de șah.
Popularitatea jocurilor pe calculator a crescut în anii 1970-1980, [anonimizat], precum și alte controale. Unul dintre primele jocuri apărute pentru astfel de tehnologii este “Spacewar!”, un joc creat de Steve Russell împreună cu Martin Graetz și Wayne Wiitanen. Jocul prezintă lupta a [anonimizat], aflate în câmpul de gravitație a unei stele. Fiecare navă are are o cantitate limitată de combustibil pentru a fi manevrată și un numar limitat de torpile. Jocul se termină atunci când o navă este distrusă de o torpilă sau atunci cand se ciocnește cu steaua.
În secolul douazeci și unu jocurile pe calculator au atins un alt nivel. Odată cu evoluția calculatoarelor și odată cu apariția noilor console de jocuri (PlayStation, Xbox, Nintendo etc.) jocurile au devenit din ce în ce mai complexe și a început să se pună accentul atât pe complexitatea acțiunii și a [anonimizat], pe reprezentarea cât mai în detaliu a unui univers fictiv sau real. Un rol deosebit de important în dezvoltarea jocurilor l-a avut inteligența artificială. Dacă la început (anii 1950) jocurile au avut o [anonimizat], din anii 1970 au început să se creeze jocuri “single player” în care apar inamici controlați de inteligență artificială. [anonimizat], stabilită de la început. [anonimizat]-se astfel în funcție de ce alegeri se iau pe parcurs. Un exemplu reprezentativ pentru un joc cu o [anonimizat] “Dragon Age: Inquisition” creat de BioWare și publicat de Electronic Arts. Jocul urmărește povestea unui personaj denumit “Inquisitor” care încearcă să calmeze nelinistea locuitorilor de pe continentul “Thedas” în timp ce caută posibilități de a închide o gaură misterioasă în cer invocă demoni pe tot continentul.
Figure – Dragon Age Inquisition
Se observă așadar din aceasta poza diferența covârșitoare dintre jocurile “Spacewar!” și “Dragon Age: Inquisition” din punct de vedere grafic. [anonimizat] a [anonimizat] a controla acțiunile personajelor care însoțesc eroul. Acești coechipieri acționează independent de erou, ba chiar își pot schimba viziunea despre erou atunci cand acesta săvârșește anumite acțiuni care pot fi admirate sau dimpotrivă, extrem de complicate. Mai mult, jocul permite configurarea acestor coechipieri pentru a utiliza anumite abilități în anumite condiții.
În concluzie, jocurile au devenit atât de complexe și de atractive, încât nu trebuie sa ne mai mire faptul că din ce în ce mai multe persoane se “scufundă” în aceste universuri care le pun la încercare imaginația și care îi ghidează prin întâmplări imprevizibile.
Kharmah este o aplicație web internațională destinată persoanelor pasionate de jocurile pe calculator ce are drept scop, după cum se înțelege din titlul lucrării, realizarea unei analize asupra jocurilor pe calculator, mai exact informarea utilizatorilor despre cele mai importante aspecte ale acestora. Prin această prezentare se înțelege faptul că utilizatorii au acces la informații privind cerințele minime ale jocurilor, o descriere a jocului și a gameplay-ului. De asemenea, fiecare joc este prevăzut cu o secțiune de discuție asupra bug-urilor existente, precum și o sectiune în care utilizatorii pot pune întrebări despre joc in general, despre gameplay, despre cum pot să depășească anumite nivele ale jocului, etc.
Motivul pentru care am decis sa creez aceste secțiuni este faptul că îmi doresc ca aplicația să nu fie doar pur informativă, ci vreau să creez un mediu în care utilizatorii pot comunica, pot să se ajute reciproc; de aici și numele aplicației, care provine de la cuvantul “karma”. Aplcația devine astfel un mediu de interacționare a personelor pasionate de jocurile pe calculator. Pentru că, de ce să nu recunoaștem acest lucru, în zilele de azi numărul persoanelor cărora le plac jocurile a început să crească din ce în ce mai mult, acest lucu datorându-se faptului că prezența unui calculator în viața unui individ a devenit o normalitate. De aceea, tot pentru a sprijini interacțiunea dintre utilizatori și totodată crearea unei legături de prietenie între aceștia, aplicația este prevăzută cu un sistem de mesagerie care funcționează similar cu un sistem de email. Menționez faptul că în interiorul aplicației nu este vizibilă adresa de email a unei persoane întrucât consider că dorința de a împărtăși această informație trebuie să vină de la fiecare utilizator în parte.
Cel mai important rol pe care aplicația îl îndeplinește este realizarea unei analize a jocurilor pe calculator, care se realizează prin intermediul recenziilor pe care utilizatorii le pot împărtăși în cadrul unei secțiuni create expres pentru fiecare joc în parte. Această analiză se face strict pe baza conținutului acestor recenzii folosind un clasificator, care le separă în recenzii pozitive și recenzii negative. În urma acestei clasificări a recenziilor, se calculează un punctaj al jocului pentru ca utilizatorii să își poată facă o idee despre cât de bun este un joc din perspectiva celorlalți utilizatori care accesează aplicația.
Motivul pentru care am decis să creez o astfel de aplicație a fost dorința de a veni in ajutorul persoanelor pasionate de jocurile pe calculator, care se află în căutare de noi jocuri pe care să le experimenteze, care doresc să afle opinii despre acestea înainte de a se aventura, sau care doresc să interacționeze cu persoane care au aceleași hobby-uri ca ei, atât pentru a putea primi ajutor din partea altora atunci când se afla într-un impas în interiorul jocului, cât și pentru a oferi la randul lor ajutor. Kharmah este, așadar, o aplicație ce pune accentul și pe socializarea dintre utilizatorii care se înscriu.
Totodată, integrarea conceptelor de inteligență artificială în cadrul unei aplicații web s-a datorat dorinței proprii de a aborda un subiect care nu îmi era atât de familiar, pe care am decis să îl aprofundez și să îl aplic pe un exemplu practic. În acest mod, am reușit să îmi fixez mai bine noțiunile teoretice de bază pe care le-am adunat pe parcursul facultății și am descoperit o mică parte din puterea de care acest domeniu este capabilă.
Tehnologiile folosite
Tehnologiile utilizate pentru realizarea aplicației pot fi împărțite în două mari categorii:
Tehnologii pentru partea de front-end, folosite în scopul obținerii unei interfețe simple, placute, responsive, care să fie cat mai ușor de utilizat de către utilizatorii aplicației;
Tehnologii folosite pentru partea de back-end, care animă aplicația și care asigura buna funcționalitate pe care aceasta și-a propus sa le îndeplinească.
Tehnologiile care fac parte din prima categorie, adică cele pentru front-end sunt:
HTML
CSS
Bootstrap
Printre tehnologiile folosite pentru partea de back-end se numără:
Arhitectura Client-Server
Arhitectura Three-Tier
Protocolul HTTP
Limbajul C#
Framework-ul Asp.Net
Web forms
TinyMce
HTML
Denumirea HTML provine de la inițialele cuvintelor Hypertext Markup Language, care reprezintă limbajul de marcare (markup language) ce stă la baza creării paginilor web si a aplicatiilor web. HTML este un limbaj de pune la dispoziția programatorilor web o gamă variată de elemente pentru structurarea din punct de vedere semantic a paginilor. Standardele de redactare utilizând HTML sunt menținute și explicate de catre organizația World Wide Web Consortium (W3C). Varianta curentă care este recunoscută ca fiind cea standard este HTML5, această variantă fiind propusă în anul 2012 și adoptată in anul 2014.
Componentele limbajului care definesc structura paginilor web sunt reprezentate de tag-uri, închise de paranteze unghiulare. Forma generală a unui tag este:
<nume_tag atribut1 = “valoare1” … atributn = “valoaren”> [continut] </nume_tag>
Ca exemplu, elementul “p” ce definește un paragraf, este reprezentat în limbajul HTML printr tag-ul de start: <p> și tag-ul de sfârșit </p>. Între tag-ul de start, care se mai numește și tag de deschidere, și tag-ul de sfârșit, numit și tag de închidere, poate exista continut sub formă de text ce va apărea în pagina web, dar și alte tag-uri. Ca observație, menționăm faptul că atunci cand avem tag-uri imbricate, tag-ul fiu trebuie să fie închis înaintea tag-ului părinte. Deși majoritatea browser-elor web încearcă să rezolve astfel de situații în mod obișnuit, pot apărea erori de afișare a textului dacă închiderea tag-urilor nu se realizează într-un mod corect.
Există, de asemenea, elemente ale limbajului HTML care nu sunt reprezentate printr-un tag de deschidere și un tag de închidere. Forma unor astfel de elemente este:
<nume_tag atribut1 = “valoare1” … atributn = “valoaren” />
Exemplu: <br /> (tag ce reprezintă sfârșit-ul unei linii de text)
Observăm faptul că pe lângă numele tag-ului și parantezele unghiulare, un tag HTML poate conține și anumite atribute care pot specifica identificatori pentru tag, anumite caracteristici ale acestuia sau elemente de stil.
Așadar, o pagină web poate fi văzută ca fiind o serie de tag-uri imbricate. Sintaxa generală a unei pagini web este:
<!Doctype> nu este un tag, ci reprezintă o instrucțiune ce specifică browser-ului web ce versiune de html este folosită pentru redactarea paginii web. Această instrucțiune trebuie ca întotdeauna să apară la începutul paginii, înainte de tag-ul html. Pentru precizarea variantei curente a HTML-ului (HTML 5), se va folosi instrucțiunea <!Doctype html>.
<html> este tagul rădăcină a unei pagini web si specifică browser-ului web că această pagină este un text html. Toate celelalte tag-uri, cu exceptia <!Doctype> trebuie să fie plasate în interiorul acestui tag.
<head> în cadrul acestui tag se regăsesc elemente ce conțin informații despre titlul paginii, metadate, stil și elemente de tip script. Tag-ul <head> poate conține următoarele tag-uri:
<title> </title> reprezintă un tag mandatoriu și desemnează titlul paginii web, titlu care apare în url sau ca rezultat al unui motor de căutare
<meta> este un tag ce furnizează informații de tip metadată. Printre informațiile care pot fi specificate prin intermediul tag-ului <meta> se numără autorul paginii, cuvinte cheie care sunt utilizate de motoarele de căutare ale browser-elor, data creării paginii, etc.
<script> </script> reprezintă tag-ul în cadrul căruia se poate insera cod javascript, utilizat pentru manipularea elementelor din cadrul paginii sau pentru validarea formularelor. De asemenea, tag-ul <script> dispune de atributul src utilizat pentru precizarea fișierului din care este încărcat codul sursă.
<style> </style>> încadrează cod CSS, limbaj utilizat pentru stilizarea paginilor web. Acest limbaj va fi descris într-o secțiune ulterioară a lucrării.
<link> este tag-ul ce creează o legătură către un fișier extern, care deobicei reprezintă un fișier CSS, al cărui nume este specificat prin setarea atributului href cu valoarea “nume_fișier”.
În interiorul tag-ului <body> </body> se încadrează conținutul propriu-zis al paginii web, precum text, imagini, videoclipuri, tabele sau legaturi către alte pagini web
În continuare voi exemplifica și voi descrie succint cele mai uzuale tag-uri utilizate în cadrul paginilor web:
<article> </article> după cum se întelege din denumire, definește elementul de structură articol
<p> </p> reprezintă tag-ul care definește un paragraf
<img/> cu ajutorul acestui tag se insereaza o imagine. Pentru a încărca efectiv o imagine, trebuie specificat atributul src ce primește ca valoare url-ul imaginii.
<input/> cu ajutorul acestui tag se pot construi mai multe elemente de inserare, în funcție de valoarea pe care atributul type o primește: “text” (pentru obținerea unui textbox), “button” (pentru obținerea unui buton), “checkbox”, “radio”, “file” (creează un control prin intermediul căruia se poate încărca un fisier) , etc.
<label/> definește o etichetă pentru un control de tip input
<ul> </ul>, <ol> </ol> aceste tag-uri definesc o listă de elemente neordonată, respectiv ordonată
<li> </li> definește un element al unei liste
<form> </form> permite crearea unui formular. Prin setarea atributului action=“url_pagina” se setează pagina către care sunt trimise informațiile din cadrul introduse în formular.
<div> </div> acest tag creează o secțiune a paginii web. În mod uzual, acest tag este folosit pentru a grupa diverse controale pentru a le stiliza folosind limbajul CSS.
<a> </a> definește un hyperlink către pagina a cărui url apare ca valoare a atributului href. Intre tag-ul de deschidere și cel de închidere se introduce textul prin intermediul căruia la apăsare va redirecționa către pagina respectivă.
<br/> definește un line break (trecerea la un rănd nou)
<h1> </h1> … <h6> </h6> aceste tag-uri definesc antente. Numărul care apare după litera h semnifică “importanța” antetului, 1 reprezentând antetul cel mai important, iar 6 corespunzând antetului celui mai puțin important.
tag-ul <!– –> delimitează un comentariu al cărui continut se inserează între cele două perechi de “–“.
CSS (Cascading Style Sheets)
CSS reprezintă un limbaj de definire a stilului care este utilizat pentru stilizarea unui document scris cu ajutorul unui limbaj de marcare (markup language). CSS este folosit cu precădere pentru stilizarea paginilor web, pentru obținerea unei interfețe atractive, a unui design plăcut pentru utilizatori și care sa fie frumos structurat și aranjat atât pentru dispozitive cu ecrane mai mari, cât și pentru dispozitive cu ecrane mici (telefoane, tablete). Însă, utilizarea acestui limbaj nu se limitează doar la stilizarea paginilor scrise cu HTML, ci poate fi utilizat pentru stilizarea oricărui document XML (Extensible Markup Language).
În continuare voi vorbi despre utilizarea CSS pentru documente scrise utilizând limbajul HTML.
Scopul pentru care CSS a fost creat a fost separarea conținutului (informației) efectiv al paginii de modul în care pagina este estetizată. Deoarece, deși limbajul CSS poate fi scris inline, utilizând atributul style care poate fi anexat oricărui element HTML, sau în interiorul tag-ului <style> </style> care de cele mai multe ori este poziționat în interiorul tag-ului <head> </head>, deși nu este mandatoriu, este recomandat ca stilul să fie incărcat dintr-un fișier sursă extern în care sunt definite regulile de stilizare.
Limbajul CSS dispune de o sintaxă extrem de simplă. Aceasta este reprezentată de o listă de elemente de tipul:
selector {
atribut1 : valoare1;
atribut2: valoare2;
atributn: valoaren;
}
Selectorul specifică asupra cărui element HTML acționează regulile ce se regăsesc în interiorul blocului. Astfel, selectorii pot indica:
un element HTML, de exemplu p, div, article. Dacă blocul de reguli este specificat pentru un selector care reprezintă o componentă a limbajului HTML, toate aceste componente ce se află pe pagina curentă vor fi afectate.
un element HTML cu un anumit atribut (ID, clasa). Pentru a obține stilizarea unui singur element specificat prin intermediul unui id, selectorul trebuie să aiba forma: #denumire_id. Pentru a stiliza mai multe elemente HTML, este recomandată gruparea lor pe clase. În acest caz, selectorul va arăta în felul următor: .denumire_clasa.
elemente ce au o anumită poziție față de alte elemente prezente în cadrul aceleiași pagini
Voi exemplifica în continuare mai multe tipuri de selectori pentru a evidenția utilizarea variată a acestora:
* selectează toate elementele
A B selectează elementul B, descendent al elementului A în arborele documentului
A + B selectează un element B ce este imediat ulterior unui element A
A > B selectează un element B, fiu al elementului A
A ~ B selectează un element B al cărui ascendent este A
A[atribut=”val”] selectează elementul A ce are drept valoarea val
Un concept interesant pe care limbajul CSS îl aduce este cel de pseudoclasă. Pseudoclasele sunt folosite ca selectori pentru a formata elemente după anumite criterii. care nu se regăsesc scrise în text. Cele mai des întalnite pseudoclase sunt: A:hover (formatează elementul A atunci când utilizatorul aduce poziționează cursorul deasupra elementului A), :visited (această pseudoclasă se referă în mod explicit la hyperlink-uri și selectează acele hyperlink-uri care au fost accesate), :first-line, :last-line, :first-letter, :last-letter.
Blocul de proprietăți conține o serie de declarații de forma:
nume_proprietate : valoare;
Atributele sunt specificate în standardele CSS menținute de W3C. Exemple reprezentative pentru astfel de atribute sunt: margin, color (culoarea textului), background-color, font-size, etc. Valorile pe care aceste atribute le pot lua sunt de doua tipuri:
cuvinte cheie: center, justify, red, absolute
valori numerice: 20px, 1.3em, 50%
valori hexazecimale: #fff
valori rgba: rgba(0,0,255)
Exemplu simplu de utilizare a limbajului CSS pentru stilizarea continutului:
În continuare voi exemplifica efectul pseudoclasei #mydiv:hover din exemplu. Culoarea div-ului s-a schimbat din albastru în galben în momentul în care cursorul a fost pozitionat pe suprafața div-ului.
Puterea limbajului CSS nu se limitează doar la stabilirea unor proprietăți ale unor elemente HTML. Acest limbaj oferă posibilitatea de a realiza animații, care reprezintă trecerea graduală de la un stil la altul (schimbarea proprietăților unui element), fără a necesita instalarea suplimentară a unui plugin.
Definirea unei animații se realizează prin instrucțiunea:
@keyframes nume_animatie{
from { atribut1: valoare 1;
….
atributn: valoare n;}
to {atribut1: valoare 1;
…
atributn: valoaren;}
}
De asemenea, se pot folosi procente pentru crearea unor intervale de tranziție a stilului
@keyframes nume_animație{
0% { atribut_i: valoare_i;}
50%{atribut_j: valoare_j;}
100% {atribut_k: valoare_k;}
}
Atribuirea unei animații la un element HTML se face în modul următor:
selector {
animation_name: nume_animație;
animation_duration: 5 s;
}
Setarea atributului animation_duration este mandatorie. În caz că această atribuire nu apare, animația nu va avea loc întrucat valoarea prestabilită este 0 s. În mod obișnuit, animația are loc o singură dată. Pentru setarea numărului de realizare a animației se setează atributul ”animtion-iteration-count” cu valoarea x , unde x reprezintă un număr natural, sau cu valoarea infinite, pentru realizarea animației la infinit.
Bootstrap
Bootstrap este un web framework, gratuit, utilizat pentru design-ul aplicațiilor web și al paginilor web. La început fiind numit “Twitter Blootprint” el a fost creat de către Mark Otto și Jacob Thornton și a fost lansat în 2011 ca fiind un framework open-source gratuit. Acest framework constă dintr-o colecție de cod HTML și CSS ce pun la dispoziția utilizatorilor o serie de clase care pot fi folsite pentru obținerea unui stil structurat, responsive. Varianta curentă de Bootstrap este Bootstrap3.
Reprezintă un instrument deosebit de important pentru realizarea aplicațiilor web, ușurând munca unui programator pentru obținerea unui design structurat, ce se poate configura în mod dinamic. Colecția de clase CSS este însoțită de cod JavaScript sub forma unor plugin-uri JQuery, utilizate pentru punerea în mișcare a carousel-elor, pentru completarea automată a câmpurilor, manipularea modal-urilor.
În cadrul aplicației realizate de către mine, principalele funcționalități oferite de Bootstrap pe care le-am folosit au fost în cea mai mare măsură clasele CSS. Voi exemplifica și voi prezenta succint cele mai importante clase pe care le-am utilizat pentru stilizarea aplicației și rolul pe care acestea le îndeplinesc:
pentru formatarea butoanelor se folosesc clasele: btn, btn-primary, btn-warning, btn-alert. Exemplu:
<input type="button" class="btn" value="Atinge butonul" style="margin:100px;">
<input type="button"class="btn-primary” value="Atinge butonul" style="margin:100px;">
<input type="button" class="btn-warning" text="buton" value="Atinge butonul" style="margin:100px;">
<input type="button" class="btn-danger" text="buton" value="Atinge butonul" style="margin:100px;">
pentru setarea dinamică a lățimii elementelor HTML am folosit cele 4 clase principale: col-xs-n, col-sm-n, col-md-n, col-lg-n, unde n reprezintă un număr de la 1 la 12. Framework-ul Bootstrap consideră ecranul (sau container-ul în general) ca fiind format din 12 coloane. Astfel, numărul n specifică numărul coloanelor din container pe care le ocupă. Cuvintele “xs”, “sm”, “md”, “lg” semnifică dimensiunea dispozitivului pentru care este aplicat formatul, “xs” fiind corespunzător unui dispozitiv cu ecran foarte mic, iar “lg” corespunzând unui dispozitiv cu ecranul mare.
pentru crearea unui element de tip carousel am folosit clasele: “carousel-slide”(pentru container-ul ce va juca rolul de carousel), “carousel-indicators”, “carousel-inner” (pentru lista ce conține elementele care vor fi rulate în cadrul carousel-ului), “carousel-control” (pentru butoanele care derulează în stânga și în dreapta elemenetele carousel-ului) și clasa “item” (pentru fiecare element în parte). Elementele au fost adăugate în mod dinamic, deoarece ele sunt generate pe baza unui algoritm de recomandare pe care îl voi exemplifica intr-o secțiune ulterioară. Codul care exemplifică utilizarea Bootstrap-ului pentru crearea carousel-ului este următorul:
Figure – Codul HTML îmbinat cu utilizarea claselor din Bootstrap pentru generarea unui carousel
Iar rezultatul codului este:
Figure – Carousel-ul obținut prin folosirea claselor din bootstrap
Arhitectura Client-Server
Arhitectura Client-Server este o structură a aplicațiilor distribuite ce separă aplicațiile în două categorii: server-ul, ce furnizează anumite servicii, și clienții, care sunt calculatoare personale sau stații de lucru care creează o conexiune cu server-ul și folosește serviciile pe care acesta le oferă. În mod uzual, server-ul și clienții comunică printr-o rețea de calculatoare.
Server-ul reprezintă un calculator puternic ce oferă clienților anumite servicii și resurse unuia sau mai multor clienți, precum și asigură accesul acestora asupra bazei de date. Un server poate primi mai multe cereri simultan și poate executa un număr limitat de sarcini la un anumit moment. Scopul lui este de a gestiona aceste cereri și de a prioritiza ordinea în care acesta răspunde la ele. Există mai multe categorii de server-e, precum:
Application server – utilizate pentru aplicații web. Aceste server-e nu trebuie să facă parte neapărat din “world wide web”
Game server (server de jocuri) – server care permite utilizatorilor să joace jocuri multiplayer
Database server (server de baze de date) – server ce oferă accesul la o bază de date
Mail server
Fax server
Print server
Clientul reprezintă un calculator personal sau o stație de lucru care instanțiază o conexiune cu server-ul și utilizează serviciile pe care acesta le furnizează. Clienții nu partajează resursele proprii între ei, fiecare conexiune fiind independentă una de cealaltă. De asemenea, clienții nu sunt nevoiți să știe cum se realizează serviciile, interesul lor este reprezentat doar de răspunsurile pe care le primesc.
Arhitectura client-server utilizeaza un sistem de gestiune a bazelor de date pentru a răspunde cererilor clienților și pentru a le furniza doar informațiile cerute de aceștia, nu întreaga bază de date. Un sistem de gestiune a bazelor de date reprezintă un program prin intermediul căruia este creată, interogată și modificată o bază de date.
Aplicația Kharmah se bazează pe o astfel de arhitectură, clienții fiind utilizatorii care navighează prin paginile web și primesc informații cu privire la jocurile care există in baza de date, precum și oferă posibilitatea anumitor utilizatori (moderatorii) de a manipula datele (adăugare de jocuri, editare, ștergere a comentariilor) și de asemenea permite utilizatorilor obișnuiți posibilitatea de a crea topic-uri pentru a li se răspunde la întrebările pe care le au în legătură cu subiectul. Serverul este, așadar, un application server și totodată un server de baze de date.
Figure – Schema unei arhitecturi client-server
Arhitectura Three-Tier
Arhitectura multitier, cunoscută și sub denumirea de arhitectură multilayered, este o arhitectură client-server în care funcțiile de de transmitere a cererilor ale clienților (implicit și interfața pe care o folosesc clienții), soluționarea cererilor și administrarea datelor este împărțită pe mai multe nivele. Cea mai cunoscută arhitectură multitier este arhitectura three-tier.
Această separare a aplicației pe mai multe nivele oferă flexibilitate dezvoltării aplicației, întrucât manipularea unui nivel (ștergere și modificare) nu presupune modificarea întregii aplicațîi. De asemenea, această metodă permite crearea la orice moment a unor subnivele care să îndeplinească noi funcționalități sau care să trateze anumite cazuri particulare.
În particular, arhitectura three-tier împarte aplicația pe trei nivele, ținute în mod uzual chiar pe platforme diferite:
Presentation tier (nivelul prezentării)
Logic tier (nivelul logic)
Data tier (nivelul datelor)
Aceasta arhitectură a fost dezvoltată de către John J. Donovan, fost profesor la MIT și președinte actual al companiei Cambridge Technology Group. Datorită faptului că este o arhitectură multitier, arhitectura three-tier moștenește avantajele unui astfel de tip de arhitectură, în sensul că modificările ce au loc pe unul din cele 3 nivele nu au niciun efect asupra celorlalte două nivele. Mai mult, nu numai că prelucrarea unui nivel nu produce modificarea altuia, ci această schimbare nici nu își face simțită prezența, ceea ce înseamnă că se pot îmbunătăți constant cele 3 nivele fără apariția erorilor de integrare.
Presentation tier reprezintă cel mai înalt nivel, nivelul la care clienții, în mod uzual utilizatorii, au acces și unde pot formula cererile către server. Acest nivel este reprezentat prin intermediul unei interfețe (de cele mai multe ori pagina web) prin care se fac cunoscute serviciile pe care aplicația le furnizează pentru utilizatorii săi. Practic, este un mod inteligibil de prezentare a funcționalității de care utilizatorii despun, de aducere la cunoștință a informațiilor la care au acces, fie pentru a le vizualiza, fie pentru a le prelucra.
Logic tier reprezintă acel nivel care aduce funcționalitate aplicației și care procesează cererile transmise de către clienți. În esență, reprezintă un mijloc de legătură între client și datele pe care acesta dorește sa le manipuleze. Totodată, un rol deosebit de important pe care acest nivel îl îndeplinește este validarea cererilor și acordul permisiunilor. Se ține cont de drepturile pe care clienții le au și se împiedică accesarea unor servicii pentru care un client nu are drepturi de utilizare.
Data tier este nivelul la care se execută cererile clienților și se returneaza informația cerută de aceștia. Acest lucru implică existența unei baze de date care este accesată prin intermediul unui sistem de gestiune al bazelor de date (Oracle, MySql, Sql Server, etc) . Contul unui client nu trebuie să fie cont pentru un sistem de gestiune a bazelor de date. În mod obișnuit, pentru un SGBD se oferă un număr limitat de conturi, și acest lucru nu ar fi palpabil pentru o aplicație care poate să aiba milioane de utilizatori. Operațiile asupra bazei de date se realizează folosind același cont pentru SGBD, întrucât în cadrul nivelului logic se fac verificările drepturilor. Utilizatorilor nu li se întoarce, așadar, decât informații la care au drepturi de acces.
Revin acum asupra aplicației pe care o descriu și care adoptă, de asemenea, o arhitectură three-tier.
Fiind o aplicație web, primul nivel, presentation tier, este reprezentat de pagini web prin care utilizatorii pot naviga și pot emite cereri de vizualizare a informațiilor despre jocuri, dar și despre secțiuni suplimentare ale acestora: secțiunea de întrebări, secțiunea de bug-uri, secțiunea recenziilor.
Funcționalitățile pe care cel de-al doilea nivel, logic tier, le furnizează sunt construite folosind limbajul C# și compilatorul Visual Studio 2013. Informațiile despre jocuri sunt vizibile și utilizatorilor neînregistrați. Însă pentru funcționalitățile de care dispun utilizatorii conectați, moderatorii sau adminii, se verifică în permanență identitatea acestora și ce drepturi au. În mod obișnuit, dacă un astfel de utilizator nu trece testul de identificare sau nu are drepturile necesare, cererea nu este transmisă următorului nivel și utilizatorul este redirecționat către pagina de Login.
Al treilea nivel, data tier, constă în utilizarea sistemului de gestiune al bazelor de date integrat în Visual Studio 2013, și anume SQL Server. Aplicația are propria bază de date stocată local, bază de date care conține informații despre jocuri, despre utilizatorii conectați, despre mesajele pe care aceștia le transmit și despre subiectele de discuție pentru fiecare joc în parte. Limbajul pe care SQL Server îl utilizează este transact-sql.
Protocolul HTTP
Denumirea HTTP provine de la inițialele cuvintelor HyperText Transfer Protocol și reprezintă protocolul de transfer utilizat de Web. Prin intermediul protocolului HTTP se asigură transmiterea și schimbul de informațiilor de tip hipertext.
Cel care a inițiat dezvoltarea protocolului HTTP în 1989 la CERN (European Organization of Nuclear Research) este Tim Berners-Lee, președintele organizației World Wide Web Consortium (W3C), cunoscut drept inventatorul Web-ului. Standardizarea protocolului a fost coordonata, de asemenea, de W3C. Varianta actuală suportată de browser-ele Web este HTTP/2, care a fost standardizat in 2015.
Modul de funcționare a protocolului HTTP este similar cu modul de funcționare a unei arhitecturi client-server. El asigură comunicarea dintre clienți, care trimit cererile sub forma unor pagini Web cu un server, care procesează cererile, verifică drepturile, eventual face anumite calcule pentru obținerea răspunsului și întoarce răspunsul către clienți tot sub formă de pagini web.
Conexiunea protocolului HTTP
Varianta 1.0 a protocolului HTTP permitea folosirea unei singure conexiuni TCP (Transmission Control Protocol) pentru transmiterea de către client a unei cereri, procesarea acesteia de către server și returnarea către client a raspunsului de la server. TCP-ul este protocolul care asigură transmiterea sigură și lipsită de erori între aplicații conectate în rețea și este utilizat de catre World Wide Web (WWW), aplicațiile de email și aplicațiile de transmitere de fișiere. După primirea răspunsului de către clienți această conexiune este eliberată. Astfel, pentru mai multe cereri către server trebuia de fiecare dată să fie stabilită o nouă conexiune TCP cu server-ul.
Acest lucru a fost schimbat prin dezvoltarea versiunii 1.1 a protocolului HTTP, în sensul că mai multe cereri pot fi transmise și procesate folosind o aceeași conexiune TCP. Astfel, clientul nu mai este nevoit să stabilească o nouă conexiune cu server-ul pentru transmiterea mai multor cereri, ci acestea pot fi transmise secvențial. Mai mult, clientul nu trebuie să aștepte primirea răspunsului de la server pentru transmiterea unor cereri ulterioare, ci le poate trimite fără niciun impediment.
Această transfer de inormație ce are loc prin intermediul protocolului HTTP se realizează utilizând o serie de metode care sunt puse la dispoziție de către protocol. Acestea sunt:
GET
HEAD
POST
PUT
DELETE
TRACE
OPTIONS
CONNECT
PATCH
Metoda GET este utilizată exclusiv pentru cererea resurselor, resrsă care în mod uzual reprezintă o pagină Web. Această metodă are rolul doar de a returna fișiere, fără niciun alt fel de operație asupra lor.
Metoda HEAD este aproximativ similară cu metoda GET. Deosebirea constă în faptul că răspunsul nu reprezintă o întreagă pagină web, ci doar antetul acesteia. Scopul utilizării acestei metode este de a obține informații referitoare la metadată. Acest lucru constă un avantaj, întrucât nu este nevoie trimiterea întregului conținut al răspunsului pentru a se obține doar informațiile pe care această metodă își propune să le obțină.
Prin intermediul metodei PUT se realizează scrierea unei pagini Web pe un server. Această metodă trebuie să fie însoțită de mijloace de autentificare și de verificare a drepturilor pe care clientul trebuie să le dețină pentru a executa o astfel de operație. Un mod de a realiza acest lucru este de a codifica cererea pentru a include antete de autentificare, de exemplu utilizând Codificarea MIME (Multipurpose Internet Mail Extensons) , protocol ce permite transferul de date care nu sunt în mod obligatoriu texte (audio, video, poze, etc). În cazul în care această pagină există deja, ea va fi surprascrisă.
Metoda POST este utilizată pentru a anexa informație nouă în cadrul paginii Web. Deobicei se folosește pentru adăugarea unor date în baza de date, pentru postarea unui anunț, a unui mesaj, etc.
Acțiunea metodei DELETE, evidentă datorită numelui pe care îl deține, este de a șterge pagina Web.
Metoda TRACE este folosită pentru debugging, pentru verificarea cererilor care ajung la server și a acțiunilor executate de acesta pentru soluționarea cererilor. Este o metodă de verificare a corectitudinii cererilor transmise de către clienți.
Pentru obținerea metodelor sau a serviciilor pe care un server le furnizează pentru un anumit URL, se apelează metoda OPTIONS. Pentru a obține întreaga funcționalitate a server-ului, se poate apela metoda OPTIONS cu parametrul “*”.
Metoda CONNECT transformă conexiunea curentă întrun canal de comunicare transparent TCP/IP, pentru a se putea utiliza protocoale de criptare pentru obținerea unor comunicări sigure. Exemplu de protocol este TLS (Transport Layer Security).
Utilizarea metodei PATCH este similară cu utilizarea metodei PUT, în sensul că ambele metode au rolul de a scrie pagini Web. Diferența dintre cele două metode constă în faptul că dacă în cazul metodei PUT scrierea unei pagini care există deja se face prin suprascriere, metoda PATCH permite modificarea parțială a unei pagini, modificarea făcându-se fără suprascriere.
Metodele POST, PUT, DELETE și PATCH sunt considerate nesigure, întrucât rularea lor are drept rezultat fie modificarea paginilor Web, fie modificări ale bazei de date. Din acest motiv, server-ul trebuie să asigure autentificarea și autorizarea unor astfel de metode.
De exemplu, aplicația dezvoltată de mine verifică autenticitatea utilizatorilor atunci când încearcă să se conecteze la o pagină nefolosind navigarea pusă la dispoziție de aplicație, ci prin editarea directă a URL-ului. Deoarece informațiile despre utilizatori sunt reținute în sesiune atunci cand se autentifică în cadrul aplicației, aceștia nu au posibilitatea de a-și modifica detaliile în timp ce sunt conectați. Mai mult, chiar dacă toți moderatorii au acces la pagina de editare a unui joc, doar proprietarii acelui joc (adică acea persoană care a adăugat jocul în aplicație) are dreptul de modificare sau de ștergere asupra jocului.
Toate metodele enumerate și explicate precedent au ca raspuns un cod numeric, ce identifică faptul ca cererea a avut loc cu succes, sau dimpotrivă cu eșec, precum și un mesaj care să descrie codul. Codurile de răspuns se împart în 5 categorii:
1XX : coduri care prezintă informație despre procesarea cererii și despre efectele pe care le-a avut procesarea acesteia
2XX : coduri reprezentative pentru mesaje de succes.
3XX : coduri corespunzătoare redirecționărilor, de exemplu 301 este codul care specifică faptul că pagina a fost mutată la o alta adresă.
4XX : coduri de eroare. Un exemplu reprezentativ este 404: Page not found (pagina nu a fost gasită)
5XX : eroare de server (erori apărute pe parcursul execuției unor servicii/metode furnizate de acesta). În mod uzual, aceste tipuri de erori ar trebui tratate prin intermediul unor pagini de eroare sau prin intermediul unor mesaje către utilizator.
Cererile pe care clienții le transmit server-ului după ce au stabilit o conexiune la acesta au următoarea forma:
o linie pentru cerere
header-e
o linie goala
corpul mesajului (optional)
Raspunsurile pe care server-ul le întoarce către clienți ca urmare a procesării cererilor acestora au form următoare:
o linie ce conține codul de retur al răspunsului, însoțit de mesajul corespunzător acestui cod
header-e
o linie goala
corpul mesajului (optional)
Limbajul C#
Întrucât aplicația pe care am dezvoltat-o folosește într-o mare parte limbajul C#, voi descrie principalele caracteristici ale limbajului în ceea ce privește conceptele de POO (Programare orientată pe obiecte) și voi prezenta clasele și metodele pe care le-am utilizat în dezvoltarea funcționalității paginilor Web.
Limbajul C#, dezvoltat de compania Microsoft și ulterior acreditat de către Ecma (European Computer Manufacturers Association) și ISO (International Organization of Standardization). Din momentul în care a fost dezvoltat, C# și-a propus să fie un limbaj modern, revoluționar, care să faciliteze în mod considerabil crearea aplicațiilor pentru sistemul de operare Windows și a aplicațiilor Web. Cea mai recentă versiune a limbajului este C# 7.0, emiă odată cu Visual Studio 2017.
Limbajul C# a fost constuit ținandu-se cont de caracteristicile limbajelor care îl preced, pe care le și adoptă:
performanța limbajului C
structura orientată pe obiecte a limbajulu C++
securitatea și “garbage collector” ale limbajului Java
dezvoltarea rapidă a limbajului Visual Basic
Fiind un limbaj orientat pe obiecte, C# oferă utilizatorilor săi posibilitatea de a folosi conceptele de bază ale programării orientate pe obiect: încapsulare, moștenire și polimorfism.
C# este prevăzut cu un număr mare de clase predefinite și permite definirea unor noi clase, fie clase noi, definite în totalitate de către programator, fie clase obținute prin moștenire din clase predefinite. În limbajul C#, o clasă poate moșteni o singură clasă, însă poate implementa una sau mai multe interfețe. Acest lucru permite organizarea funcțiilor și a variabilelor prin construirea unor tipuri de date (clase) care să le încapsuleze, care pot fi utilizate prin instanțierea unor variabile și apelarea metodelor corespunzătoare.
Exemplu de definire a unei clase utilizate în cadrul aplicației Kharmah (pentru exemplu am selectat doar câmpurile și câteva metode pentru că definirea în totalitate a clasei era relativ lungă):
Figure – Exemplu de definire a unei clase numite Codificator
În limbajul C#, tipurile sunt în esență clase, iar el permite declararea de variabile care se numesc obiecte. O clasă conține câmpuri, care sunt reprezentate de variabilele declarate în interiorul clasei, și metode, care sunt funcțiile declarate și definite în interiorul claselor și care descriu operațiile principale pe care clasa respectivă le oferă. Metodele se împart în două categorii:
funcții fără tip, care nu returnează un rezultat. În general, funcțiile care intră în această categorie sunt funcții care fie afișează anumite mesaje într-un anumit context, fie manipulează alte date sau fișiere. O funcție fără tip are signatura:
void nume_funcție ([Lista_parametri])
Exemplu:
void AfiseazaMesaj(String mesaj){
Console.WriteLine(“Mesajul este: “ + mesaj);
}
funcții cu tip, care returnează un rezultat. În mod uzual, această categorie corespunde funcțiilor care calulează anumite rezultate pe baza parametrilor. Signatura unei astfel de metode este:
nume_tip nume_funcție([Lista_parametri])
Exemplu:
int Dubleaza(int x){
return x * x;
}
În ambele cazuri, lista parametrilor este opțională. De asemenea, o funcție poate avea un parametru, mai mulți parametri, sau niciun parametru. Parametrii sunt variabile declarate între cele două paranteze și care sunt utilizate pentru realizarea scopului pentru care a fost creată funcția. Parametrii se împart și ei în doua categorii:
parametri formali – sunt parametrii care apar în declararea funcțiilor și care sunt utilizați în interiorul acestora în diverse calcule sau
parametri actuali – reprezintă parametrii care apar în apelul metodei și a căror valoare este utilizată concret pentru executarea metodei. Aceștia pot fi constante, sau variabile declarate ulterior.
Există doua tipuri de transmitere a parametrilor. Primul este transmiterea parametrilor prin valoare. Acest lucru semnifică faptul că metoda construiește pentru fiecare parametru formal o variabilă de acel tip și atribuie acestor variabile valorile parametrilor actuali. Așadar, metoda se va folosi de aceste variabile nou create pentru producerea rezultatului, parametrii actuali rămânând nemodificati dupa terminarea acesteia.
Al doilea tip de transmitere a parametrilor este prin referință. Dacă un parametru este transmis prin referință, se va folosi chiar zona lui de memorie pentru executarea funcției. Astfel, după terminarea execuției, valoarea acestuia ar putea fi modificată in funcție de natura operațiilor din cadrul metodei. În acest caz, pentru apelul funcției nu pot fi utilizate constantele drept parametri actuali. Pentru a se specifica transmiterea unui parametru prin referință se scrie, înainte de tipul de date al parametrului, cuvântul cheie “ref”.
Exemplu:
int Dubleaza(ref int x)
O funcție poate returna doar o singură valoare, al cărui tip este cel declarat în signatura funcției. Dacă se dorește returnarea mai multor rezultate, se poate utiliza o funcție fără tip (void) și parametri transmiși prin referință. Datorită faptului că variabilele folosite în apelul funcțiilor vor avea valorile modificate din cauza transmiterii prin referință, acestea vor juca rolul rezultatelor returnate de către funcție.
Utilizarea unui câmp sau a unei metode a unui obiect ce are ca tip o clasa A se realizează utilizând următoarea sintaxă:
A ob = new A();
ob.nume_metoda([lista_parametri_actuali])
Funcția pe care o îndeplinește operatorul (.) este de a accesa câmpurile și metodele din cadrul unei clase sau din cadrul unui namespace. Un namespace reprezintă un mod de organizare al claselor și al metodelor. El poate fi văzut ca un pachet ce conține o colecție de clase. Scopul pentru care a fost creat este acela că napespace-ul permite existența mai multor clase cu aceeași denumire. Astfel, dacă există două namespace-uri: namespaceA și namespaceB, poate exista câte o clasă ClasaC în fiecare dintre ele. Utilizarea clasei se va realiza în felul următor:
namespaceA.ClasaC pentru referirea clasei din namespaceA
namespaceB.ClasaC pentru referirea clasei din namespaceB
Pentru utilizarea namespace-urilor trebuie să fie introdusă instrucțiunea în afara clasei în care este utilizată:
using namespace nume_namespace;
Cuvântul cheie static poziționat înainte de tipul unei variabile indică faptul că acea metodă sau câmp poate fi accesat fără a crea înainte un obiect de tipul acelei clase. Mai mult, valoarea câmpului static este partajată de toate obiectele ce au ca tip clasa respectivă. El trebuie inițializat la declarare, și modificarea acestuia prin intermediul unui obiect va fi vizibilă și pentru celelalte obiecte.
În figura 5 în care este exemplificată definirea unei clase în limbajul C# se observă repetarea unui cuvânt cheie înaintea tipului de date al variabilelor și al metodelor, “public”. Acest cuvânt cheie reprezintă un mod de acces asupra datelor. În limbajul c# exista 5 moduri de acces:
public
protected
private
internal
protected internal
Tipul de acces public desemnează faptul că accesul la câmp sau metodă este permis din orice punct al programului, fie el în cadrul metodelor definite în clasă, fie în metodele definite în cadrul altor clase.
Tipul de acces protected permite accesul la câmp sau metodă doar metodelor definite în clasa ce îl/o conține, precum și in cadrul claselor derivate din clasa în care câmpul sau metoda este definit/ă.
Cuvantul cheie private este tipul de acces care nu permite utilizarea câmpului sau a metodei din afara clasei. Aceste date nu sunt vizibile pentru alte clase și de aceea nu pot fi apelate.
Tipul de acces internal este similar tipului de acces public, cu diferența că metoda sau câmpul internal este vizibil doar în cadrul dll-ului (asamblării) curent.
Tipul de acces protected internal permite ca metoda/câmpul sa fie vizibil în cadrul metodelor ce fac parte din clasa C, în cadrul metodelor claselor derivate din clasa C, precum și claselor care se află în cadrul aceleiași asamblări.
Un lucru adițional pe care limbajul C# îl aduce față de clasele din C++ si Java este reprezentat de proprietăți. Proprietățile se definesc atunci când se dorește ca accesul la membrii claselor să se realizeze prin intermediul unor metode. Din perspectiva clientului, însă, acest acces se face ca și cum metoda respectivă ar fi de fapt un câmp al clasei. Astfel, accesul la memrii clasei are loc în mod indirect.
Voi exemplifica cum se construiește o proprietate simpla:
Class C {
private int val = 0;
public int Valoare
{
get{ return val; }
set {val = value;}
}
}
Utilizarea proprietății se realizează în modul următor:
C obiect = new Obiect();
obiect.Valoare++;
int x = obiect.Valoare;
Am vorbit până acum despre modul în care se poate defini o clasă. Însă nu am explicat în mod riguros modul de a declara un obiect de tipul unei clase și inițializarea acestuia. Pentru inițializarea unui obiect, este nevoie de un constructor. Un constructor reprezintă o metodă a unei clase fără tip, care are drept nume numele clasei și care este apelată atunci cand este inițializat un obiect al clasei respective. Deoarece limbajul C# permite definirea funcțiilor cu același nume (dar care diferă totuși prin numărul sau tipul parametrilor formali), o clasă poate conține mai mulți constructori. Dacă nu este definit, există implicit un constructor fără parametri, care are rolul de a aloca memorie pentru obiect. Așadar, scopul constructorului este de a construi obiectul, adică de a aloca o zonă de memorie pentru obiect și, eventual, de a inițializa câmpurile clasei. Mai pot fi construiți de asemena constructori cu parametri, care sunt folosiți pentru inițializarea câmpurilor, sau constructori ce au parametri drept alte obiecte de tipul clasei, prin care obiectului i se atribuie valoarea unui alt obiect. Acest tip de constructor se numește constructor de copiere.
Limbajul C#, limbaj de programare orientat pe obiecte, suportă de asemenea moștenirea. Moștenirea reprezintă procedeul de construcție a claselor care “prelungesc” clasele din care provin. Atunci cand o clasă B moștenește o clasă A, toate câmpurile și metodele clasei A vor fi componente ale clasei B. De asemenea, clasa B poate deține câmpuri și metode suplimentare. Astfel, clasa B poate utiliza metode definite în clasa A (care nu sunt private) și, totodată, poate utiliza noi metode sau câmpuri definite în cadrul acesteia. Sintaxa prin care o clasă este construită prin moștenire este:
Class A : B { …}
Deși limbajul C# permite moștenirea unei singure clase de bază, moștenirea multiplă întâlnită în cadrul limbajului C++ se poate realiza doar pentru derivarea interfețelor.
Interfețele reprezintă clase în intermediul cărora sunt declarate metode care însă nu sunt definite, fiind prezente doar declarațiile metodelor. Aceste metode urmează a fi implementate de clasele care le moștenesc. Pentru implementarea unei interfețe I se folosește signatura:
Class A : I
În cadrul clasei A trebuie definite absolut toate metodele declarate în interfața I. Altfel, va apărea o eroare.
Exemplu:
Un aspect deosebit de important al moștenirii este conceptul de polimorfism. Acest concept se referă la faptul că metoda dintr-o clasă poate fi suprascrisă în cadrul unei clase derivate care să se adapteze la modificările ce apar în plus în clasa derviată față de clasa părinte. Pentru a specifica faptul că o metodă poate fi suprascrisă, se plasează cuvântul cheie “virtual” în cadrul signaturii metodei:
[tip acces] virtual nume_tip nume_metoda([lista parametri])
Dacă această metodă nu va fi suprascrisă în clasa derviată, atunci aceasta va apela metoda din clasa de bază. Pentru a specifica suprascrierea metodei virtuale din clasa părinte, se utilizează cuvântul “override” în signatura metodei din clasa derivată:
[tip_acces] override nume_tip nume_metoda([lista parametri])
unde numele metodei din clasa derivată trebuie să fie identic cu cel din clasa de bază.În noua metodă derivată se poate scrie, astfel, un cod nou, adaptat funcționalității noi pe care aceasta o aduce.
Colecția de clase pe care limbajul C# o pune la dispoziția programatorului este foarte vastă și diversificată. Nu voi defini toate aceste clase, întrucât scopul lucrării nu este acesta. Voi prezenta în continuare doar clasele cele mai importante pe care le-am utilizat în dezvoltarea aplicației Web.
Clasa Dictionary<TKey, TValue> reprezintă una din clasele care aduc o funcționalitate extrem de folositoare, unde TKey și TValue pot reprezenta orice clasă, predefinită sau definită de către programator. Acest tip de date permite crearea unei colecții de perechi de tipul (cheie, valoare), unde cheia și valoarea pot avea orice tip, chiar tipuri diferite. Proprietățile care mi-au fost cele mai folositoare sunt: Count (care returnează numărul de perechi existente in colecție), Keys și Values, proprietăți care returnează colecții care conțin toate cheile din cadrul dicționarului, respectiv toate valorile existente.
Metodele pe care le pune la dispoziție și pe care le-am utilizat în mod intens sunt:
void Add(TKey, TValue), care adaugă o pereche în dicționar
bool ContainsKey(TKey), care returnează true dacă o cheie se află în dicționar, false în caz contrar
bool ContainsValue(TValue), similar cu ContainsKey, însă verifică existența valorii transmise ca parametru
bool RemoveKey(TKey), ce șterge o cheie din dicționar
void Clear(), care șterge toate perechile existente în dicționar
Clasa List<T> funcționează similar cu clasa ArrayList<T> din Java, în sensul că permite definirea unei colecții de obiecte de tipul T ce pot fi accesate prin indici.
Metodele importante folosite de către mine au fost:
void Add(T) pentru adăugarea unui element în listă
void Clear() pentru ștergerea tuturor componentelor
bool Contains(T) pentru verificarea existenței unui obiect în cadrul listei
int IndexOf(T) pentru a obține indicele la care se află un anumit element
bool Remove(T) pentru ștergerea unei singure componente.
void Sort() – pentru sortarea listei
Pentru construirea unui token utilizat pentru resetarea parolei unui utilizator al aplicației atunci când acesta își uită parola, am utilizat clasa RNGCryptoServiceProvider care implementează un generator de numere aleatoare . Singura metodă a acestei clase pe care am utilizat-o este:
void GetBytes(Byte[])
care umple vectorul de biți cu valori aleatoare. Voi exemplifica utilizarea acestei metode:
Figure
Pentru scrierea și citirea în/dintr-un fișier, limbajul C# pune la dispoziție clasele StreamReader și StreamWriter, a căror utilizare este extrem de simplă. Pentru scriere, respectiv citire, constructorii claselor arata în modul următor:
StreamReader(“cale_catre_fisier”)
StreamWriter(“cale_catre_fisier”)
Citirea de fișier s-a realizat linie cu linie în cazul meu de utilizare, astfel încât am utilizat metoda clasei StreamReader: ReadLine().
Scrierea în fișier se realizează utilizând metodele clasei StreamWriter: Write(String) și WriteLine(String)
ASP. NET
Motivul principal pentru care Microsoft a creat limbajul C# a fost pentru a oferi framework-ului ASP.NET o bază deosebit de puternică pentru a construi aplicații web. Așadar, ASP.NET reprezintă un framework creat de Microsoft pentru construirea intr-un mod extrem de ușor și rapid a unor pagini web independente de browser și dinamice. A fost lansat în anul 2002 odată cu lansarea versiunii 1.0 al framework-ului .NET. De asemenea, el este și framework-ul utilizat pentru construirea aplicației web Kharmah.
Una dintre caracteristicile principale care fac alegerea framework-ului ASP.NET o alegere inteligentă este ușurința de a construi interfețe grafice (pagini web) și de a le stiliza. Acesta pune la dispoziția programatorilor un toolbox ce conține elemente HTML și ASP (Active Server Pages). Framework-ul ASP.NET oferă un mod unic de constuire a paginilor web folosind acest toolbox prin posibilitatea de a “trage” elemenetele din listă și de a le poziționa în pagina web. Ce este și mai interesant este faptul că atunci când un element este poziționat în pagina web, este creat automat un ID (identificator) și i se setează atributul runat cu valoarea “server”, pentru ca acesta să poată fi vizibil de metodele din back-end care asigură funcționalitatea aplicației.
Figure – Toolbox-ul framework-ului ASP.NET
Mai mult, framework-ul ASP.NET permite vizualizarea paginii în modul design, care reprezintă grafic o previzualizare a paginii construite. De asemena, această fereastră permite programatorilor modificarea paginii (inserarea, repoziționarea, modificarea și ștergerea elementelor).
De asemenea, tot prin utilizarea acestei interfețe se pot selecta elementele componente ale paginii și se poate utiliza opțiunea de vizualizare a proprietăților, opțiune care deschide o nouă fereastră ce permite vizualizarea și editarea tuturor proprietăților ale elementului curent.
Figure – Fereastra de design a paginii web + tabel de proprietăți
Un alt avantaj deosebit de important oferit de framework-ul ASP.NET îl reprezintă instrumentele ce fac legătura într-un mod extrem de ușor cu baza de date și reprezentarea lor într-o manieră versatilă și personalizată. Aceste instrumente sunt: GridView (instrument care creează un tabel), ListView și Repeater.
ListView și Repeater-ul este sunt elemente extrem de utile pentru reprezentarea datelor într-un mod individualizat. Aceste instrumente oferă posibilitatea de a specifica un ItemTemplate prin intermediul căruia se stabilește modul în care o linie de date obținută printr-o cerere să fie reprezentată în cadrul paginii Web.
Pentru obținerea datelor care se doresc a fi reprezentate, aceste instrumente utilizează un obiect numit SqlDataSource, ce face legătura cu baza de date prin intermediul unui Connection String. Acestui obiect trebuie să i se specifice cererea SQL și, eventual, locul din care sunt preluate valorile parametrilor cererii în cazul în care aceștia există. Specificarea parametrilor se face plasând simbolul @ înaintea numelui parametrilor. Iar valorile acestora pot fi setate fie din QueryString, fie din Session, fie din alte elemente HTML sau ASP din cadrul paginii.
Voi exemplifica în continuare utilizarea instrumentului ListView în cadrul aplicației dezvoltate de către mine pentru afișarea tuturor recenziilor corespunzătoare unui joc:
Figure – Utilizarea ListView-ului
Efectul pe care îl are acest cod în cadrul paginii web este:
Figure
Utilizarea celorlalte instrumente înrudite cu ListView se face într-un mod similar, fiecare astfel de obiect având o secțiune de definire a unui ItemTemplate
TinyMCE
Aplicația pe care o dezvolt oferă utilizatorilor posibilitatea de a insera text personalizat în anumite secțiuni ale aplicației. De exemplu, pentru a realiza o descriere a jocului într-un mod mai “liber” mai atractiv la vedere și mai animat, moderatorii se pot folosi de un instrument deosebit de versatil, și anume TinyMce.
Ce face mai exact TinyMce? Acest instrument utilizează o funcție, pe care o voi executa ulterior, pentru a altera controalele, în cazul meu textarea-urile ce au clasa setată “tinyText”, astfel încât să apară similar cu o pagină de redactare a aplicatiei Microsoft Word, precum în următoarea imagine:
Figure – Interfața instrumentului TinyMce
Am decis să folosesc acest instrument întrucât oferă o vesatilitate considerabilă și permite o stilizare frumos structurată și placută. În figura 7 se observa numeroase instrumente care sunt puse la dispoziția utilizatorilor. Primele două simboluri de pe a doua linie din bara, cele două săgeți reprezintă butoane pentru “undo” și “redo”.
De asemenea, acest editor este prevăzut cu instrumente pentru modificarea font-ului, a culorii textului, a dimensiunii, inserarea listelor ordonate și neordonate, introducerea emoticoanelor. Mai mult, el oferă posibilitatea introducerii imaginilor și a videoclipurilor. Acesta este unul din motivele principale pentru care am dorit să utilizez acest instrument. Deoarece aplicația conține secțiuni în care utilizatorii pot cere ajutor sau vor să discute anumite bug-uri ale jocurilor, ei au posibilitatea de a insera imagini pentru a se putea face cat mai înteleși.
Practic, TinyMce este un instrument ce transforma orice control de tip input într-un document Microsoft Word care este deosebit de ușor de prelucrat. O funcție interesantă pe care o aduce este opțiunea de a previzualiza conținutul editat în cadrul casetei, după cum se poate vedea în următoarea figura:
Figure
Să vizualizăm acum funcția care face toate aceste lucruri posibile:
Figure
Funcția tinymce.init se execută o singură data la deschiderea unei pagini și are drept efect, în cazul aplicației dezvoltate de mine, transformarea tuturor controalelor ce aparțin clasei “tinyText” în editorul de text. Setarea selectorului menționează ce elemente HTML vor fi transformate.
Pentru atributul toolbar se specifică ce butoane să apară în bară, adică ce instrumente sunt puse la dispoziția utilizatorilor.
Atributul “plugins” conține diversele funcționalități de care dispune editorul printre care se numără: posibilitatea de a previzualiza documentul, utilizarea emoticoanelor, inserarea de imagini și videoclipuri, etc. Un plugin important este plugin-ul pentru “autoresize”. Acest plugin asigură faptul că editorul va ocupa tot spațiul pe care îl are la dispoziție la orice moment în cadrul unui container, ceea ce conduce la un aspect responsive al editorului.
De asemenea, design-ul editorului poate fi schimbat prin setarea atributului “skin”. Pentru crearea unui design, tinyMCE oferă o aplicație web ce permite utilizatorilor editarea design-ului și salvarea schemei. Pagina pe care utilizatorii o pot accesa în scopul obținerii unui alt design pentru editor este: http://skin.tinymce.com/.
Algoritmul de recomandare
Recomandarea jocurilor se numără printre cele mai importante funcționalități ale aplicației pe care le dezvolt și, consider eu, o funcționalitate extrem de importantă în cadrul oricărei aplicații Web. Recomandarea permite, în principiu, identificarea într-un ritm mai rapid a subiectelor ce prezintă interes pentru un utilizator.
Recomandarea pe care am integrat-o oferă utilizatorilor aplicației posibilitatea de a naviga mai ușor printre jocurile care ar putea să intereseze pe aceștia atât ca gen, cât și ca alte atribute (spațiu ocupat pe disc, sistemul de operare pe care poate rula, etc.)
Așadar, recomandarea nu se bazează doar pe o simplă căutare în baza de date după anumite criterii, ci are ca bază teoretică “cosine similarity”. Ideea de bază este că pentru fiecare atribut al unui joc se creează un vocabular, format din totalitatea cuvintelor conținute de toate jocurile din aplicației în dreptul atributului respectiv. Apoi, pe bază acestor vocabulare se construiesc vectorii aferenți fiecărui joc după ideea: se parcurge în ordine fiecare cuvânt ci din concatenarea tuturor vocabularelor și se pune pe poziția i 1, dacă acel cuvânt apare în descrierea atributului jocului, 0 în caz contrar.
Recomandarea am decis să o construiesc pe pagina fiecărui joc, unde se vor calcula si valorile cosinusului dintre jocul respectiv și toate jocurile regăsite în baza de date. Formula cosinusului dintre doi vectori este:
unde
v, w sunt vectori
<v, w> reprezintă produsul scalar dintre vectorii v și w
reprezintă norma vectorului cu componentele v(i)
Așadar, pașii algoritmului sunt:
crearea vocabularelor pentru fiecare atribut
crearea vectorilor corespunzători fiecărui joc
calcularea cosinusului dintre vectorul jocului pe a cărui pagină se regăsește utilizatorul și vectorii celorlalte jocuri existente
afișarea jocurilor în cadrul unui carousel (pe care l-am exemplificat mai sus în secțiunea cu utilizarea Bootstrap-ului) în ordine descendentă în funcție de valoarea cosinusului
Metodele care construiesc totalitatea vocabularelor atributelor sunt:
Figure – Funcția de creare a dicționarului
Figure 15 – Funcția de adăugare a cuvintelor în alfabet
Pentru memorarea atributelor am utilizat clasa Dictionary<Tkey, Tvalue>. În fiura 14 se observă că inițializarea dictionarului se realizează introducand 5 perechi de cifre și șiruri vide. Semnificația acestor numere se regăsește în figura 15. Atunci când se preiau atributele fiecărui joc din baza de date prin cererea Sql “txt1”, ordinea acestor atribute este: 0 – os (sistemul de operare), 1 – procesorul, 2 – memoria RAM, 3 – grafica (placa video), 4 – spațiul pe care îl ocupă pe disc, iar valoarea 5 corespunde genului; aceasta se adaugă separat de celelalte atribute, deoarece denumirea lor se găsește în tabela Gen (jocurile pot aparține mai multor genuri).
Așadar, pentru fiecare joc în parte se apelează metoda “adaugaAtribute”, care adaugă fiecare cuvânt al fiecărui atribut în dictionar[i]. Înainte de adăugare se verifică faptul că respectivul cuvânt nu reprezintă un cuvant de legătură, de exemplu “or”, “and”, “Windows” și nici nu se află deja în componenta dicționarului pentru cheia respectivă. Astfel, pentru fiecare cheie i a dicționarului, valoarea acesteia va conține cuvinte unice.
Trecem mai departe la funcțiile care construiesc vectorii corespunzători fiecărui joc:
Figure
Figure – Funcția care creează fiecare vector în parte
Metoda “creeazaVector” se apelează pentru fiecare id din tabela Jocuri ale bazei de date, iar vectorul obținut ca rezultat este salvat în variabila “vectoriJocuri”, o variabilă statică de tip Dictionary<int, List<int>>, care conține drept cheie id-urile jocurilor și drept valoare vectorul corespunzător jocului. În primă fază se construiește șirul “sir” ce conține toate cuvintele din cadrul atributelor jocului cu id-ul “id_joc”. Apoi, printr-o parcurgere a tuturor cuvintelor din dicționar, se verifică existența cuvântului în șirul nou creat folosindu-mă de metoda “Contins(String s)” a clasei “String”. Dacă există, se adaugă in vector 1 și 0, în caz contrar. Parcurgerea cuvintelor din dicționar în aceeași ordine pentru fiecare joc asigură, deci, corectitudinea construirii acestora.
În sfârșit urmează secvența de cod ce construiește valorile cosinusului dintre vectori și inserarea lor în carousel-ul gol existent deja în cadrul paginii:
Figure – Calcularea cosinusului dintre doi vectori
Această metodă este apelată de pe pagina jocului, unde sunt calculate toate valorile cosinusului dintre vectorul jocului curent și vectorii celorlalte jocuri, ca mai apoi jocurile să fie adăugate în carousel-ul gol, deja existent în cadrul paginii, în ordinea descendentă a valorilor cosinusului.
Figure – Calcularea cosinusurilor și adăugarea vectorilor în carousel
Algoritmul de clasificare de opinii
În această secțiune voi vorbi despre unul dintre scopurile principale pe care aplicația pe care o dezvolt trebuie să îl îndeplinească, și anume analiza opiniilor despre jocurile pe calculator.
Această analiză nu se bazează pe un simplu sistem de votare a jocului regăsit pe o mare parte din aplicațiile web existente, ci se se realizează prin examinarea recenziilor introduse de catre utilizatorii aplicației în secțiunea fiecărui joc dedicată acestui lucru și clasificarea recenziilor în două mari categorii: recenzii pozitive și recenzii negative. Procedeul de clasificare se efectuează utilizând concepte de “machine learning”, mai exact pe noțiunea de SVM (Support Vector Machines), care în cadrul aplicației folosește în loc de kernel function, string kernels . Voi explica în detaliu conceptele folosite în întocmirea clasării recenziilor.
Machine Learning. SVM
După cum am menționat mai devreme, SVM reprezintă o ramură a conceptului de machine learning, dar nu am specificat ce semnifica acest concept din urmă. În știința calculatorului, machine learning reprezintă abilitatea calculatorului de a face predicții asupra datelor în urma unui proces de învățare (antrenare) prin intermediul căruia se creează un model pe baza unor date care formează mulțimea de antrenare.
În materia inteligenței artificiale, învățarea este de două tipuri:
invățare nesupravegheată, care pe baza unei mulțimi de antrenare care nu conține etichete, se încearcă identificarea etichetelor
invățare supravegheată, care presupune clasificare și regresie liniară.
Deoarece în cadrul proiectului de licență am folosit conceptul de invățare supravegheată pentru a invăța calculatorul să clasifice recenziile în cele două categorii specificate mai sus, voi vorbi în continuare doar despre acest tip de invățare. Clasa recenziilor pozitive va fi notată cu +1, iar clasa recenziilor negative va fi notată cu -1.
În cadrul invățării supravegheate, mulțimea de antrenare are forma:
Sn = {(xi, yi) | i = }
unde xi este un vector de trăsături din Rm, iar yi ∈ {-1, 1} reprezintă o etichetă.
Pentru construirea mulțimii de antrenare am folosit drept trăsături numărul de apariții ale subșirurilor de lungime 6, conform kernelului de tip p-spectrum, trăsături care mai apoi au fost supuse procedeului de normalizare. Această alegere este explicată în cadrul secțiunii următoare.
În general, un SVM reprezintă un model de învățare care studiază date cu scopul de a clasifica datele sau pentru o analiză a regresiei. Deoarece în cadrul aplicației mele este folosit pentru a delimita recenziile pozitive de cele negative, SVM-ul utilizează o mulțime de antrenare care conține etichete și trăsături care au fost menționate mai sus și care vor fi detaliate în secțiunea următoare și rolul acestuia este de a clasifica noi recenzii din mulțimea de testare, a cărei construcții este similară cu cea a mulțimii de antrenare, însă aceasta trebuie să conțină noi date. Pe această mulțime se verifică performanța clasificatorului, rata cu care acesta etichetează corect datele de testare.
Cum realizează SVM-ul acest lucru? El transpune trăsăturile mulțimii de antrenare într-un hiperplan de dimensiune mare și caută o dreaptă care să separe aceste punctele care aparțin unor categorii diferite. Această dreaptă este aleasă astfel încât distanța dintre dreaptă și punctele cele mai apropiate de fiecare parte a acesteia să fie maximizată. Pentru a realiza acest lucru, se calculează ponderi pentru fiecare trăsătură astfel încât trăsăturile care au un impact mai mare asupra clasificării în cele două categorii să aibă ponderi mai mari, oferindu-le o importanță mai mare față de ponderile care nu contribuie în mod semnificativ.
String kernels
String kernels sunt, în esență, kernel functions. Însă particularitatea care deosebește aceste două noțiuni este faptul că dacă kernel functions lucrează cu vectori de numere, string kernels utilizează șiruri (de simboluri) cu lungime variabilă. În cadrul lucrării de licență pe care am realizat-o, împreună cu SVM care reprezintă o metodă kernel, ele sunt folosite pentru clasificarea recenziilor postate de către utilizatori.
Fiind dată o mapare , o funcție kernel reprezintă o funcție
Dintre cele mai utilizate funcții kernel se numără:
kernel-ul liniar:
kernel-ul polinomial:
kernel RBF (Radial Basis Function) :
Așadar, în cazul string kernel-urilor, x și y reprezintă șiruri de caractere. Există mai multe variante de string kernels care pot fi utilizate împreună cu SVM, dintre care precizez:
p-spectrum Kernel – fiind date două șiruri x și y, acest tip de kernel calculeaza produsul scalar dintre numărul de apariții ale tuturor subsirurilor de lungime p ce intră în componența celor două șiruri. Formula kernel-ului p-spectrum este dată de:
unde reprezintă numărul de apariții ale subșirului s în șirul X, iar x și y sunt două șiruri.
p-grams presence bits kernel – acest tip de kernel este similar cu cel de mai sus, singura diferență dintre cele două constând în faptul că kernel-ul de tip p-spectrum presence bits ia in considerare doar apariția subșirurilor, în loc de numărul de apariții. Formula kernel-ului de intersecție este dată de:
unde ia valoarea 1, dacă subșirul s există in șirul x, 0 în caz contrar.
kernelul de intersectie – este tipul de kernel care spre deosebire de celelalte două de mai sus, calculează numărul minim de apariții ale subșirurilor de lungime p din cadrul celor două șiruri primite ca parametri. Este utilizat cu precădere pentru clasificarea obiectelor din imagini. Formula acestui tip de kernel este dată de:
unde x și y sunt două șiruri de simboluri, iar reprezintă numărul de apariții al subșirului s în șirul x.
Relația dintre aceste trei tipuri de string kernel este dată de:
Rolul pe care îl îndeplinește un string kernel este acela de a transpune recenziile într-un spațiu dimensional al trăsăturilor mai mare. Astfel, împreună cu o metodă kernel de învățare, în cazul de față SVM, se calculează o serie de ponderi corespunzătoare fiecărei trăsături pentru a identifica subșirurile care sunt relevante pentru delimitarea pe cele două categorii.
Alegerea tipului de string kernel depinde foarte mult de natura problemei. În cazul aplicației mele, unde calculatorul trebuie să învețe să separe recenziile în cele două clase, frecvența subșirurilor este foarte importantă. De aceea, pentru antrenare folosesc primul tip de string kernel, și anume p-spectrum Kernel. Pentru alegerea lui p (lungimea substring-urilor), întrucât limba care se folosește în cadrul aplicației este engleza și datorită faptului că lungimea medie a unui cuvânt în engleză este 4.46, a trebuit să iau în considerare subșiruri de lungime 5-8 . Astfel, am testat toate valorile lui p din acest interval și în urma antrenării am obținut cea mai mare acuratețe, de 85% pentru valoarea 6. Așadar, am păstrat valoarea lui p ca fiind 6.
O etapă esențială ce trebuie parcursă pentru realizarea unei mulțimi de antrenare corecte este normalizarea datelor. Primul pas pe care l-am realizat pentru normalizarea datelor a fost să trec toate caracterele în lowercase și, de asemenea, să elimin toate semnele de punctuație, toate spațiile de tip rând nou, tab și toate spațiile albe, consecutive. Deoarece recenziile pot avea lungimi diferite și odată cu aceasta și numărul de apariții ale subșirilor, clasificatorul nu ar putea funcționa cum trebuie, întrucât pentru două recenzii a căror diferență de lungime este foarte mare, numărul de apariții ale subșirurilor semnificative ar putea fi foarte diferite, ceea ce nu este consistent. De aceea, pentru a elimina problema lungimii, fiecare trăsătură (pereche de tipul subșir : nr_apariții) trebuie modificată astfel încât numărul de apariții să fie împărțit la norma vectorului format din toate numerele de apariții ale subșirurilor de pe linia unei recenzii. Astfel, în loc să avem ca trăsături numărul de apariții ale subșirurilor, vom avea procentajul de contribuție a numărului de apariții în cadrul vectorului
Studiu de caz: Movie Reviews
Deoarece mulțimea de testare trebuie să fie disjunctă cu mulțimea de antrenare, am decis să fac antrenarea clasificatorului utilizand un set de 1400 de recenzii din domeniul filmelor, dintre care 700 pozitive și 700 negative.
Voi reda în continuare pașii pe care i-am urmat pentru crearea mulțimii de antrenare. Menționez faptul că recenziile au fost salvate în două foldere diferite, “pos” și “neg” și a trebuit să le parcurg separat. Însă codul este similar pentru ambele tipuri de recenzii. De aceea am ales să exemplific codul doar pentru unul dintre ele în ceea ce urmează.
Primul pas a fost crearea alfabetului, care conține toate subșirurile de lungime 6 din fiecare recenzie:
Figure – Crearea alfabetului
Codul este unul simplu: am șters semenele de punctuație și caracterele care nu fac parte din alfabetul englez utilizând funcția “eliminaPunctuatie(string sir)”, apoi pentru fiecare subșir de 6 caractere din recenzie, testez dacă există deja în lista alfabet (pe care o utilizez doar pentru a verifica daca există deja un subșir sau nu), iar dacă nu îl memorez și îl scriu în fișier.
Al doilea pas a fost construirea mulțimii de antrenare pe baza kernelului p-spectrum.
Figure – Crearea mulțimii de antrenare
De aseenea, mulțimea de antrenare o voi salva într-un fișier. Așadar, fiecare componentă a mulțimii de antrenare este inițializată cu eticheta corespunzătoare (în figură am pus 1, întrucât se citesc recenziile pozitive) și de asemenea, din recenzie sunt eliminate toate caracterele ce nu intră în componența alfabetului englez sau nu este cifră. Apoi, pentru fiecare cuvânt din alfabet, testez dacă recenzia îl conține. Dacă da, adaug la componenta curentă trăsătura “index:nr” unde index reprezintă indicele cuvântului din mulțimea de antrenare, iar nr reprezintă numărul de apariții ale cuvântului în recenzie. Codul pentru recenziile negative este similar, singura diferență fiind faptul că se inițializează cu -1 componenta curentă a mulțimii de antrenare
Observăm faptul că am ales ca mulțimea de antrenare să fie de forma {(yi, xi) | } deoarece acesta este formatul pentru biblioteca LIBSVM pe care am folosit-o pentru antrenarea clasificatorului. Voi explica folosirea acestuia în secțiunea următoare. De asemenea, remarc faptul că această metodă nu normalizează mulțimea de antrenare.
Pasul al treilea, și ultimul, constă în normalizarea vectorilor mulțimii de antrenare.
Figure – Normalizarea vectorilor
Am început prin construirea normei vectorului pentru fiecare pereche de eticheta + vector din cadrul mulțimii de antrenare. După ce norma a fost calculată, se creează o noua linie care se modifică doar prin faptul că numărul de apariții este împărțit cu norma vectorului.
LIBSVM
LIBSVM reprezintă o bibliotecă ce implementează funcțiile de bază ale unui SVM. Această bibliotecă a fost creată de către Chih-Chung Chang și Chih-Jen Lin pentru o mare parte din limbajele de programare, printre care: C#, ASP.NET, Python, C++, etc.
Oferă un mod foarte ușor de utilizare și de înțelegere a conceptelor de bază ale SVM-ului. Eu am utilizat această bibliotecă expres pentru antrenarea clasificatorului care diferențiază recenziile pe cele două categorii, iar dintre funcțiile pe care le ofera am folosit funcția de antrenare, de calcul a acurateții și de predicție a etichetei și a distanței dintre punct și hiperplanul de separare. Voi exemplifica în continuare utilizarea acestor metode.
Figure – Antrenarea clasificatorului
Primul pas pentru antrenarea clasificatorului este să se citească mulțimile de antrenare și de testare, iar biblioteca LIBSVM permite acest lucru prin intermediul metodei ReadProblem(string cale_către_fișier) a clasei ProblemHelper. Această clasă conține o metodă ReadAndScaleProblem(string cale_către_fișier) care scalează de asemene vectorii de trăsături, însă eu nu am utilizat această clasă pentru că am făcut-o manual.
Clasa prin intermediul căreia se definește un clasificator este C_SVC a cărei constructor primește ca parametri mulțimea de antrenare (în cazul meu prob), tipul de kernel pe care să îl utilizeze (liniar), valoarea parametrului C > 0 care reprezintă parametrul de penalizare, cache_size (100) și o valaore bool care are următoarea semnificație:
dacă este setată ca false, clasificatorul doar va fi antrenat, fără a face predicții asupra etichetelor (deci se calculează doar ponderile), iar atunci când este utilizat pentru a prezice o etichetă va returna întotdeauna aceeași valoare pentru orice componentă a mulțimii de antrenare;
dacă este setată ca true, clasificatorul va fi antrenat și va face predicții asupra etichetelor.
Acuratețea predicțiilor asupra mulțimii de antrenare se obține apelând metoda GetCrossValiDationAccuracy(int nr_fold), unde nr_fold reprezintă numărul de submulțimi în care va fi divizată mulțimea de antrenare. Pentru a identifica valoarea optimă pentru C, am testat mai multe puteri ale lui 10. Pentru C = 5 am obținut cea mai mare valoare de 85%.
Prezicerea etichetei unei recenzii din mulțimea de antrenare se realizează apelând metoda double Predict(svm_node[] x) unde x reprezintă o componentă a mulțimii de testare. Mai mult, clasa C_SVC oferă metoda:
Dictionary<int,double> PredictProbabilities(svm_node[] x)
care returnează pentru fiecare clasă probabilitatea ca recenzia x să aparțină acesteia, care este echivalentă într-o oare măsură ca fiind distanța de la punct la hiperplanul de spearare determinat de ponderile clasificatorului.
Deoarece mulțimea de antrenare este mare (1400 de recenzii), antrenarea ține în jur de 3 ore, fapt ce m-a determinat ca după antrenare să salvez modelul într-un fișier extern. Metoda care permite exportarea unui model către un fișier xml este:
void export(string cale_către_fișier)
Fișierul în care este exportat modelul îl utilizez apoi în cadrul aplicației mele pentru a defini clasificatorul la deschiderea aplicației și a-l salva apoi în Application[“svm”], de unde îl voi extrage atunci când o recenzie va fi încărcată pentru a calcula punctajul pe care aceasta îl oferă jocului si apoi pentru a determina nota jocului. Această parte va fi explicată în capitolul următor în cadrul prezentării paginii de afișare a recenziilor unui joc.
Descrierea aplicației
După cum am menționat în introducere, aplicația Kharmah este o aplicație web ce are ca scop principal prezentarea și analiza opiniilor despre jocurile pe calculator. Aplicația vine în ajutorul persoanelor care se află în căutare de jocuri noi, pe care le pot descoperi într-un mod foarte ușor în cadrul paginii de start a aplicației. Pentru a perfecționa identificarea jocurilor de interes pentru utilizatori, aplicația este înzestrată cu un sistem de recomandare a jocurilor înrudite cu cele accesate de către aceștia.
De asemenea, această aplicație funcționează asemănător unui forum, în sensul că un utilizator poate să creeze topic-uri în două secțiuni diferite: secțiunea de help (unde se pot pune întrebări în legătură cu jocul respectiv și de asemenea, se pot afișa ghiduri utile pentru acesta) și secțiunea de bug-uri. În acest fel, utilizatorii pot primi răspunsuri pentru eventualele întrebări (în cadrul secțiunii help) sau pot discuta metode de evitare a bug-urilor sau, de ce nu, metode de a-l exploata.
Totodată, fiind o aplicație care se dorește a fi un mediu prin intermediul căruia utilizatorii pot intra în contact și se pot ajuta reciproc, aceasta este prevăzută cu un sistem de mesagerie intern, pentru a încuraja socializarea dintre membrii comunității.Astfel, utilizatorii au posibilitatea de a comunica în privat în cadrul aplicației fără a fi nevoiți să transmită adresele acestora de email.
În continuare, în cadrul acestei secțiuni vor fi prezentate structura bazei de date, cazurile de utilizare, paginile aplicației, precum și implementări pe care le consider mai interesante.
Specificații tehnice
În vederea dezvoltării aplicației pe care am realizat-o pentru lucrarea de licență am folosit limbajul C# împreună cu framework-ul ASP.NET. Motivul pentru care am ales acest framework este acela că el oferă posibilitatea de realizare a aplicațiilor web într-un mod foarte curat și modern și, nu în ultimul rând, foarte ușor.
Un avantaj important în utilizarea acestui framework reprezintă utilizarea instrumentelor de tip “Validator” pe care le pune la dispoziție. De exemplu, prin simpla adăugare a unui “RequiredFieldValidator” și setarea lui pentru verificarea unui textbox se împiedică apelarea funcției corespunzătoare unui buton dacă acel câmp nu a fost completat, fără a fi nevoit să fac verificări asupra câmpurilor. Și acesta nu reprezintă singurul avantaj; alți validatori sunt deosebit de utili pentru validarea datelor, de exemplu CompareValidator (care se asigură că două câmpuri sunt egale) sau RegularExpressionValidator care permite definirea unei expresii regulate și verificarea input-ului unui câmp. Mai mult, RegularExpressionValidator conține și expresii regulate predefinite, precum: adresă de email, adresă a unei pagini web, etc.
Deoarece există mai multe tipuri de utilizatori cu drepturi diferite, la logare salvez username-ul, parola criptată și tipul de utilizator în sesiune. Și pentru fiecare pagină care poate fi accesată doar anumite tipuri de utilizatori, verific faptul că aceștia sunt logați, folosind username-ul și parola din sesiune dacă există, și verific și tipul de utilizator pentru a verifica autorizarea acestuia pe pagină.
De exemplu, pe pagina adminului, care conține informații despre conturile altor utilizatori și permite acestora manipularea acestora, un vizitator, un utilizator normal sau un moderator nu ar trebui să aibă acces la această pagină. De aceea, prin intermediul anumitor funcții mă asigur în primul rând că utilizatorul este logat și, de asemenea, că tipul contului este admin. În cazul în care una din aceste condiții nu este satisfăcută utilizatorul este întors către pagina de Login, unde toate informațiile din cadrul sesiunii sunt șterse.
Funcția pe care am implementat-o pentru verificarea faptului că utilizatorul este logat este:
Figure – Funcție de verificare dacă un utilizator este conectat
Practic, această funcție constă doar din niște simple interogări ale bazei de date pentru verificarea faptului că parola criptata introdusă de către utilizator coincide cu parola din baza de date (care la înregistrare a fost introdusă și ea în format criptat).
Deoarece aplicația mea permite inserarea de hipertext, poze, videoclipuri, cuvinte boldate, colorate, etc., care mai apoi sunt postate pe paginile aplicației sau pe paginile altor utilizatori (ca mesaje), trebuie să validez conținutul pe care aceștia îl pot insera în cadrul câmpurilor. Atacurile ce pot avea loc în acest sens poartă denumirea de “Cross-Site Scripting” attacks (XSS).
Atacurile de tip “Cross-Site Scripting” se întalnesc în cadrul aplicațiilor în care informația adăugată de către utilizatori este afișată în paginile aplicației. Astfel, un atacator poate introduce scripturi care ar putea obține accesul, de exemplu, la valorile cookies-urilor, a variabilelor din sesiune, și ar putea să le scrie pe o pagină cunoscută de ei. Pentru a preveni acest lucru, input-ul utilizatorilor trebuie validat sau codificat.
Deoarece aplicația mea permite inserarea de hipertext prin intermediul editorului TinyMCE, codificarea tag-urilor ce sunt introduse prin intermediul lui nu le pot codifica pentru că acestea nu ar mai avea efect. De exemplu, în conținutul textului ar apărea: “<b> Salut, Remus </b>”. Metoda de prevenire pe care am decis să o utilizez este validarea input-ului.
Tag-urile care pot pune în pericol aplicația sunt tag-urile script și iframe. De aceea am implementat o funcție care verifică existența unor astfel de tag-uri și dacă le găsește, conținutul introdus nu va fi inserat în baza de date, iar utilizatorul va primi o eroare “Invlid content”. Funcția pe care am implementat-o în acest scop este:
Figure – Funcția de verificare a conținutului introdus de către utilizator
După cum se observă din figură, cuvântul iframe este chiar interzis. Simpla prezență a acestuia induce ideea unui conținut rău-voitor. Tot ce mai urmează să verific este faptul că un caracter < nu este urmat de cuvântul script sau de mai mute spații albe și cuvântul script, verificare pe care o realizez cu ajutorul funcției string[] Split (string[] separatori).
Structura bazei de date
Baza de date este formată din 13 tabele, după cum se poate observa din următoarea diagramă, care prezintă atributele fiecărei tabele, precum și dependențele dintre acestea:
Figure – Diagrama bazei de date
Se observă faptul că toate relațiile many-to-many au fost înlăturate și transformatte în relații one-to-many. De exemplu, tabela Gen, care repzintă genul unui joc. Deoarece un joc poate aparține mai multor genuri și același gen poate fi avut de către mai multe jocuri, initial această relație a fost many-to-many. Am soluționat această problemă prin adăugarea unei noi tabele, Apartine, care face legătra dintre un joc și genurile sale.
Se observă în figura 23 că există o tabelă Aprecieri. Inițial, o recenzie avea ca atribut numarul de aprecieri sau deprecieri, însă trebuia controlat faptul că o persoană nu poate aprecia de mai multe ori aceeași recenzie și, de asemenea, dacă o persoană care initial apreciase o recenzie acum o depreciază, trebuie ca aprecierea să îi fie ștearsă. Tipul acestei tabele poate avea două valori: like/dislike.
O altă tabelă pe care țin să o explic este tabela corespunzătoare topicului. În cadrul aplicației mele, topic-ul se poate creea în secțiunea de help și în secțiunea de bug-uri, iar acestea sunt similare indiferent de secțiunea în care se află. De aceea am hotărât ca astfel de topic-uri să fie memorate în cadrul aceleiași tabele. Pentru a face totuși diferența între ele, am adăugat coloana tip care specifică secțiunea din care face parte: intrebare sau bugreport.
Cazuri de utilizare
Aplicația web pe care am dezvoltat-o administrează 4 tipuri de utilizatori:
Utilizatori neînregistrați
Utilizatori înregistrați
Moderatori
Admini
Utilizatorii neînregistrați (adică acele personae care nu si-au creat un cont în cadrul aplicației reprezintă acea clasă de utilizatori cu cele mai puține privilegii. Aceștia pot vizualiza doar informații în legătură cu jocurile pe care aplicația le prezintă: descrierea jocului, discuțiile ce au loc în cadrul secțiunilor de help și de bug-uri, recenziile jocului.
Utilizatorii înregistrați, în plus față de cei neînregistrați, pot creea noi topic-uri, pot vizualiza profilurile altor utilizatori, pot transmiete mesaje private, etc.
Moderatorii sunt persoanele care se ocupă cu administrarea paginilor (adăugarea jocurilor, ștergerea topic-urilor, a răspunsurilor la diferite secțiuni)
Adminii reprezintă tipul de utilizator care are acces la conturile celorlalte persoane și pot oferi drepturi sau pot înlătura drepturi ale acestora.
Voi prezenta mai detaliat cazurile de utilizare pentru fiecare tip de utilizatori prin cadrul unor “user stories”:
Descrierea paginilor
În cadrul acestei secțiuni voi prezenta paginile aplicației Kharmah, funcționalitățile pe care acestea le oferă și, unde este mai interesant, detalii tehnice despre modul în care am implementat funcționalitățile respective.
Pagina de login
În cadrul acestei pagini, un utilizator poate completa câmpurile pentru username și parola, apoi apasă pe butonul “Login” pentru a se autentifica. În cazul în care username-ul introdus nu este găsit în baza de date sau combinația de username-parolă nu este validă, utilizatorului I se transmite un mesaj precum se vede din imagine. Dacă utilizatorul și-a uitat parola contului, el are posibilitatea de a-si reseta parola dând click pe link-ul “Forgot password?” care îl va redirecționa către pagina de cerere a resetării parolei.
În cazul în care utilizatorul nu are cont, el poate să acceseze pagina principală ca vizitator dând click pe Home în bara de navigație sau iși poate crea un cont dând click pe “Register” sau accesând link-ul “Sign Up Here”.
Pagina de înregistrare
Figure – Pagina de înregistrare
Această pagină prezintă utilizatorului un formula prin intermediul căruia el completează toate câmpurile (fiind toate mandatorii), după care apasă pe butonul Register pentru a finaliza înregistrarea. În cazul în care email-ul sau username-ul pe care le introduce există deja în baza de date, utilizatorului i se va afișa un mesaj de eroare prin care I se va transmite acest lucru.
De asemenea, adresa de email introdusă trebuie să fie una validă. Pentru verificarea validității acesteia, am folosit un instrument oferit de framework-ul ASP.NET: RegularExpressionValidator căreia i-am setat ca expresie regulată: “\w+([-+.']\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*” și ca element de verificare textbox-ul aferent adresei de email.
Deoarece cele două parole introduse de către utilizator trebuie să fie identice, am folosit instrumentul “CompareValidator” care permite compararea valorilor a două câmpuri și afișarea unui mesaj atunci când acestea sunt distincte.
După ce utilizatorul a apăsat pe butonul “Register” contul va fi adăugat, însă starea lui va fi “dezactivat”. Pentru a-și activa contul, el va trebui să acceseze link-ul de activare a contului primit ca mesa pe adresa de email.
Cum am realizat acest lucru? Primul pas a fost crearea unei tabele “Activari” care să memoreze username-ul contului dezactivat și token-ul de activare care e trimis prin intermediul mesajului.
Pentru crearea token-ului, am folosit următoarea funcție:
Figure
unde AvailableCharaters reprezintă caracterele cu care doresc să creez token-ul
static readonly char[] AvailableCharacters = {
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-', '_'
};
Clasa RNGCryptoServiceProvider pune la dispoziție metoda:
void GetBytes(byte[] data)
care umple un vector de tip byte cu biti random. În funcție de acești biți rezultați, am folosit operatorul “%” pentru a obține caractere din vectorul AvailableCharacters.
Figure – funcția de trimitere a unui email de activare
Pentru trimiterea email-ului către utilizator am utilizat clasele MailMessage și SmtpClient pentru definirea mesajului și autentificarea expeditorului. Prin intermediul obiectului de tip MailMessage se rețin informațiile cu privire la mesaj: destinatarul, subiectul, conținutul mesajului, iar clasa SmtpServer asigură transmiterea mesajului folosind un cont a cărui date trebuie furnizate. Port-ul 587 este port-ul sistemului de mesagerie Gmail.
Atunci când link-ul este accesat, se utilizează token-ul din query string pentru identificarea contului și pentru activarea acestuia.
Pagina de cerere a resetării parolei
În cadrul acestei pagini utilizatorul trebuie să completeze adresa de email al contului pentru care a uitat parola. În urma apăsării butonului “Reset Password”, acesta va primi pe adresa de email un link către pagina de resetare propriu-zisă a parolei, link care are atașat token-ul aferent contului pentru care se face resetarea parolei. Ca la activarea contului de mai sus, se generează un token care este memorat împreună cu username-ul și data la care a fost introdus în baza de date pentru a putea fi folosit la resetarea propriu-zisă a parolei.
Pagina de resetare a parolei conține doar formularul:
Figure – Formularul de resetare a parolei
prin a cărei completare se realizează schimbarea parolei. După ce schimbarea a avut loc cu success, linia token-ului din tabela “Resetări” este ștearsă. De asemenea, dacă utilizatorul încearcă să acceseze această pagină cu un token invalid sau expirat (adică a trecut mai mult de o zi de la cererea de resetare), acestuia i se aduce la cunoștință eroarea care s-a produs.
Pagina principală a aplicației
Figure – Pagina principală
Această pagină oferă utilizatorului o perspectivă asupra jocurilor care sunt prezentate de către aplicația dezvoltată de mine. Prin intermediul unui GridView, element oferit de către framework-ul ASP.NET, utilizatorul are acces la informațiile generale despre jocurile existente: titlu, gen, editorul care a adăugat jocul, precum și nota jocului calculată pe baza recenziilor oferite de către utilizatori.
De asemenea, utilizatorul are la îndemână două câmpuri prin intermediul cărora poate să filtreze datele din tabel:
Un element de tip DropDownList (instrument al framework-ului ASP.NET) care conține lista tuturor genurilor de jocuri
Un TextBox prin intermediul căruia utilizatorul poate căuta jocurile după titlul acestora
Din figura 29 se observă faptul că utilizatorul este logat. De aceea, în bara de navigație acesta mai are vizibile două pagini pe care le poate accesa, și anume: Profiles, Messages. În dreptul tab-ului Messages se observă existența cifrei 1. Aceea reprezintă o notificare a utilizatorului și semnifică numărul de mesaje necitite.
Pagina mesajelor
Figure – pagina de mesaje
Această pagină informează utilizatorul asupra mesajelor pe care le are și este accesibilă doar utilizatorilor logați. Pentru ca acesta să identifice mesajele necitite, există două tipuri de iconite. Iconița plicului deschis semnifică faptul că mesajul a fost vizualizat, iar iconița plicului inchis faptul că mesajul nu a fost vizualizat. Citirea unui mesaj se face dând click pe iconiță, situație în care va apărea un panou cu mesajul primit.
Compunerea unui mesaj și trimiterea acestuia se realizează dând click pe butonul “Compose a message” care va deschide un modal prin care utilizatorul completează câmpurile necesare trimiterii unui mesaj: destinatar, subiect, corpul mesajului.
Deoarece afișarea mesajelor se face pe baza informațiilor reținute în Session, un alt utilizator nu poate avea acces la informațiile unui alt utilizator decât dacă acesta se autentifică în cadrul aplicației cu respectivul cont.
Figure
Se observă în cadrul scrierii unui mesaj utilizarea instrumentului TinyMCE. Astfel utilizatorii pot trimite mesaje sub orice forma: text, imagini, videoclipuri, emoticoane, etc.
Pagina de căutare a profilelor
Pagină accesibilă numai utilizatorilor autentificați, această pagină permite acestora căutarea altor utilizatori după nume, prin intermediul câmpului din stânga. De asemenea, pagina este prevăzută în partea dreaptă cu lista ultimilor utilizatori conectați împreună cu data și ora la care au fost activi. Prin apăsarea pe imaginilor corespunzătoare fiecărui utilizator, se poate naviga către pagina de vizualizare a profilului.
Pagina de vizualizare a profilului și pagina de editare a profilului personal
Am ales să prezint aceste două pagini deoarece sunt foarte similare, singura diferența dintre ele fiind faptul că în pagina de editare un utilizator autentificat își poate schimba datele profilului său: țară, ocupație, nume, prenume, etc. De asemenea, în cadrul paginii de editare a profilului, un utilizator are posibilitatea de a-si modifica parola. Trebuie însă ca parola veche să fie cunoscută pentru a realiza o astfel de schimbare.
De reținut faptul că un utilizator neînregistrat nu poate avea acces la pagina de profil al unui utilizator. Motivul este faptul că informațiile, chiar dacă nu sunt foarte personale, nu ar trebui sa fie vizibile chiar de către orice vizitator.
Pagina de adăugare a unui joc
Figure – Pagina de adăugare a unui joc
Pagină accesibilă doar moderatorilor, această pagină prezintă formularul în urma a cărui completare se realizează inserarea unui joc în baza de date. Prin intermediul acestui formular un moderator adaugă informațiile despre specificațiile tehnice minime ale unui calculator pentru a suporta rularea jocului respectiv în condiții optime, de exemplu procesorul, placa video, spațiul pe disc, dar și informații generale asupra jocului, cum ar fi titlu, gen, poză. De asemenea, utilizând TinyMCE, un moderator are libertatea de a face descrierea unui joc într-un mod unic, folosind instrumentele pe care acest program le pune la dispoziție.
Pagina unui joc
Figure – Pagina unui joc
Această pagină, accesibilă tuturor, reprezintă pagina principală a unui joc. După cum se poate observa, sub poza jocului sunt afișate secțiunile corespunzătoare acestuia:
Pagina de descriere
Pagina de întrebări
Pagina de raportare a bug-urilor
Pagina de recenzii
Tot în cadrul acestei pagini sunt afișate și jocurile cele mai asemănătoare cu cel curent, jocuri care sunt obținute în urma executării algoritmului de recomandare prezentat într-o secțiune precedentă. Din figura 34 se remarcă buna funcționare a algoritmului, întrucât jocul cel mai asmemănător cu Call of Duty: Modern Warfare 3 este Call of Duty: Black Ops.
Pagina de descriere a unui joc
Figure – Pagina de descriere a unui joc
Altă pagină vizibilă tuturor, aceasta reprezintă rezultatul inserării unui joc de către un moderator. Remarcăm faptul că prin intermediul utilizării programului TinyMCE, s-au adăugat cu succes atât descrierea în cuvinte a jocului, cât și videoclipul care prezintă trailerul acestuia.
Pagina de vizualizare a topic-urilor
Figure – Paginile de vizualizare a topic-urilor și de vizualizare a răspunsurilor
Pagina de vizualizare a topic-urilor este identică indiferent dacă secțiunea din care face parte este de întrebări sau de raportare a bug-urilor. Același lucru este valabil și pentru pagina de vizualizare a discuțiilor din cadrul aceluiași topic. Aceste pagini pot fi accesate de orice tip de utilizator.
Pagina de vizualizare a topic-urilor conține un tabel care afișează toate topic-urile existente din cadrul jocului respectiv și pentru fiecare topic informațiile cu privire la autorul topic-ului, subiect, data la care a fost postat, precum și numărul de răspunsuri care le-a primit. Dând click pe subiectul topic-ului se navighează automat către pagina de vizualizare a discuției pe baza acestuia. Automat, primul mesaj afișat este cel al autorului pe care l-a intorodus în pagina de creare a unui topic.
Ulterior, utilizatorii pot posta mesaje prin intermediul cărora să răspundă la acest topic sau care să exprime alte probleme similare cu acesta astfel încât în cele din urmă autorul să primească un răspuns la întrebarea sau problema pe care a întâlnit-o.
Figure – Pagina de creare a unui topic
Pagina din fugura 37 este accesibilă doar utilizatorilor logați și permite acestuia crearea unui nou topic în scopul obținerii răspunsuri la problema pe care o are acesta. Am ales ca în ambele cazuri să utilizez editorul de test TinyMCE pentru a permite utilizatorilor să descrie mai usor, eventual cu poze sau videoclipuri, eventualele nelămuriri sau explicații.
Pagina de Recenzii
Figure – Pagina de recenzii
Pagină accesibilă oricărui tip de utilizator, aceasta reprezintă secțiunea din aplicație care afișează opiniile utilizatorilor despre jocul respectiv. Fiecare recenzie deține câte un buton de apreciere și depreciere, iar acestea sunt programate astfel încât un utilizator nu poate aprecia sau deprecia de mai multe ori aceeașî recenzie și, de asemenea, atunci când utilizatorul care apreciase deja o recenzie apasă butonul de depreciere a acesteia, aprecierea va fi înlocuită cu deprecierea.
Rolul pe care le au recenziile este acela de a calcula nota jocului, după cum s-a menționat în secțiunea 3. Atunci când un utilizator adaugă o recenzie, se calculează punctajul recenziei. Acest lucru se realizează în trei pași:
Pas 1: Crearea vectorului corespunzător recenziei se realizează cu ajutorul funcției:
Figure – Funcția de creare a vectorului aferent recenziei
Pentru a crea vectorul, prima dată trebuie să normalizăm cererea, adică să eliminăm punctuația și caracterele speciale care nu ne transmit nicio informație asupra opiniei utilizatorului, și de asemenea să eliminăm spațiile libere consecutive; lucru pe care îl fac cu ajutorul funcției string eliminaPunctuatie(string sir) și cu ajutorul reconstrucției recenziei utilizănd metoda string[] Split(string[] separatori) .
Fișierul “alfabet.txt” conține ngramele de lungime 6 care au fost create pentru antrenarea clasificatorului. Deoarece numărul aparițiilor acestor ngrame reprezintă trăsăturile cu care acest clasificator a fost antrenat, vectorul trebuie construit cu aceleași trăsături. Așadar, pentru fiecare ngramă din alfabet număr apariția acesteia în cadrul recenziei cu ajutorul funcției int numaraSubstringuri(string sir, string subsir), după care am creat vectorul similar cu vectorii din mulțimea de antrenare: pentru fiecare ngramă al cărei număr de apariții “nr” este mai mare ca 0, se adaugă componenta index:nr, unde index reprezintă poziția ngramei încadrul alfabetului.
După cum am menționat și în secțiunea SVM-ului, al doilea pas important este scalarea vectorului, adică împărțirea numărului de apariții ale ngramelor la norma vectorului compus din numărul aparițiilor ngramelor. Pentru a realiza acest lucru, am implementat funcția:
Figure – Funcția de scalare a vectorului
Astfel, printr-o primă iterație a vectorului se calculează suma pătratelor numerelor de apariții, după care se calculează radical din această sumă pentru a obține norma, iar apoi prin a doua iterație prin vector se împarte fiecare număr de apariție la norma vectorului. Se observă faptul că vectorului i-am pus valoarea 1 pe prima poziție. Acest număr nu influențează cu nimic vectorulu, deoarece clasificatorul îi va prezice eticheta doar pe baza numărului de apariții ale ngramelor, însă acesta trebuie pus pentru ca vectorul să fie definit conform vectorilor din mulțimea de antrenare.
Pasul final constă în determinarea punctajului recenziei pe baza distanței dintre punctul determinat de vector și hiperplanul care separă cele două clase determinat de ponderile clasificatorului.
Figure – Funcția de calculare a punctajului recenziei
Acest punctaj îl calculez pe baza probabilității de apartenență a recenziei la clasa 1 (clasa recenziilor pozitive), întrucât această probabilitate reprezintă într-o anumită măsura această distanță. Pentru obținerea acestor probabilități ma folosesc de clasificatorul deja antrenat, pe care l-am salvat într-un fișier xml tocmai pentru acest lucru, pentru a nu mai fi nevoie să îl antrenez de fiecare dată. La pornirea aplicației creez un clasificator pe care îl import din fișierul xml și îl salvez în Application[“svm”].
În cadrul funcției prezentate în figura 41 preiau acest clasificator gata antrenat din Application[“svm”]. Deoarece mulțimea de testare poate fi creată doar pe baza unui fișier, prima dată a trebuit să introduc vectorul scalat într-un fișier numit “multimeTestare.txt”. Folosesc apoi metoda Dictionary<int, double> PredictProbabilities(svm_node[] x) a clasei C_SVC care returnează, după cum am explicat în secțiunea 3, probabilitățile de apartenență la fiecare clasă, în cazul meu probabilitățile de apartenență la clasele 1 și -1. Această probabilitate este intre 0 și 1, așa că pentru a obține un punctaj înmulțesc această valoare cu 0 și rotunjesc la prima zecimală, obținând astfel o notă între 0 și 10.
În cele din urmă, după ce obțin punctajul recenziei, calculez nota jocului fâcând media dintre notele recenziilor jocului respectiv și actualizănd totodată nota jocului.
Pagina adminului
Figure – Pagina adminului
Această pagină oferă utilizatorilor de tip admin informații referitoare la conturile utilizatorilor ce s-au înregistrat în cadrul aplicației. Având aceste informații ei pot manipula conturile utilizatorilor astfel încât să ofere drepturi acestora prin schimbarea tipului contului sau, dimpotrivă pentru suprimarea acestor drepturi în cazul în care moderatorii nu își fac treaba. De asemenea, tot adminii pot dezactiva conturile persoanelor care nu au un comportament civilizat și care folosesc injurii la adresa altor persoane. În plus, ei pot activa în special în cazul în care anumite persoane nu si-au putut activa contul din motive variate.
Copyright Notice
© Licențiada.org respectă drepturile de proprietate intelectuală și așteaptă ca toți utilizatorii să facă același lucru. Dacă consideri că un conținut de pe site încalcă drepturile tale de autor, te rugăm să trimiți o notificare DMCA.
Acest articol: Kharmah – Aplicație Web pentru prezentarea și analiza opiniilor despre jocurile pe calculator [308882] (ID: 308882)
Dacă considerați că acest conținut vă încalcă drepturile de autor, vă rugăm să depuneți o cerere pe pagina noastră Copyright Takedown.
