• Non ci sono risultati.

PostgreSQL

Nel documento Riconoscimento delle specie ittiche (pagine 30-51)

PostgreSQL è un potente sistema di database relazionale ad oggetti open source che utiliz- za ed estende il linguaggio SQL combinato con molte funzionalità che memorizzano e ridi-

mensionano in modo sicuro i carichi di lavoro di dati più complicati. Le origini di PostgreSQL risalgono al 1986 come parte del progetto POSTGRES presso l’Università della California a Berkeley e ha oltre 30 anni di sviluppo attivo sulla piattaforma principale. Ha guadagnato una solida reputazione per la sua architettura, affidabilità, integrità dei dati, estensibilità e per la sua vasta comunità open-source utile a fornire costantemente soluzioni performanti e innovative. Nel 2001 è diventato conforme alle operazioni ACID.

5.4.1

Servizi di PostgreSQL

La vastità di operazioni disponibili in postgres sono utilizzabili sia da linea di comando (psql), sia graficamente (pgAdmin4).

psql

è un front-end basato su terminale per PostgreSQL. Consente di digitare le query in modo interattivo, inviarle a PostgreSQL e visualizzarne il risultato. Inoltre, fornisce una serie di meta-comandi e funzioni shell-like per facilitare la scrittura di script.

pgAdmin4

E’ un runtime desktop creato utilizzando Python e javascript/jquery. L’applicazione viene distribuita su un server Web accessibile dal proprio browser. Permette di ese- guire quasi tutte le operazioni fattibili anche in psql, con la differenza di avere una dashboard utile a monitorare parametri dei database in funzione.

5.4.2

Utilizzo

Python ha diversi driver per utilizzare PostgreSql, il più popolare èpsycopg che implementa

completamente la specifica Python DB-API 2.0. La versione utilizzata è l’ultima, psycopg2. L’adapter per il database è implementato in C risulta veloce e sicuro. Tra le funzioni più note ricordiamo cursor lato client e lato server, notifiche e comunicazioni asincrone (ecc. . . ). Per l’utilizzo bisogna seguire una scaletta:

• Specificare la configurazione del server che ospita il database (viene definita una sola volta ed è statica)

con = psycopg2 . connect ( h o s t = " l o c a l h o s t " , database = " t e s t " , user = " p o s t g r e s " ,

password = f . d e c r y p t ( passw_postgres ) . decode ( ) )

• Ogni volta che si ha il bisogno di interrogare il database aprire ed eseguire un cursor dalla connessione

c u r s o r = con . c u r s o r ( )

c u r s o r . execute ( " SELECT ∗ from us er s WHERE username = ’%s ’ " % ( user , ) ) (in questo caso la query ritorna un array bidimensionale; il numero di righe è dato dal

numero di oggetti all’interno del database che hanno come username “mario”). Per ottenere il risultato bisogna eseguire la funzione:

row = c u r s o r . f e t c h a l l ( )

• Dopo ogni query bisogna committare le modifiche (nel caso in cui la query appena eseguita fosse un’operazione di modifica) e chiudere il cursore

con . commit ( ) c u r s o r . c l o s e ( )

• Alla fine del programma chiudere la connessione con . c l o s e ( )

5.4.3

Struttura del DB

Per il progetto sono servite due tabelle, una adibita alla memorizzazione dei dati riferiti al singolo utente (quali id, username, password criptata, token criptato e il flag che indica se l’utente è loggato) e una per le immagini (con id dell’utente come chiave esterna, un array di byte per persistere le immagini, la data in cui la foto è stata scattata e la specie predetta).

Users:

CREATE TABLE us er s (

i d BIGSERIAL NOT NULL PRIMARY KEY,

username VARCHAR( 3 0 0 ) NOT NULL , password VARCHAR( 3 0 0 ) NOT NULL , to ken VARCHAR( 3 0 0 ) NOT NULL , logged boolean

) ;

Images:

CREATE TABLE images (

u_image BYTEA NOT NULL , u _ i d BIGINT ,

d a t e t i m e t e x t ,

type VARCHAR( 3 0 0 ) ,

FOREIGN KEY ( u _ i d ) REFERENCES u se rs (i d )

) ;

