Șef lucrări dr. Ing. Ionuț Cristian Resceanu Iulie, 2017 CRAIOVA [TITLUL PROIECTULUI DE DIPLOMĂ] Robert Gabriel Rădoi COORDONATOR ȘTIINȚIFIC Șef… [311609]
PROIECT DE DIPLOMĂ
Robert Gabriel Rădoi
COORDONATOR ȘTIINȚIFIC
Șef lucrări dr. Ing. Ionuț Cristian Resceanu
Iulie, 2017
CRAIOVA
[TITLUL PROIECTULUI DE DIPLOMĂ]
Robert Gabriel Rădoi
COORDONATOR ȘTIINȚIFIC
Șef lucrări dr. Ing. Ionuț Resceanu
Iulie, 2017
CRAIOVA
„Învățătura este o comoară care își urmează stăpânul pretutindeni.”
[anonimizat]: [anonimizat], Calculatoare și Electronică a [anonimizat], [anonimizat]:
cu titlul [TITLUL LUCRĂRII],
coordonată de ȘEF LUCRĂRI DR. ING. [anonimizat] 2017.
[anonimizat]:
reproducerea exactă a [anonimizat]-o [anonimizat]-o [anonimizat],
[anonimizat], [anonimizat] a unor aplicații realizate de alți autori fără menționarea corectă a [anonimizat] a [anonimizat].
Pentru evitarea acestor situații neplăcute se recomandă:
plasarea între ghilimele a citatelor directe și indicarea referinței într-o [anonimizat] a [anonimizat] a sursei originale de la care s-a [anonimizat] s-[anonimizat], figuri, imagini, statistici, [anonimizat], a căror paternitate este unanim cunoscută și acceptată.
Data, Semnătura candidat: [anonimizat],
PROIECTUL DE DIPLOMĂ
REFERATUL CONDUCĂTORULUI ȘTIINȚIFIC
În urma analizei lucrării candidat: [anonimizat]:
[anonimizat]:
Data, [anonimizat] a fost concepută cu scopul de a [anonimizat].
Aplicația oferă posibilitatea căutarii în câteva secunde printre serialele aflate în baza de date și adăugarea acestora într-o secțiune de „Seriale Personale”. [anonimizat] e-mail de fiecare dată când un episod nou o să apară.
Aplicația conține două tipuri de utilizatori:
utilizatorul normal
administratorul
Diferența dintre cele două tipuri de utilizatori este următoarea: administratorul are aceleași posibilități ca și utilizatorul normal (de a adăuga și de a șterge serialele din secțiunea „Seriale Personale” însă, [anonimizat] a [anonimizat] a [anonimizat] a acestora.
Primul capitol al lucrării este alcătuit din patru subcapitole, astefel: primul subcapitol prezintă o scurtă descriere a framework-ului AngularJs precum și tipul de arhitectura MVC .Cel de-al doilea, prezintă o scurtă descriere a NodeJS –ului precum și o paralelă între acesta si limbajul PHP. În cel de-al treilea subcapitol este prezentată o aplicație de tip SPA și avantajele acesteia față de o aplicație Web normală. Ultimul subcapitol conține o scurtă prezentare a ceea ce înseamnă Bootstrap.
Cel de-al doilea capitol al lucrării descrie arhitectura bazei de date precum și o scurtă prezentare a programului MongoDB Compass, program utilizat pentru realizarea acesteia dintr-o interfață grafică.
Capitolul trei conține prezentarea aplicației, componentele din care aceasta este alcătuită, și modul de funcționare a acestora. Tot aici sunt prezentate meniurile și submeniurile specifice, și modul de interacțiune al utilizatorului cu aplicația.
În capitolul patru este prezentată ahitectura de la baza aplicației și realizarea practică a acesteia. Pentru fiecare fereastră sunt exemplificate secvențe de cod corespunzătoare. În încheierea acestui capitol este prezentat si modul de comunicare între componente.
Termenii cheie: AngularJS, NodeJS, Bootstrap, MongoDB, MVC.
CUPRINS
LISTA FIGURILOR
Figura 1. Selectarea prin click dreapta a opțiunii „Update field” 35
Figura 2. Actualizarea întregului tabel 35
LISTA TABELELOR
Tabelul 1. Nume de utilizatori și valorile rezumat ale parolelor acestora 36
Capitolul I
Introducere în JavaScript, AngularJS și NodeJS –
Limbajul JavaScript
Caracterizare
JavaScript este un limbaj de programare OOP (orientat pe obiecte) , care se bazează pe conceptul prototipurilor. Acesta este foarte cunoscut, fiind folosit la construirea site-urilor web, însă este folosit și în scopul accesării obiectelor încastrate (embedded) în alte aplicații.
Pe lângă mediul web, JavaScript mai este folosit și pentru widget-uri desktop sau pentru
documente PDF.
JavaScript deține o colecție de obiecte standard, precum Array, Math, Date dar și elemente de limbaj, cum ar fi structuri de control, operatori, declarații.
JavaScript și Java
Cele două limbaje de programare sunt fundamental diferite, fiind deseori confundate. Java a apărut în anul 1995 și a introdus conceptul de VM(Virtual Machine), fiind folosit pentru a realiza aplicații legate de partea de server.
JavaScript a apărut inițial sub denumirea de Live Script în anul 1995. Datorită succesului major de care s-a bucurat Java, limbajul LiveScript a fost redenumit.
Diferențele dintre cele două limbaje:
JavaScript este un limbaj OOP de scripting, în timp ce Java este un limbaj OOP de pogramare.
JavaScript poate fi executat doar în cadrul browser-ului, pe când în Java pot fi create aplicații atât pentru browser cât si pentru Mașina Virtuală.
Codul limbajului Java trebuie să fie compilat, pe când cel din JavaScript este prezentat sub formă de text.
Sunt dependete de plugin-uri diferite.
Structura unui program JavaScript
Pentru a crea un program în limbajul JavaScript nu este nevoie decât de unde editor pentru text, cum ar fi NotePad++, SublimeText, Atom. Depistarea erorilor se face direct in browser, fară a mai fi nevoie de instalarea unui depanator pentru codul aplicației. Acest lucru constituie un avantaj față de alte limbaje de programare, cum ar fi .NET, care are nevoie de Visual C# .NET sau Visual Basic .NET.
O aplicație simplă, de tipul „Hello World” , care apare în toate cărtile de programare are următoarea structură:
var mesaj_intampinare = „Hello World”;
console.log(mesaj_intampinare);
Introducere in AngularJS
Prezentare generală
AngularJS este un framework dezvoltat la început de către Misko Hevery și Adam Abrons fiind menținut în momentul de față de către cei de la Google. Acesta a fost construit în principal pentru dezvoltarea ușoară a aplicațiilor Web.
Este folosit în special pentru SPA-uri(Aplicație pe o singură pagină), dar și pentru aplicațiile mobile deoarece se combină foarte ușor cu Bootstrap, rezultatul fiind o aplicație fluidă, rapidă dar și care răspunde ușor pornind de la o rezoluție mică, a unui dispozitiv mobil până la o rezoluție mare corespunzătoare unui browser web.
În comparație cu alte platforme open-source ale JavaScript-ului, AngularJS, deși ca dimensiune(40kb) este mai mare de exemplu decât BackboneJS(6.5kb), acesta din urmă necesită și descărcarea unor librării suplimentare(jQuery și Underscore), ajungând la un număr mai mare de kb, deci o încărcare mai lentă a paginii web.
Timpul de încărcare joacă un rol foarte important deoarece sunt folosite din ce in ce mai des dispozitivele mobile(tablete, telefoane) care folosesc un timp relativ superior de încărcare față de conexiunile clasice.
Arhitectura MVC
O arhitectură de tip MVC ( Model – View – Controller ) este alcătuită din trei părți:
Model – este responsabil pentru administrarea datelor aplicației.
View – afișează fizic datelor unui utilizator
Controller – controlează interacțiunea dintre Model și View
Această arhitectură este foarte populară deoarece izolează partea logică a aplicației de partea vizuală. Controller- ul primește cerințele aplicației, lucrând alături de model pentru a pregăti datele ce vor fi afișate.
Introducere în NodeJS
1.2.1. Prezentare generală
NodeJS reprezintă JavaScript-ul ce lucrează pe partea de server. A fost creat de Ryan Dahl alături de alți programatori în 2009. În 2011 a fost lansat Node Package Manager (NPM) care are scopul de a facilita instalarea altor module JavaScript dezvoltate de către comunitatea NodeJs.
NodeJS-ul a contribuit la creșterea și evoluția JavaScript-ului, utilizatorii realizând că JavaScript-ul pe server nu este așa de rău ca pe browser.
Un alt avantaj important al NodeJs-ului îl reprezintă faptul că are un singur fir de execuție și faptul că este o arhitectură asincronă (un bloc de comandă este executat numai după ce blocul anterior a fost executat complet).
Aplicațiile NodeJs pot rula atât pe Windows, cât și pe Linux, macOS, sau servere Unix. Ele pot fi scrise folosind CoffeeScript (o alternativă a JavaScript-ului), Dart, Microsoft TypeScript, sau oricare alt limbaj care poate fi compilat la JavaScript.
1.2.2.Comparație între NodeJS și PHP
În timp ce în PHP funcțiile sunt executate secvențial, în NodeJs este adoptată procedura asincronă, introducând promisiunile si callback-urile. Un alt avantaj al folosirii NodeJs-ului îl constituie faptul că este folosit același limbaj atât pe partea de server cât și pe partea de client.
În comparație cu PHP, care este un limbaj de programare, NodeJs este mai de grabă un intepretor pentru cod de JavaScript. Pentru a executa un cod de PHP pe server, trebuie instalat PHP Package, pe când NodeJs necesită Node.js installer.
Față de NodeJs, PHP conține puține variabile și funcții care pot fi mai ușor de folosit.Un alt aspect de luat in considerare îl reprezintă baza de date. PHP a fost creat să existe alături de MySQL și variantele acestuia cum ar fi MariaDB, pe când NodeJs poate interacționa cu SQL doar prin intermediul unei librării.
Aplicație structurată pe o singură pagină (Single Page Application)
Prezentare generală
O astfel de aplicație (Single Page Application) este o aplicație web, sau un site web care își propune să ofere utilizatorului experiența unei aplicații desktop. Într-o astfel de aplicație, toate resursele necesare: HTML, JavaScript și CSS sunt încărcate o singură dată, iar apoi celelalte resurse sunt adăugate dinamic, în funcție de necesitătile utilizatorului.
Interacțiunea unei SPA cu serverul se face prin fișiere de tip JSON(JavaScript Object Notation), în timp ce paginile nu sunt în mod tradițional pagini. Când utilizatorul navighează, conținutul încărcat este independent de conținutul aplicației, fiind numit View.
Deoarece SPA descarcă în avans structura paginii web, nu mai este nevoie de un request pentru conținut adițional.Acest lucru este similar ca și in cazul unei aplicații desktop.
Avantajele și dezavantajele unei SPA
Avantaje:
rapiditatea, toate resursele fiind încărcate o singură dată
ușurința depistării erorilor folosind Google Chrome, cu ajutorul căruia pot fi monitorizate operațiile de la nivel de server, poate fi investigată pagina și datele asociate acesteia
este simplă crearea unei aplicații mobile datorită codului folosit la nivel de server , acesta putând fi utilizat atât pentru aplicații web dar și pentru aplicații mobile.
Dezavantaje:
necesită prezența și disponibilitatea JavaScript-ului
comparativ cu o aplicație web tradițională, SPA este mai puțin securizată
în această abordare, butoanele de Înainte – Înapoi (back and forward) devin nefuncționale.
Modul de funcționare al unei aplicații SPA
Bootstrap – descriere generală
Bootstrap este un framework folosit pentru a proiecta siteuri și aplicații web, dezvoltat inițial de Twitter, ca și framework pentru munca internă din companie. Acesta este în momentul de față cel mai utilizat framework pentru interfețele web, el devenind un standard în crearea de template-uri pentru sisteme cum ar fi WordPress și Joomla.
Acesta împarte pagina în 12 coloane ale căror valori procentuale sunt egale, fiind extrem de fiabil, iar clasele responsive sunt controlate în funcție de lățimea de care dispune dispozitivul.
Compomentele de care dispune Bootstrap-ul: meniu de navigare care include dropdown, carousel, breadcrumbs și altele. Pe lângă acestea, mai conține și de o serie de stiluri pentru elemente de bază, cum ar fi: butoane, tabele, formulare.
Capitolul II
-Proiectarea bazei de date-
Baze de date NoSQL
Prezentare generală
NoSQL reprezintă o derivare a unui sistem de baze relaționale. Fiecare fișier dintr-o astfel de bază de date este trimis către unul sau mai mulți operatori prin intermediul unui mecanism de redirectare IO (intrare-ieșire).
Principalele caracteristici:
stochează obiecte(documente), reducând nevoia de join-uri ca în bazele de date relaționale
limbaj de interogare îmbunatățit, care însă păstrează principiile C++ și SQL
prezintă suport pentru indexare
suportă atât operații de actualizare atomice dar și operațiile tradiționale
posibilitatea stocării unor fișiere mari fară să complice stiva de date
Dezavantaje ale NoSQL-ului:
nu exista standarde, precum există un standard SQL pentru bazele de date relaționale
nu sunt metode performante pentru a proteja datele
posibilități limitate de interogare
numărul mic de dezvoltatori pentru NoSQL
MongoDB
MongoDB este o bază de date de tip NoSQL, fiind orientată pe documente. Principala diferență este legată de stocarea datelor: într-o astfel de bază de date, datele nu sunt stocate folosind tabele, ca în cazul unei baze de date relațională. În MongoDB, datele sunt stocate ca și documente JSON care dispun de scheme dinamice.
MongoDB Compass permite, prin intermediul unei interfețe, vizualizarea și explorarea printre colecțiile din baza de date.
Crearea colecțiilor bazei de date
O bază de date, este o modalitate de a stoca date și informații pe un suport extern, având posibilitatea extinderii și regăsirii rapide a acestora.
În cazul nostru, am folosit pachetul Mongoose, care permite crearea unor scheme dinamice în baza de date la nivel de NodeJs.
Cele două scheme, utilizate în aplicație sunt următoarele:
showSchema
userSchema
userSchema
userSchema = new mongoose.Schema({
name: {
type: String,
trim: true,
required: true
},
email: {
type: String,
unique: true,
lowercase: true,
trim: true
},
personalShows: [{
type: mongoose.Schema.Types.ObjectId,
ref: 'Show'
}],
temporaryShows: [{
type: mongoose.Schema.Types.ObjectId,
ref: 'Show'
}],
episodesWatched: [],
password: String,
level: Number,
personalPhoto: String
});
showSchema
showSchema = new mongoose.Schema({
showId: {
type: Number,
unique: true
},
name: String,
firstAired: Date,
overview: String,
rating: Number,
status: String,
poster: String,
banner: String,
creatorName: String,
creatorProfile: String,
network: String,
episodesNumber: Number,
episodeDuration: [Number],
genre: String,
owners: [{
type: mongoose.Schema.Types.ObjectId,
ref: 'User'
}],
subscribers: [{
type: mongoose.Schema.Types.ObjectId,
ref: User
}],
seasons: [{
seasonId: Number,
seasonNumber: Number,
episodeNumber: Number,
episodeName: String,
airDate: Date,
seasonPoster: String,
episodes: []
}],
});
Baza de date a aplicației
Structura internă a bazei de date
Baza de date a aplicației noastre este alcătuită din două colecții. În următoarea imagine este prezentată baza de date, după ce s-a realizat proiectarea acesteia, a colecțiilor și a relațiilor dintre ele.
Un utilizator poate aveam în secțiunile temporaryShows și personalShows un dicționar de ObjectId-uri, care fac referire la un anumit serial.
Un serial conține în secțiunea de owners un dicționar de ObjectId-uri care fac referire la utilizator, iar în secțiunea subscribers un dicționar de ObjectId-uri care fac referire tot la un serial.
Dacă am fi folosit în locul bazei de date MongoDB, de exemplu, MySQL, cele două scheme, cea de User și cea de Show ar fi arătat astfel:
Capitolul III
-Prezentarea aplicației-
3.1.Prezentarea generală
Această aplicație a fost concepută cu scopul de a ajuta utilizatorii să își gestioneze serialele preferate, precum și să fie la curent cu ultimele episoade apărute.
Aplicația oferă posibilitatea căutarii în câteva secunde printre serialele aflate în baza de date și adăugarea acestora într-o secțiune de „Seriale Personale”. După adăugarea în această secțiune, ultizatorul poate să marcheze episoadele deja vizionate precum și să se aboneze la un anumit serial primind un e-mail de fiecare dată când un episod nou o să apară.
Aplicația conține două tipuri de utilizatori:
utilizatorul normal
administratorul
Pentru a putea utiliza aplicația, este nevoie de crearea unui cont. Există un singur administrator de sistem, restul utilizatorilor făcând partea din categoria utilizatorilor normali.
Acest lucru presupune completarea unu formular de înregistrare în care sunt necesare: numele, adresa de e-mail, dar și o parolă. Nu pot fi înregistrați mai mulți utilizatori folosind aceeași adresă de e-mail, aceștia din urmă fiind informați cu privire la faptul că deja este utilizată adresa. Formularul mai dispune și de un sistem de înștiințare cu privire la complexitatea parolei aleasă.
Dacă unul dintre câmpurile enumerate mai sus nu este completat, utilizatorul nu poate crea acel cont.
După apăsarea butonului de „Înregistrare”, dacă toate cerințele au fost îndeplinite, în baza de date va fi creat un JSON care conține toate datele utilizatorului
Figura nr…Schemă utilizator
După crearea contului, utilizatorul este redirecționat către pagina de Autentificare, unde trebuie să-și introducă datele. Dacă adresa de email și parola au fost introduse corect, utilizatorul va avea acces la fereastra principală.
În partea stânga , se găsește meniul la care utilizatorul are acces. Cu ajutorul acestora, utilizatorul, poate accesa conținutul de seriale din baza de date precum și serialele personale, poate vizualiza diferite statistici, precum și un calendar cu episoadele serialelor care urmează să apară.
În cazul administratorului, acesta dispune de un submeniu suplimentar.
3.2.Fereastră de autentificare
Figura nr.. Fereastră de autentificare
Fereastra de autentificare este alcătuita din două Text Input-uri și un buton de „Autentificare”
Modul de funcționare
Utilizatorul se autentifică prin introducerea adresei de e-mail și a parolei. Adresa de e-mail este unică, pentru fiecare utilizator în parte. După introducerea acestora și apăsarea butonului, aplicația va verifica dacă în baza de date exista acea înregistrare.
Dacă adresa de e-mail și parola au fost introduse greșit, utilizatorul va fi avertizat, primind un mesaj de eroare.
Dacă adresa de e-mail și parola au fost introduse corect, utilizatorului îi este permis accesul la aplicație.
3.3. Fereastra principală
Următoarea fereastră este deschisă dacă au fost completate în mod corespunzător câmpurile din secțiunea de autentificare.
Figura nr.. Fereastră Principală
Fereastra principală este alcătuită din două view-uri: unul pentru meniul din stânga, și unul pentru conținutul ce va fi încărcat dinamic.
Din fereastra principală, utilizatorul poate căuta un serial anume introducându-i numele în caseta de căutare.
3.3.1. Secțiunea – Meniuri
Meniul este alcătui din subcategorii:
Meniul Principal
Acasă
Show-uri Personale
Show-uri În Desfașurare
Calendar
Statistici
Setări
Setările Utilizatorului
Zona de Administrare (vizibilă doar pentru administratorul de rețea)
Opțiunea de deconectare
Fig nr.. Meniu Principal
3.3.1.1 Meniul Acasă – Căutare
Meniul Acasă este meniul care va fi selectat în mod implicit atunci când utilizatorul se conectează la aplicație. Acesta va afișa in fereastra principală cele mai bune 18 seriale ordonate după notele acestora din baza de date.
Tot din această fereastră, utilizatorul poate căuta printre serialele aflate în baza de date, folosind caseta de căutare, fiindu-i returnate toate serialele care au în componență grupul de litere „mi”.
Figura nr. Meniu Acasă – Căutare
3.3.1.2. Meniul Show-uri personale
Accesând acest meniu, utilizatorului îi sunt afișate doar serialele pe care acesta le-a adăugat la Show-uri Personale. Vor fi afișate imagini sugestive pentru fiecare serial în parte,alături de numele acestuia și numărul de episoade corespunzător.
Pentru a putea elimina unul din serialele adăugate în această categorie, utilizatorul este nevoit să acceseze meniul anterior(Acasă).
Figura nr… Meniu Show-uri Personale
3.3.1.3. Meniul Show-uri în desfășurare
Accesând acest meniu, utilizatorul poate accesa doar serialele care se află în desfășurare, care urmeză să apară în continuare. Alături de numele acestora, utilizatorul mai poate viziona numărul sezonului și numărul episodului care urmează să apară precum și numărul de zile rămase până la apariția acestuia.
Selectând numele unui serial, utilizatorul va fi redirecționat către o pagină de detalii cu privire la sezoanele și episoadele acestuia.
Figura nr… Meniu Show-uri În Desfășurare
3.3.1.4. Meniul Calendar
În acest meniu, utilizatorul poate accesa un calendar, în interiorul căruia sa vor afla episoadele din serialele adăugate în categoria Seriale Personale.
Calendarul va conține numai episoadele serialelor care se află în desfășurare, marcate conform zilei de apariție al următorului episod.
Calendarul dispune de următoarele butoane:
un buton pentru accesarea zilei precendete
un buton pentru accesarea zilei următoare
un buton care-l duce pe utilizator la ziua curentă, în cazul în care acesta caută până la o anumită dată și dorește să se întoarcă rapid la ziua de azi.
trei butoane pentru afișarea calendarului în funcție de lună, săptămână, sau zi.
3.3.1.5.Meniul Statistici
Acest meniu, conține diferite statistici cu privire la activitatea utilizatorului. El este format din trei diagrame:
diagrama pentru evidențierea numărului total de minute petrecute de utilizator vizualizând seriale
diagrama în care sunt evidențiate toate genurile serialelor favorite
diagrama în care sunt afișate episoadele vizioanate
3.3.1.6. Zona de Administrare
În acest meniu, un singur utilizator are acces, fiind vorba de administratorul de rețea.
Meniul nu va fi vizibil pentru un utilizator normal.Acesta are acces la întreaga aplicație, mai puțin la acesta.
Zona de Administrare este compusă din trei secțiuni:
Secțiunea Utilizatori
Secțiunea Setări Utilizatori
Secțiunea Administrare Seriale Utilizatori
Secțiunea Utilizatori
În această secțiune, administratorul poate vizualiza numărul de utilizatorii înregistrați, precum și date despre aceștia, cum ar fi:
adresa de e-mail
numărul de seriale adăugate în categoria serialelor personale
Tot din această secțiune, administratorul, apăsând butonul de ștergere asociat fiecărui utilizator are posibilitatea să-l șteargă din baza de date.
Secțiunea Setări Utilizatori
Din această secțiune, pot fi modificate datele personale ale unui utilizator. Selectând unul din utilizatorii aflați în Secțiunea Utilizatori, administratorul poate să-i editeze numele, e-mail-ul sau parola acestuia.
Figura nr… Secțiunea Setări Utilizator
Secțiunea Administrare Seriale Utilizatori
În această secțiune, administratorul are acces la toate serialele adăugate de un anumit user, având posibilitatea, dacă acesta a încălcat vreo regulă, să elimine o parte din acestea.
3.3.1.7.Meniul Setări Personale
Accesând acest meniu, un utilizator, dacă dorește, are posibilitatea de a-și modifica parola.Noua parolă, la fel ca și cea inițială, va fi salvată în baza de date, trecând printr-un proces de criptare, astfel, ea devenind mai puternică și mai greu de descifrat.
Pentru criptare, am folosit un pachet, “bcrypt” , pentru NodeJs, care genereză un salt de 10 poziții, parola fiind apoi hășuită, în funcție de acel salt.
Figura nr…Meniu Setări Personale
Câmpurile de nume, e-mail și parolă actuală nu sunt disponibile pentru editare, ele fiind modale. După apăsarea butonului de Salvați modificările, utilizatorul va primi un mesaj de confirmare, dacă parola a fost modificată cu succes, sau un mesaj de eroare.
Figura nr… Alertă Schimbare Parolă
3.4. Fereastră Detalii Show
În momentul în care utilizatorul va da click pe unul dintre serialele aflate în meniul “Acasă”, acestuia, îi va fi încărcată o fereastră, care conține următoarele elemente:
scurtă descriere a ceea ce se întâmpla în acel serial
numărul de sezoane apărute
starea serialului, în momentul de față
Figura nr…. Fereastră Detalii Adăugare Show
Apăsând pe butonul „Adaugă la show-uri personale”, serialul respectiv va fi adăugat in colecția de seriale ale utilizatorului.
Singura modalitate de a elimina un serial din acea colecție, este de a accesa din nou pagina de detalii a serialului respectiv și apăsând butonul “Elimină de la show-uri personale”.
Figura nr…Fereastră Detalii Eliminare Show
3.5.Fereastră Show Personal
Această fereastră este conține următoarele componente:
buton de Abonare/Dezabonare
detalii despre show
toate sezoanele alături de imagini sugestive pentru fiecare
detalii despre fiecare sezon
Buton de Abonare/Dezabonare
Prin intermediul acestui buton, un utilizator, o dată ce a adăugat un serial în secțiunea de Show-uri personale, are posibilitatea de abonare sau dezabonare.
Dacă acesta dorește să fie înștiințat cu privire la următoarele episoade care vor apărea, abonându-se, va primi un e-mail , cu 2 ore înainte ca un episod nou să apară. Pentru a realiza acest lucru, în baza de date, pe lângă JSON-urile de Useri și Show-uri, am adăugat un JSON în care vor fi stocate datele următorului episod care urmează sa apară.
FIGURA EMAIL
Detalii despre show
În această secțiune se vor afla informații mai detaliate despre acel serial. Utilizatorul are acces la o descriere mai detaliată cu privire la ce se întâmplă pe parcursul episoadelor, precum și o imagine sugestivă.
Fig nr… Detalii despre show
Sezoane și detalii despre acestea
Accesul printre sezoane poate fi făcut prin apăsarea pe una dintre imaginile corespunzătoare acestuia.Astfel, când un sezon este selectat, un tabel cu episoadele acestuia este încărcat.
Tabelul este alcătuit din cinci coloane:
număr episod
nume episod
data la care acesta a fost lansat
un buton de „Vizionare”
un buton de detalii
figura….
Prin apăsarea butonului de „Vizionare”, aflat pe linia unui episod, acesta, va fi marcat ca și episod văzut.
Prin apăsarea butonului de “Detalii”, aflat pe linia unui episod, utilizatorul va obține mai multe detalii despre ce se va întâmpla în episodul respectiv, precum și o imagine din acel episod.
CAPITOLUL IV
-Arhitectura sistemului.Realizare practică-
4.1. Structura aplicației
Această aplicație este construită având o arhitectură de tipul MVC.
Acronimul MVC vine de la Model – View- Controller
Este alcatuita din 2 vieuri unul de sidebar si unul de content + schema referitor la treaba asta
Modificare in aplicatie ca ruta de baza sa fie / sa te duca pe home nu pe my shows
Procesul tipic pentru a implementa o astfel de arhitectură presupune divizarea aplicației pentru o orgaziare cât mai eficientă și cât mai productivă.
Aplicția pentru gestioarea conținutui audio-video este structurată astfel:
un folder “public”
un fișier JavaScript pentru acțiunile de la nivelul serverului
Analizând acest folder, observăm că el conține la rândul său în alte foldere, după cum urmează:
controllers
directives
filters
services
stylesheets
utils
vendor
views
4.2. Analiza rutelor aplicației
Aplicația conține mai multe end-pointuri, reprezentate prin rutele definite în fisierul „app.js”. Pentru a specifica fiecare rută, am folosit $routeProvider, un serviciu care aparține AngularJS-ului.
$routeProvider
.when('/admin', {
templateUrl: 'views/admin.html',
controller: 'AdminCtrl',
})
.when('/', {
templateUrl: 'views/home.html',
controller: 'MainCtrl',
})
.when('/shows/:id', {
templateUrl: 'views/detail.html',
controller: 'DetailCtrl',
})
.when('/statistics', {
templateUrl: 'views/diagram.html',
controller: 'DiagramCtrl',
})
.when('/login', {
templateUrl: 'views/login.html',
controller: 'LoginCtrl',
})
.when('/signup', {
templateUrl: 'views/signup.html',
controller: 'SignupCtrl'
})
.when('/add', {
templateUrl: 'views/add.html',
controller: 'AddCtrl',
})
.when('/settings', {
templateUrl: 'views/settings.html',
controller: 'SettingsCtrl',
})
.when('/myshows', {
templateUrl: 'views/myshows.html',
controller: 'MyShowsCtrl',
})
.when('/upcoming', {
templateUrl: 'views/upcoming.html',
controller: 'UpcomingCtrl',
})
.when('/result', {
templateUrl: 'views/result.html',
controller: 'ResultCtrl',
})
.when('/result/:id', {
templateUrl: 'views/showresult.html',
controller: 'ShowResultCtrl',
})
.when('/shows/:id/:seasonId', {
templateUrl: 'views/seasondetail.html',
controller: 'SeasonDetailCtrl',
})
.when('/calendar', {
templateUrl: 'views/calendar.html',
controller: 'CalendarCtrl',
})
.otherwise({
redirectTo: '/',
});
Când o anumită rută este accesată,utilizatorul va fi direcționat către un view care are în spate un controller responsabil pentru partea logică. Dacă se încearcă accesarea altor rute decât cele care se află în lista de mai sus, utilizatorul va fi redirecționat către ruta default ( / ).
4.2.1. Ruta “login”
Atunci când utilizatorul accesează adresa aplicației, el va fi redirecționat către localhost:4000/login, obligându-l pe acesta să se autentifice pentru a putea folosit aplicația.
Formularul de autentificare, la nivel de HTML, are următorul cod:
<form method="post" ng-submit="login()" name="loginForm" class="form-horizontal">
<div class="form-group">
<div class="col-md-12">
<input class="form-control" type="text" name="email" ng-model="email" placeholder="Email" required autofocus>
</div>
</div>
<div class="form-group">
<div class="col-md-12">
<input class="form-control" type="password" name="password" ng-model="password" placeholder="Parolă" required>
</div>
</div>
<div class="form-group">
<div class="col-md-6">
<button type="submit" ng-disabled="loginForm.$invalid" class="btn btn-info btn-block">Autentificare</button>
</div>
</div>
</form>
După cum se observă, atunci când este completat formularul de autentificare, funcția login() este apelată. Ea este definiă în interiorul controller-ului acestui view, iar pentru a o putea folosi în acest fel a fost nevoie de atașarea acesteia la $scope.
$scope.login = function(user) {
Auth.login({
email: $scope.email,
password: $scope.password
});
var user = {
email: $scope.email,
password: $scope.password
}
};
Am folosit un serviciu de autentificare, modelul din arhitectura MVC, pentru a putea transmite datele între view-uri. Când acesta este apelat, fișierul auth.js preia datele introduce în formular, și le folosește pentru a face un api call către server.
login: function(user) {
return $http.post('/auth/login', user)
.success(function(data) {
localStorageService.set('token', data.token);
var payload = JSON.parse($window.atob(data.token.split('.')[1]));
localStorageService.set('currentUser', JSON.stringify(payload.user));
$location.path('/');
$alert({
title: 'Felicitări!',
content: 'Ai fost autentificat cu succes.',
animation: 'fadeZoomFadeDown',
type: 'material',
duration: 3
});
})
.error(function() {
delete $window.localStorage.token;
$alert({
title: 'Error!',
content: 'Adresă de email/parolă invalidă.',
animation: 'fadeZoomFadeDown',
type: 'material',
duration: 3
});
});
},
La nivelul server-ului, se realizează căutare în baza de date în funcție de adresa de email, care este unică.
app.post('/auth/login', function(req, res, next) {
User.findOne({
email: req.body.email
}, function(err, user) {
if (!user) return res.send(401, 'User-ul nu există în baza de date');
user.comparePassword(req.body.password, function(err, isMatch) {
if (!isMatch)
return res.send(401, 'Adresă de e-mail/parolă invalidă');
var token = createJwtToken(user);
res.send({
token: token
});
});
});
});
4.2.2. Ruta principală
După autentificare, utilizatorul este redirecționat către ruta principală a aplicației.
Aici, la nivel de controller sunt încărcate într-o variabilă “shows”, toate serialele aflate în baza de date.
$scope.shows = Show.query();
Afișarea acestora se face iterând prin toate rezultatele, și construind un < div > pentru fiecare.
<div class="col-xs-6 col-md-2" ng-repeat="show in shows | orderBy:'rating':true | limitTo:18 | filter: showName">
</div>
4.2.3. Ruta “myshows”
Accesând această rută, în view-ul principal vor fi încărcate toate serialele personale ale unui utilizator.
var user = User.get({
userId: $scope.currentUser._id
}, function(user, err) {
if (user.personalShows.length > 0) {
$scope.shows = Show.query({
_id: user.personalShows
});
} else {
$scope.shows = [];
}
});
Acestea vor vi afișate la fel ca și în cazul rutei principale, într-un div separat.
4.2.4. Ruta “upcoming”
Când utilizatorul selectează opțiunea “upcoming”, din bara de navigare, va fi redirecționat către această rută.
La nivelul controller-ului am realizat o prelucrare suplimentară a datelor, pentru afișare numărului de zile rămase până la apariția umrătorului episod
încărcarea tuturor serialelor personale ale utilizatorului
iterarea prin toate sezoanele fiecărui serial și păstrarea ultimul sezon într-o variabilă
iterarea prin ultimul sezon, comparând data de apariție a fiecărui episod cu data actuală
User.get({
userId: userID
}, function(user, err) {
if (user.personalShows.length > 0) {
$scope.shows = Show.query({
_id: user.personalShows
}, function(shows, err) {
for (var i = 0; i < shows.length; i++) {
seasons = shows[i].seasons;
lastSeasonEpisodes = seasons[seasons.length – 1].episodes;
for (var j = 0; j < lastSeasonEpisodes.length; j++) {
if (lastSeasonEpisodes[j].air_date > this.currentDate) {
shows[i].nextEpisode.name = lastSeasonEpisodes[j].name;
shows[i].nextEpisode.air_date = lastSeasonEpisodes[j].air_date;
shows[i].nextEpisode.number = lastSeasonEpisodes[j].episode_number;
shows[i].nextEpisode.overview = lastSeasonEpisodes[j].overview;
shows[i].nextEpisode.nrsez = lastSeasonEpisodes[j].season_number;
shows[i].nextEpisode.poster = lastSeasonEpisodes[j].still_path;
shows[i].nextEpisode.rate = lastSeasonEpisodes[j].vote_average;
}
}
}
}.bind($scope));
4.2.5. Ruta “calendar”
Accesând această rută, utilizatorul va avea acces la un calendar în care se vor regăsi episoadele din serialele persoanale care urmează să apară.
De această dată, în controller vom genera câte un eveniment pentru fiecare episod.
for (var i = 0; i < lastSeasonEpisodes.length; i++) {
if (lastSeasonEpisodes[i].air_date > currentDate) {
var newEvent = new Object();
newEvent.title = showInfo.name + " S" + lastSeasonEpisodes[i].season_number + "E" + lastSeasonEpisodes[i].episode_number;
newEvent.start = new Date(lastSeasonEpisodes[i].air_date);
$('#calendar').fullCalendar('renderEvent', newEvent, true);
}
}
4.2.6. Ruta “statistics”
4.2.7. Ruta “settings”
Pentru a-și schimba parola personală, un utilizator trebuie sa acceseze ruta settings. Completând caseta de “parolă nouă” și apăsând pe buton, funcția saveSettings() va fi apelată.
$scope.saveSettings = function(argument) {
Auth.changeSettings({
name: $scope.currentUserName,
email: $scope.currentUserEmail,
password: $scope.currentPassword,
newPassword: $scope.newPassword
});
}
Și de această data am folosit același serviciu de autentificare.
changeSettings: function(user) {
return $http.post('/auth/changesettings', user)
.success(function() {
$alert({
title: 'Felicitări!',
content: 'Parola a fost actualizată.',
animation: 'fadeZoomFadeUp',
type: 'material',
duration: 3
});
})
.error(function(response) {
$alert({
title: 'Eroare!',
content: response.data,
animation: 'fadeZoomFadeUp',
type: 'material',
duration: 3
});
});
}
La nivel de server, se face o cautare în baza de date a utilizatorului, în funcție de adresa acestuia de email.Dacă aceasta este validă, se va realiza o operație de update, înlocuindu-se vechea parolă cu cea nouă.
app.post('/auth/changesettings', function(req, res, next) {
var password = req.body.newPassword;
User.findOne({
email: req.body.email
}, function(err, user) {
user.password = password;
user.save(function(err) {
if (err) return next(err);
res.send(200);
});
});
});
4.2.8. Ruta “admin”
Ruta este accesibilă numai pentru administrator. Prin accesarea acesteia, el are posibilitatea să administreze utilizatorii înregistrați precum și serialele pe care aceștia le au în secțiunea de „Show-uri personale”.
În controller-ul view-ului de admin, am definit o funcție, necesară pentru a extrage toți utilizatorii din baza de date.
(function getUsers() {
Subscription.getUsers().success(function (userList) {
$scope.userList = userList;
});
})();
Pentru a accesa datele, am folosit serviciul Subscription , necesar pentru a realiza un api call către server.
getUsers: function() {
return $http.get('/api/userlist', {});
} ;
La nivelul server-ului se face o interogare în baza de date, returnându-se o listă cu toți utilizatorii înregistrați.
app.get('/api/userlist', function(req, res, next) {
User.find({}, function(err, users) {
var userMap = {};
users.forEach(function(user) {
userMap[user._id] = user;
});
res.send(userMap);
});
});
Acțiunile realizate în cadrul view-ului, acelea de „Ștergere utilizator” sau “Ștergere serial”, presupun apelarea altor api call-uri, acționate tot prin intermediul serviciul de Subscription.
$scope.deleteUser = function(user) {
Subscription.removeUser(user).success(function() {
$scope.isDeleted = true;
})
}
$scope.deleteUserShow = function(show) {
Subscription.deleteUserShow(show, $rootScope.currentUser).success(function(
})
}
4.2.9. Ruta “add”
Această rută poate fi accesată dacă se dorește adăugare la “Show-uri personale”, a unui serial care nu se află în baza de date locală a aplicației.
La nivel de HTML, este vorba de o casetă de input, în care este introdus numele serialului, și un buton pentru adăugarea acestuia în listă.
La nivel de server, sunt realizate următoarele operații asyncrone:
se realizează un request pe site-ul care conține toată baza de date online, în urma căruia se obține id-ul serialului respectiv
se realizează un alt request, folosind id-ul anterior și obținându-se toate informațiile despre serialul respectiv
fiecare poster este encodat la base64, pentru a putea fi salvat local
serialul va fi adăugat în listă, iar dacă acesta deja există, un mesaj de atenționare va fi afișat
async.waterfall([
function(callback) {
request.get('https://api.themoviedb.org/3/search/tv?api_key=' + apyKey + '&query=' + seriesName, function(error, response, body) … );
},
function(seriesId, callback) {
request.get('https://api.themoviedb.org/3/tv/' + seriesId + '?api_key=' + apyKey, function(error, response, body) … );
},
function(show, callback) {
var url = "http://image.tmdb.org/t/p/w185//" + show.poster;
request({url: url, encoding: null
}, function(error, response, body) {
show.poster = 'data:' + response.headers['content-type'] + ';base64,' + body.toString('base64');
callback(error, show);
}); }
], function(err, show) {
if (err) return next(err);
show.save(function(err) {
if (err) {
if (err.code == 11000) {
return res.send(409, {
message: show.name + ' există deja.'
});
}
return next(err);
}
});
User.findOne({
'email': userEmail
}, function(err, user) {
user.personalShows.push(show);
user.save(function(err) {
user.populate('personalShows', function(err) {
if (err) return next(err);
res.json(200);
})
});
4.2.10. Ruta „shows/id”
După adăugare unui serial în secțiunea „Show-uri personale”, utilizatorul poate accesa detalii despre acel serial în momentul în care va da click pe el.
View-ul de detalii este compus din mai multe elemente, cum ar fi butoane: subscribe,vizionat, detalii, precum și din liste și tabele. Butonul de unsubscribe este accesibil utilizatorului numai dacă acesta este abonat la serialul respective.
La nivelul controller-ului, funcțiile apelate prin apăsarea butoanelor au următorul cod:
$scope.subscribe = function() {
Subscription.subscribe(show, $rootScope.currentUser).success(function() {
$scope.show.subscribers.push($rootScope.currentUser._id);
});
};
$scope.unsubscribe = function() {
Subscription.unsubscribe(show, $rootScope.currentUser).success(function() {
var index = $scope.show.subscribers.indexOf($rootScope.currentUser._id);
$scope.show.subscribers.splice(index, 1);
}); };
$scope.watchEpisode = function(episode) {
Subscription.addToWatchedEpisode(episode, $rootScope.currentUser)
.success(function() {
$scope.currentUser.episodesWatched.push(episode.id);
})
}
$scope.unWatchEpisode = function(episode) {
Subscription.removeWatchedEpisodes(episode, $rootScope.currentUser)
.success(function() {
var index = $scope.currentUser.episodesWatched.indexOf(episode.id);
$scope.currentUser.episodesWatched.splice(index, 1);
});}
Toate aceste funcții sunt apelate prin intermediul serviciului de Subscription, la nivelul cărui se realizează api call-urile către server.
app.post('/api/subscribe', function(req, res, next) {
Show.findById(req.body.mongooseShowId, function(err, show) {
if (err) return next(err);
show.subscribers.push(req.body.currentUser._id);
show.save(function(err) {
if (err) return next(err);
res.send(200);
});
});
});
De exemplu, pentru abonarea la un serial, se face o căutare în baza de date a acestuia în funcție de id-ul unic din schemă. Dacă acesta există, în secțiunea de subscribers a show-ului respectiv va fi introdus id-ul unic al utilizatorului.
generale
Cerințele generale sunt preluate din [Olt07] (lucrare pe care o recomandăm călduros spre citire candidaților noștri, înainte de demararea redactării tezei): „responsabilitatea tezei este în întregime a candidatului, atât în ceea ce privește conținutul, cât și forma. Lucrarea trebuie să aibă o organizare clară și riguroasă, care să dovedească gândirea inginerească a candidatului. Ideile exprimate în lucrare trebuie să se înlănțuie conform unei logici clare. În acest sens, elementele de coerență și de coeziune a textului trebuie folosite în mod corect. Ideile se organizează în paragrafe, redactate cu indentație și fără spațiu între ele. Nu fraza creează paragraful, ci ideea!
Stilul este extrem de important („Stilul este veșmântul gândului,” spune Samuel Johnson). Lucrarea trebuie redactată într-un limbaj științific adecvat domeniului de cercetare abordat. Se vor evita particularitățile limbajului colocvial. Nu sunt admise greșeli gramaticale de redactare (acord, punctuație, lexic etc.). Lucrarea normativă ce va fi avută în vedere în această privință este Dicționarul ortografic, ortoepic și morfologic al limbii române [DOOM05].
Candidatul are obligația de a verifica dacă datele, termenii folosiți, numele proprii, citatele, titlurile (în limba română și în alte limbi) sunt corecte.
Candidatul trebuie să fie consecvent în exprimarea ideilor, în folosirea termenilor, a numelor proprii, a datelor, precum și a punctuației și a elementelor de structură a lucrării. Consecvența este necesară și în privința tipurilor de evidențieri grafice folosite (litere cursive, litere îngroșate sau sublinieri).
Termenii tehnici de origine străină neadaptați, consacrați de lucrările de specialitate, nu se traduc, dar, dacă folosiți o sursă bibliografică străină, puteți încerca traducerea unor termeni noi, cu condiția ca cei din limba de origine să fie prezenți alături. În ambele cazuri, se recomandă scrierea acestor termeni cu litere speciale (de regulă italice).
La cele menționate mai sus, adăugăm următoarea observație, care nu este deloc lipsită de importanță: notarea semnelor diacritice românești este obligatorie. Un text românesc în care ă se confundă cu a nu face o impresie bună, dă o notă de neglijență.”
Structura documentului
Lucrarea trebuie să conțină următoarele capitole:
Coperta tezei;
Pagina de titlu;
Declarația de originalitate (completată și semnată de către autor);
Formularul de înregistrare a enunțului temei lucrării (completat și semnat în solidar de către autor și coordonatorul științific);
Referatul coordonatorului științific (completat și semnat de coordonator);
Declarația de mulțumire a autorului (opțională);
Cuprinsul lucrării;
Lista figurilor;
Lista tabelelor;
Introducere;
Conținutul propriu-zis al lucrării (capitolele constituente);
Concluzii;
Bibliografia;
Referințele web;
Anexele (index, codul sursă, site-ul web al aplicației, etc.).
Dimensiunile lucrării
Nu pot fi acceptate lucrări cu un număr de pagini mai mic decât 50. Prin urmare se dau, cu titlu de recomandare, următoarele dimensiuni:
50 – 80 de pagini pentru o lucrare de licență;
60 – 100 de pagini pentru o lucrare de disertație.
În altă ordine de idei, recomandăm următoarea distribuție a paginilor pe diversele secțiuni/capitole [Olt07]:
Introducerea reprezintă cca. 10 – 15% din lucrare;
Conținutul reprezintă cca. 75 – 80% din lucrare;
Concluziile reprezintă cca. 10%.
Elemente de tehnoredactare
Pe scurt:
dimensiunea paginilor va fi A4, 21 x 29,7 cm;
marginile recomandate sunt: sus/jos/stânga/dreapta – 2,54 cm;
fontul recomandat este Times New Roman;
corpul literelor va avea dimensiunea de 11 puncte;
spațiul dintre rânduri va avea dimensiunea de 1 rând și jumătate (1,5);
indentarea unui paragraf se va face cu 1,27 cm;
textul paragrafelor trebuie să fie aliniat în mod echilibrat stânga-dreapta (în eng. – justify);
paginile trebuie să fie numerotate conform acestui șablon.
Formulele matematice
Pentru redactarea formulelor matematice recomandăm utilizarea instrumentului Microsoft Equation Editor și importul lor (o data terminate) în Microsoft Word.
Ilustrațiile
Figurile
Partea grafică a lucrării are o pondere semnificativă în nota finală acordată lucrării candidatului. Se recomandă prin urmare acordarea unei atenții sporite tehnoredactării figurilor.
Actualizarea Listei Figurilor este obligatorie (procedura este similară cu cea exemplificată în secțiunea 1.6.2).
Tabelele
Exemplificăm aici utilizarea tabelor. Pentru fiecare tabelă adăugată lucrării, autorul trebuie să prevadă și adăugarea unei legende (în eng., Caption). La final, este recomandată actualizarea listei figurilor. Pașii necesari pentru actualizarea listei tabelelor sunt:
Click dreapta pe Lista Tabelelor. Va apărea un meniu similar cu cel din Figura 1.
Figura 1. Selectarea prin click dreapta a opțiunii „Update field”
Selectarea opțiunii „Update entire table” conform cu Figura 2.
Figura 2. Actualizarea întregului tabel
Verificarea fontului folosit pentru conținutul propriu-zis al Listei Tabelelor și alegerea fontului Times New Roman în caz de incongruență.
Tabelul 1. Nume de utilizatori și valorile rezumat ale parolelor acestora
În interiorul lucrării, tabelul poate fi citat folosind eticheta și numărul de ordine ale sale precum în exemplul acesta (vezi Tabelul 1).
Legenda (unei figuri/tabele)
În cele doua secțiuni de mai sus s-au demonstrat două modele de legende atașate fiecărei tabele sau figuri. Microsoft Word permite modificarea, respectiv adăugarea de etichete noi corespunzătoare unui anumit tip de legendă.
Termeni de utilizare
Autorii
Acest șablon de document a fost creat de M.Marian pentru colectivul Facultății de Automatică, Calculatoare și Electronică a Universității din Craiova și pentru uniformizarea structurii proiectelor de diplomă ale studenților săi.
Licența de utilizare
Nu există restricții de utilizare. Documentul nu este constrâns de nicio licență. Sugestiile de îmbunătățire pot fi adresate autorului pe adresa de e-mail: marius.marian@cs.ucv.ro.
Concluzii
Autorul prezintă concluziile sale…
Bibliografie
Bibliografia va fi ordonată alfabetic dupa eticheta fiecărei element (de ex. DOOM05 în lista de mai jos este o etichetă). Etichetele materialelor consultate vor fi formatate folosind:
primele litere ale primului autor urmate de cele două cifre semnificative ale anului apariției materialului, sau
dintr-un acronim popular al lucrării respective, urmat din nou de cele două cifre semnificative ale anului apariției.
[DOOM05] – Dicționarul ortografic, ortoepic și morfologic al limbii române, Editura Univers Enciclopedic, București, 2005
Referințe web
Recomandăm și aici respectarea regulilor enunțate pentru secțiunea 4.
[Alm08] – Pedro de Almeida, Patrik Fuhrer, Documentation Guidelines for Diploma and Master Thesis, Universitatea din Fribourg, Elveția, 2008, disponibil on-line la adresa http://diuf.unifr.ch/drupal/softeng/teaching/guidelines
[Olt07] – Th. Olteanu, C. Albu, Ghid pentru redactarea lucrării de diplomă sau a disertației de masterat, Universitatea Română de Arte și Științe „Gheorghe Cristea”, 2007, disponibil via web la adresa http://www.ugc.ro/tpl/GHID REDACTARE DIPLOMA LICENTA.pdf
Codul sursă
În această anexă se adaugă codul sursă al aplicației…
Site-ul web al proiectului
Autorul prezintă în această anexă (opțională) site-ul web asociat proiectului său.
CD / DVD
Autorul atașează în această anexă obligatorie, versiunea electronică a aplicației, a acestei lucrări, precum și prezentarea finală a tezei.
Index
B
Bibliografie 9
C
CUPRINSUL xi
D
Dimensiuni 3
F
Figuri 4
Formulele matematice 4
I
Ilustrațiile 4
L
Legenda 6
LISTA FIGURILOR xii
LISTA TABELELOR xiii
R
Referințe web 10
S
Structura documentului 2
T
Tabele 5
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: Șef lucrări dr. Ing. Ionuț Cristian Resceanu Iulie, 2017 CRAIOVA [TITLUL PROIECTULUI DE DIPLOMĂ] Robert Gabriel Rădoi COORDONATOR ȘTIINȚIFIC Șef… [311609] (ID: 311609)
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.