5.5

Comunicazione Client-Server

Come detto in precedenza il server non ha un indirizzo pubblico, ma privato. E’ raggiungibile solo in locale digitando l’ip locale dell’host che esegue lo script in python del server.

chiave:

• Autenticazione del sito web visitato

• Protezione della privacy (riservatezza e confidenzialità)

• Integrità dei dati scambiati tra le parti comunicanti

Per implementare una comunicazione https in flask ci sono vari metodi:

• Un metodo ad-hoc (che genera delle chiavi on-fly diverse per ogni connessione)

• Self-Signed Certificates (quella utilizzata)

• Un metodo per Web Server usato in produzione (richiede un dominio)

Self-Signed Certificates

Il certificato autofirmato è quello in cui la firma viene generata utilizzando la chiave privata associata allo stesso certificato. Questo significa che nessuna CA (Certifcation Authority) ha firmato con la propria chiave, questo è il motivo per cui se si volesse accedere ad un URI il browser fornirà questa warning:

Tale messaggio comunica all’utente che il sito non è convalidato con le chiavi dell’ente di certificazione e l’utente può decidere di proseguire a suo rischio e pericolo. Per generare le chiavi auto-firmate bisogna installare il pacchetto openssl:

e digitare il comando:

o p e n s s l req −x509 −newkey r s a :4096 −nodes −o u t c e r t . pem −k e y o u t key . pem

−days 365

dove -days indica la validità delle chiavi in termini di tempo. Il comando appena esegui- to chiederà maggiori informazioni sull’ente che richiede le chiavi in modo da comunicare all’utente finale le origini del server (Organization Name, Email Address, Country Name ecc. . . ) Dopo aver generato le chiavi ed averle auto-firmate, nel server, come parametro della funzione run(), bisogna specificare il contesto ssl:

app . run ( h o s t = ’ 0 . 0 . 0 . 0 ’ , s s l _ c o n t e x t = ( ’ c e r t . pem ’ , ’ key . pem ’ ) ) Il risultato sarà avere una connessione https in cui l’utente ha la possibilità di sapere chi gestisce i dati inviati a quel server:

5.5.2

Mapping

Essendo un servizio REST i servizi messi a disposizione del server sono tutti mappati in URI differenti. Di seguito verranno elencati i parametri necessari affinchè ogni richiesta sia convalidata dal server e verranno elencate le possibili risposte. Per ogni map il server risponderà adeguatamente alle richieste POST, facendo una richiesta GET l’utente verrà

" username " : " xxxx " , " password " : " xxxx " }

Se l’username è già presente all’interno del DB il server risponderà con codice 205 (reim- posta contenuto) e con il seguente messaggio:

{ ’ message ’ : " User a l r e a d y e x i s t " }

Altrimenti in caso di convalida della richiesta il server comunicherà il token generato con codice 201 (creato):

{ " t oke n " : " xXyYzzzZ . . . " }

/login

Permette all’utente registrato di poter accedere ai servizi esposti dal server. Body da specificare:

{

" username " : " xxxx " , " password " : " xxxx " }

Risposta: Se l’username specificato non esiste nel database, 401(accesso negato): { ’ message ’ : ’ Wrong username ! ’ }

Se l’username è corretta e la password no, 401(accesso negato): { ’ message ’ : ’ Wrong password ! ’ }

Altrimenti se i parametri sono corretti, 200 (OK): {

’ message ’ : ’ Logged ’ , ’ t oke n ’ : ’ xxxYxXZZz ’ }

/logout

Permette la terminazione della sessione, per usufruire dei servizi l’utente dovrà ri-loggarsi. Body da specificare:

{

" to ken " : " xXyYzzzZ . . . " }

Con risposta, 200(OK):

{ ’ message ’ : " l o g o u t " }

/classify

E’ la richiesta utile per avere la predizione da parte del modello. Affinchè la richiesta vada a buon fine, l’utente deve essere loggato alla piattaforma. Body da specificare:

{

" img " : " b y t e A r r a y . . . " , " to ken " : " xXyYzzzZ . . . " }

Se l’immagine non fosse stata specificata, 205 (reimposta contenuto): { ’ message ’ : ’ Please s p e c i f y t h e image ’ }

Altrimenti, 200 (OK): { " s p e c i e " : " s ha rk " , " p r e c i s i o n e " : " x . y%" } /saveImage

L’utente, dopo aver scattato la foto, ha la necessità di salvare la foto nel proprio album. Il server riceve l’immagine e la salva nel database con u_id l’id dello user che vuole salvare la foto. Body da specificare:

{ " to ken " : " xxxyyxZzz " , " img " : " b y t e A r r a y " , " date " : " d a t e t i m e " , " t y p e " : " FishType " }

Se l’immagine non venisse specificata, ritornerebbe lo stesso messaggio di /classify. Nel caso in cui la data non fosse specificata:

{ ’ message ’ : ’ Please s p e c i f y t h e date ’ } In caso positivo:

" newpassword " : " newPassword " }

In caso positivo, 200 (OK):

{ ’ message ’ : " password m o d i f i e d " } Altrimenti, se la password da modificare non è corretta :

{ ’ message ’ : " password i s i n c o r r e c t " }

/loadImages

L’utente deve poter accedere al proprio album fotografico, visualizzando tutte le sue imma- gini catturate.

{ " t oke n " : " xXyYzzzZ . . . " }

E come risposta sarà ritornato l’array di immagini associate all’utente.

/delete

{

" to ken " : " xxxyyxZzz " , " date " : " d a t e t i m e " ,

}

L’immagine è identificata tramite la sua data di scatto. Come messaggio di risposta verra ritornato:

L’utente vuole poter eliminare immagini dal proprio album. { ’ message ’ : " image saved " }

NB: tutte le richieste che hanno bisogno del token per essere eseguite, hanno un unico

punto in cui è stata gestita la convalida del token: la funzione decoratrice @token_required

che in caso non lo convalidasse ritornebbe l’errore specificato al suo interno non permet- tendo il proseguimento della richiesta. Viene controllato che esso sia stato specificato al- l’interno del JSON e viene anche controllata la signature del token in modo da convalidare l’utente.

5.6

Funzioni di Encryption

Per aumentare la sicurezza dei dati sensibili nel codice e nei dati salvati su DB, sono state utilizzate delle funzioni di encryption. In particolare per questo bisogno è stata utilizzata la libreria ‘Fernet’.

key = F e r n e t . generate_key ( ) f = F e r n e t ( key )

to ken = f . e n c r y p t ( b "my deep dark s e c r e t " ) f . d e c r y p t ( t oke n ) # per d e c r i p t a r e

Lato server è stata criptata la password per accedere al database. Lato DB la password e il token associato ad ogni utente.

5.7

Unit test

Per lo sviluppo di unit test è stata utilizzata una libreria che non richiede installazione deno- minataunittest. Lato server sono state testate sostanzialmente le risposte di ogni singola

mappatura, verificando che il codice di risposta e il body siano come atteso.

• Creare un nuovo script allo stesso livello dello script da testare

• Importare la libreria

import u n i t t e s t

• Creare una classe che deriva dalla classe unittest.TestCase

class TestWebApp ( u n i t t e s t . TestCase ) :

• Definire test come funzioni che hanno un nome che inizia con test_

def test_GetPostMainPage ( s e l f ) :

Per eseguire i test digitare: py scriptTest.py Per risalire al code coverage è stata in- stallata una libreria coverage (pip install coverage) che ha il bisogno di eseguire i test con il comando

coverage run s c r i p t T e s t . py coverage r e p o r t

5.8

Librerie installate

Datetime

• CORS (affinchè ionic potesse comunicare con il server)

• Fernet

Capitolo 6

Client

L’utente deve poter comunicare con il server attraverso un’applicazione mobile. Grazie al- l’app l’utilizzatore è in grado di registrarsi alla piattaforma, loggarsi e utilizzare tutti i servizi messi a disposizone dal servente. Ha la possibilità di accedere a tutte le foto da lui scattate e classificate dal modello.

6.1

Ionic

Il framework utilizzato per lo sviluppo lato client è Ionic. Ionic framework è un toolkit di interfaccia utente open source per la creazione app mobili e desktop di alta qualità utilizzan- do tecnologie Web (HTML, CSS e JavaScript). Si concentra sull’interazione dell’interfaccia utente di un’app (controlli, interazioni, gesti, animazioni). Si integra perfettamente con altre librerie o framework come Angular, oppure può essere utilizzato in forma standalone. At- tualmente sta sviluppando il supporto per Vue e React. Punto di maggiore importanza è che con Ionic si creano app funzionanti su più piattaforme: IOS nativo, Android, Desktop e web come Progressive Web App. Vanta una vasta community pronta ad aiutare i futuri sviluppatori. Possiede un’interfaccia di linea di comando che include un server integrato, strumenti di creazione e debug (ecc..) che saranno argomentati nei capitoli successivi.

6.1.1

Installazione

Per l’installazione è stato utilizzatonpm che è il registro software più grande al mondo. Con

il comando (omettere sudo se si è sotto Windows) $ sudo npm i n s t a l l −g cordova

è possibile installare cordova che è il framework che permette di creare applicazioni mobili che non possono essere considerate né puramente native (il rendering è fatto con visualiz- zazioni web) né basate completamente sul web. Fornisce un layer intermedio tra funzioni native del dispositivo e ionic. Digitare poi (omettere sudo se si è sotto Windows)

Come detto ionic mette a disposizione un’interfaccia a linea di comando utile per creare un nuovo progetto, per debuggare le app che si stanno creando.

i o n i c s t a r t nomeApp <TIPO PROGETTO>

viene creato un progetto del tipo specificato, nel caso di FishClassifier <TIPO PROGETTO> = tabs.

Per eseguire l’app:

• su device connesso in modalità debug

i o n i c cordova run a n d r o i d device −−l i v e r e l o a d

• su browser(localhost:8100)

i o n i c s er ve −−l i v e r e l o a d

• su browser simulando il comportamento di IOS e Android i o n i c s er ve −l a b −−l i v e r e l o a d

6.2

Pagine

Lato applicazione l’utente interagisce con pagine html, stilate sotto regole CSS. Per la creazione di una pagina vuota digitare il comando:

i o n i c g e n e r a t e page

Dopo la creazione di un progetto in Ionic vengono già generate delle pagine in base al tipo di progetto creato. In automatico vengono generati tutti i file di configurazione per l’importazione di moduli e plugins (package.json, node_modules)

6.2.1

Tabs

L’app creata implementa una navigazione basata su tab. Una volta loggato, l’utente è in grado di accedere alla sua scheda utente, al tab adibito allo scatto dell’immagine da clas- sificare e al proprio album. Questa navigazione è gestita tramite il ’RouterModule’ utilizzato nella tabs page che funge da contenitore di tutti i tab (in questo caso 3). In base all’icona selezionata viene caricata la giusta pagina associata a quel tab. Come si evince, ogni pa- gina corrisponde ad un tab e tutti i tab sono gestiti da un punto di snodo che definisce la pagina da renderizzare e i compononenti da caricare.

Profile Tab In questa pagina l’utente è in grado di terminare la propria sessione da quel

dispositivo, cambiare la propria password e visualizzare sotto che account è loggato. Per visualizzare questo dato, in fase di login, è stato memorizzato l’username dell’utente.

Camera Tab Rappresenta il tab più significativo, include la funzionalità di scatto della foto

Il seguente diagramma di sequenza esprime a pieno tutte le interazioni del tab con gli altri componenti della piattaforma:

Album Tab Quando la pagina viene avviata, viene fatta una richiesta asincrona al server

[H]

L’utente ha la possibilità di selezionare una delle foto presenti nella galleria per poter avere maggiori dettagli riguardanti la foto. A questo scopo è stato utilizzatoion-modal che non è

[H]

6.2.2

Login

E’ la pagina renderizzata al primo avvio o nel caso in cui nessun altro utente è rimasto collegato tramite quel device.

Sono presenti due text_input contenuti in un form che controlla che entrambi i campi non siano nulli e in tal caso rende disponibile il click del tasto ‘LOGIN’. Tutto è gestito in angular lato HTML inserendo il parametro disabled nel tag del bottone:

< i o n−b u t t o n s i z e =" l a r g e " t y p e =" s u b m i t " [ d i s a b l e d ] = " form . i n v a l i d " expand =" b l o c k " > Login < / i o n−b u t t o n >

Viene creato un JSON con le informazioni appena immesse e viene fatta la richiesta del login al server che tornerà la corrispondente risposta. In caso l’utilizzatore non avesse un account ha la possibilità di crearne uno digitando sulla parola ‘register’ accedendo al form per la registrazione.

6.2.3

Register

L’utente entra a far parte della piattaforma compilando i tre campi mostrati in figura. Come il form per il login, il tasto ‘REGISTER’ è disponibile solo se i campi non sono null. Dopo aver compilato tutti i campi e aver cliccato sul bottone, viene controllato che i due campi ‘Password’ e ‘Password again’ coincidano e in caso positivo viene fatta la richiesta al server.

6.2.4

Change password

La pagina viene renderizzata nel momento in cui l’utente, sotto il tab ‘Profile’, digita ‘CHAN- GE PASSWORD’.

Affinchè la password sia cambiata l’utente deve specificare la vecchia password e inserirne una nuova ripetuta due volte (come nel caso della pagina di registrazione). Dopo aver clic- cato sul bottone ‘CHANGE’ verrà fatta una richiesta al server che modificherà la password e dopo averla criptata, la salverà nel database.

6.3

Async Function

E’ una funzione che esegue in modo asincrono utilizzando una Promise per ritornare il risultato. Il risultato di una funzione asincrona è un oggetto AyncFunction. E’ stato utile definire una funzione asincrona quando il client effettua le richieste al server, mostrando la barra di caricamento in modo da dare un feedback all’utente che qualcosa sta accadendo dietro le quinte.

A questo proposito è stato utilizzato il componente ‘LoadingController’ ed è stata defini- ta una funzione asincrona al cui interno veniva creato lo spinner con il messaggio ‘Plea- se wait. . . ’, per poi farlo visualizzare fino a quando la funzione di richiesta non terminava (questo chiamando la funzione ‘dismiss()’ del loadingController).

{ headers : myHeaders } ) . s u b s c r i b e ( response = >{ } , e r r o r = >{ } , ( ) => { l o a d i n g C o n t r o l l e r . d i s m i s s ( ) ; } ) ;

l’ultima riga va specificata solo nel caso in cui si volesse utilizzare lo spinner che rimane in attesa che la richiesta venga eseguita.

6.5

CameraPreview plugin

La scelta di utilizzo di questo plugin è stata solo a scopo estetico; infatti esiste un plugin più semplice da utilizzare denominato ‘Camera’. La differenza sta nel fatto che per scattare una foto con il metodo più semplice veniva utilizzato il layout della fotocamera predefinito del device, con CameraPreview invece è stato possibile wrappare in un elemento HTML la visualizzazione della fotocamera.

Installazione

I o n i c cordova p l u g i n add cordova−p l u g i n−camera−p r e v i e w npm i n s t a l l @ionic−n a t i v e / camera−p r e v i e w

Dopo di ciò verificare sotto ‘plugins’ che esso sia stato scaricato; verificare anche che nel package.json sia riportato il plugin con il link al repository github.

Utilizzo

Prima di tutto importare il componente, specificarlo nel costruttore della pagina dove la ca- mera deve essere utilizzata. Impostare i principali parametri tra cui il punto di partenza della visualizzazione in termini x e y (corrisponde all’angolo in alto a sinistra della visualizzazio- ne della fotocamera), altezza e larghezza, settare a false il previewDrag che indica che la renderizzazione della camera non è mobile all’interno della pagina. Chiamare la funzione ‘startCamera(parametriSpecificati)’ di CameraPreview all’interno della funzione ‘ionViewDi- dEnter()’ che nel ciclo di vita di una pagina indica il momento in cui la pagina sta per esse- re caricata. Nella funzione ‘ionViewWillLeave’ (quando la pagina sta per essere distrutta) chiamare il metodo ‘stopCamera()’.

Nel documento Riconoscimento delle specie ittiche (pagine 30-51)

Documenti correlati