Analisi degli Accessi al Sistema di Verbalizzazione Esami Online

82
UNIVERSITÀ DEGLI STUDI DI TRIESTE Dipartimento di Ingegneria e Architettura Corso di Studi in Ingegneria dell'Informazione Analisi degli Accessi al Sistema di Verbalizzazione Esami Online Tesi di Laurea Triennale laureando Giacomo Garbin relatore Alberto Bartoli correlatore Eric Medvet ANNO ACCADEMICO 2015-2016

Transcript of Analisi degli Accessi al Sistema di Verbalizzazione Esami Online

Page 1: Analisi degli Accessi al Sistema di Verbalizzazione Esami Online

UNIVERSITÀ DEGLI STUDI DI TRIESTE

Dipartimento di Ingegneria e Architettura Corso di Studi in Ingegneria dell'Informazione

Analisi degli Accessi al Sistema di Verbalizzazione

Esami Online Tesi di Laurea Triennale

laureando Giacomo Garbin

relatore

Alberto Bartoli

correlatore Eric Medvet

ANNO ACCADEMICO 2015-2016

Page 2: Analisi degli Accessi al Sistema di Verbalizzazione Esami Online
Page 3: Analisi degli Accessi al Sistema di Verbalizzazione Esami Online

Indice Introduzione 4

Prerequisiti 4

Organizzazione del testo 4

Ringraziamenti 5

1. Dati di Partenza 8

1.1. Dataset Sessioni 9

1.2. Dataset Cineca 10

1.3. Dataset Utenti 10

1.4. Analisi & Domande 12

2. Sessioni di Verbalizzazione 14

2.1. Struttura del Dataset Cineca 16

2.1.1. Gerarchia e Contenuti 16

2.1.2. Linee Complete e Linee Incomplete 17

2.2. Estrazione dei jsessionid dal Dataset Cineca 17

2.2.1. Linee Incomplete 17

Carenza dati nel periodo 20-30 marzo 2016 22

2.2.2. Linee Complete 22

Attacco Shellshock 24

2.3. Intersezione jsessionid Sessioni / Cineca 25

3. Geolocalizzazione 28

3.1. Da Indirizzi IP a Coordinate Geografiche 29

3.2. Posizione delle Sessioni di Verbalizzazione 30

3.3. Aggiornamento delle Time Zone 32

3.3.1. Fasce Orarie 34

3.3.2. Giornate Feriali / Festive 35

4. User Agent 40

4.1. Scomposizione delle UA string in Token 41

4.1.1. Parsing: Primo Metodo 41

4.1.2. Parsing: Secondo Metodo 43

4.2. UA string: dalla Versione “fat” alla “slim” 44

4.3. Sessioni di Verbalizzazione: Desktop / Mobile 48

1

Page 4: Analisi degli Accessi al Sistema di Verbalizzazione Esami Online

5. Statistiche Utenti 50

5.1. Statistiche Aggregate 51

5.1.1. Ruoli 51

5.1.2. Dipartimenti 54

5.1.3. Utenti Strutturati 57

5.2. Caratterizzazione degli Utenti 59

5.2.1. Statistiche Individuali 60

Casi Particolari 62

5.2.2. Statistiche Complessive 63

Percentili 64

6. Mappa Interattiva 66

6.1. Strumenti dell’Applicazione 67

6.1.1. Control Panel 68

6.1.2. Info Window 68

6.1.3. User Selector 69

6.2. Struttura dell’Applicazione 71

6.2.1. Ciclo di interazione Utente-Mappa 71

6.2.2. Implementazione del Database 72

Session Table 73

IP Address Table 73

User Table 74

6.3. Caricamento dei Dati 74

6.3.1. Session Table 74

6.3.2. IP Address Table 76

6.3.3. User Table 77

6.3.4. Ulteriori Accorgimenti 78

Conclusione 80

Sequenza Richieste / Risposte HTTP delle Sessioni di Verbalizzazione 80

Profili Utenti 80

Grafici sulla Mappa Interattiva 80

2

Page 5: Analisi degli Accessi al Sistema di Verbalizzazione Esami Online

3

Page 6: Analisi degli Accessi al Sistema di Verbalizzazione Esami Online

Introduzione

L’oggetto dello studio illustrato nella presente relazione è un insieme di dati contenente diverse informazioni riguardo le sessioni autenticate al sistema ESSE3 e le richieste HTTP dirette al sistema di verbalizzazione, entrambe - sessioni e richieste - effettuate nel periodo compreso tra dicembre 2015 e marzo 2016.

Gli aspetti dei dati analizzati saranno molteplici. In primo luogo verrà presentato il metodo utilizzato per individuare, tra le sessioni autenticate al sistema ESSE3, quelle inerenti al sistema di verbalizzazione.

Si vedrà poi come sia stato possibile geolocalizzare le posizioni di queste particolari sessioni e come il completamento di tale operazione abbia permesso di aggiornare i riferimenti temporali delle sessioni a seconda dell’ubicazione delle stesse.

Un altro aspetto importante dell’analisi verterà sullo studio degli user agent utilizzati dai client responsabili delle sessioni.

Infine l’attenzione sarà focalizzata sugli utenti responsabili delle sessioni, dei quali saranno osservati diversi aspetti statistici.

Prerequisiti La comprensione della presente relazione non presuppone la conoscenza di particolari nozioni

matematiche e in particolare nozioni statistiche. Mentre per quanto riguarda i linguaggi di programmazione, durante l’intera trattazione

dell’analisi dei dati verrà fatto un uso cospicuo del linguaggio R. Inoltre nei capitoli secondo, quarto e sesto verranno presentati dei listati rispettivamente nei linguaggi Bash Script, JavaScript e SQL.

Organizzazione del testo La struttura della relazione è organizzata in capitoli. I primi cinque sono dedicati all’analisi

dei dati oggetto dello studio, mentre nel sesto ed ultimo capitolo viene presentata l'applicazione web sviluppata di pari passo con l’analisi dei dati. Segue un sunto degli argomenti trattati in ciascun capitolo.

§ 1. Dati di Partenza

Nel primo capitolo vengono presentate le principali fonti di informazione su cui sono stati basati l’analisi e lo sviluppo dell’applicazione web presentata al capitolo sesto. In particolare verranno riportate le prime osservazioni sui dati raccolti e le preliminari operazioni di importazione e di formattazione delle informazioni. A conclusione del capitolo viene riportata la lista di domande utilizzata come schema guida nello svolgimento dell’analisi.

§ 2. Sessioni di Verbalizzazione

Nel secondo capitolo si vedrà come sia stato possibile raggiungere uno dei principali obbiettivi preposti allo studio, ossia l’individuazione delle sessione afferenti al sistema di verbalizzazione tra quelle autenticate al sistema ESSE3. Si vedrà come la soluzione adottata ha

4

Page 7: Analisi degli Accessi al Sistema di Verbalizzazione Esami Online

richiesto l’analisi di un insieme di logfile, ciascuno dei quali costituito - in media - da circa 90 mila linee, per un totale di oltre 22 milioni di linee.

§ 3. Geolocalizzazione

Nel terzo capitolo verrà affrontato un altro argomento cardine dello studio, ovvero la geolocalizzazione delle posizioni da cui sono state eseguite le sessioni. L’applicazione web presentata al capitolo sesto è stata sviluppata a partire dai dati ricavati da tale processo. Le informazioni geografiche acquisite consentiranno inoltre di correggere gli istanti di inizio e di fine sessione in base alla rispettiva time zone.

§ 4. User Agent

Il capitolo quattro viene dedicato allo studio degli user agent utilizzati dai client responsabili delle sessioni. Verrà descritto il procedimento - e le difficoltà che ne sono scaturite - adottato per estrarre dalle user agent string le informazioni essenziali. La versione compatta delle user agent string così ricava verrà sostituita alle versione originale, rendendo così l’analisi più agevole e i risultati in generale più affidabili.

§ 5. Statistiche Utenti

Nel capitolo cinque l’analisi viene focalizzata sugli utenti responsabile delle sessioni. Gli utenti saranno in primo luogo aggregati secondo una caratteristica comune (quali il ruolo o il dipartimento) e in seguito considerati individualmente. Per ciascuno di essi saranno calcolate diverse statistiche che verranno utilizzate - come si vedrà al capitolo sesto - nell’ambito dell’applicazione web sviluppata di pari passo con l’analisi.

§ 6. Mappa Interattiva

Il sesto capitolo si discosta dai precedenti in quanto l’argomento trattato non sarà un particolare aspetto dell’analisi dei dati studiati, bensì la presentazione dell’applicazione web il cui sviluppo si è affiancato all’osservazione dei dataset. Verranno presentati gli strumenti messi a disposizione dall’applicazione e sarà descritta la struttura dell’applicativo, con particolare enfasi al database relazionale e alle operazioni di caricamento dei dati.

Ringraziamenti È tardi purtroppo quando inizio a scrivere questo trafiletto e mancano poche ore alla scadenza

del termine ultimo per la consegna del materiale da porre sotto esame alla commissione di laurea, quindi sono costretto ad essere breve anche se questo momento, il momento della discussione della mia tesi di laurea “triennale”, avrebbe meritato forse qualche parole in più.

Per ringraziare i miei genitori non basterebbero tutte le pagine da qui alla fine di questo documento, per sdebitarmi con loro di tutti i sacrifici fatti e per scusarmi di tutte le delusioni arrecatigli non basterebbe una vita intera. Posso solo rinnovare la promessa, in primis fatta a me stesso, che da qui in avanti e come ho fatto negli ultimi due anni cercherò di guadagnare giorno dopo giorno la fiducia che nonostante tutto hanno sempre posto in me.

Il secondo grazie lo dedico ad Alessio, il quale è stato sicuramente uno dei fattori chiave della fase due della mia carriera universitaria e probabilmente della mia vita. Thanks buddy.

5

Page 8: Analisi degli Accessi al Sistema di Verbalizzazione Esami Online

Infine contravvenendo a quanto suggerisce Umberto Eco, il quale scrive “È di cattivo gusto ringraziare il relatore. Se vi ha aiutato ha fatto solo il suo lavoro. ”, voglio dedicare alcune righe al professor Bartoli.

È sicuramente vero che quando un relatore segue un suo laureando, sta in quel momento facendo soltanto il suo lavoro; ma è altrettanto vero che quando un laureando lavora con impegno e costanza alla sua tesi, sta in quel momento facendo soltanto il suo dovere. Eppure il professor Bartoli non ha mai risparmiato un complimento o un apprezzamento nei confronti di un lavoro che, a prescindere dal risultato, intuiva essere costato tempo e fatica allo studente, il quale sentendosi gratificato per lo sforzo compiuto ritrova voglia ed energie per il prosieguo del lavoro.

Oltre questo aspetto se vogliamo “umano”, in questi mesi ho ritrovato il docente scrupoloso conosciuto ai tempi del corso di Reti di Calcolatori. Il maestro capace di esprimere concetti chiave comprimendoli in pochissime parole, i quali vanno ad arricchire il bagaglio culturale e professionale dello studente e si imprimono come dogmi nella memoria. Ad esempio, la frase “Lei lo ha mai letto il manuale per usare gmail? No, perché non esiste. ” si accenderà, come una spia luminosa, ogniqualvolta mi capiterà di progettare una interfaccia grafica.

Il professor Bartoli in tutto questo sta facendo “soltanto” il suo lavoro, ma è indiscutibile che il suo lavoro la sappia fare estremamente bene.

6

Page 9: Analisi degli Accessi al Sistema di Verbalizzazione Esami Online

7

Page 10: Analisi degli Accessi al Sistema di Verbalizzazione Esami Online

1. Dati di Partenza

L’analisi si è incentrata inizialmente su tre insiemi di dati (o dataset), ognuno dei quali contiene delle informazioni specifiche.

dataset sessioni Informazioni riguardo le sessioni autenticate al sistema ESSE3 registrate nel periodo compreso tra dicembre 2015 e marzo 2016.

dataset cineca Informazioni fornite da CINECA su una parte delle richieste HTTP dirette al sistema di verbalizzazione registrate nel periodo compreso tra dicembre 2015 e marzo 2016.

dataset utenti Informazioni su un sottoinsieme degli utenti responsabili delle sessioni autenticate al sistema ESSE3.

In questo primo capitolo verrà descritta la struttura dei dataset presentati e per ciascuno di essi

saranno riportate le iniziali operazioni di raffinamento e di analisi compiute.

8

Page 11: Analisi degli Accessi al Sistema di Verbalizzazione Esami Online

1.1. Dataset Sessioni Il dataset sessioni si presenta sotto forma di spreadsheet. Ciascuna riga rappresenta una e una

sola sessione autenticata al sistema ESSE3 registrata nel periodo compreso tra dicembre 2015 e marzo 2016.

Tutti i più noti software odierni per la gestione di fogli di calcolo offrono la possibilità di convertire una tabella in formato testo; specificando allora il carattere di tabulazione (tab) come separatore tra i campi delle righe, è possibile importare il dataset in R tramite la seguente istruzione. > session.dat <- read.table("session.txt", sep = " \t" , header = TRUE, stringsAsFactors = FALSE)

Al termine dell’importazione, la struttura del dataframe session.dat dovrebbe essere simile

alla seguente. > str(session.dat)

'data.frame': 27514 obs. of 16 variables :

$ SESSIONID : chr "8ibhjtPbD08NMKTrG74y" "RkJF9m6XevAAnbVCMVcT" ...

$ ID_USER : int 110725 112158 115803 110619 111969 114533 111176 112413 ...

$ GRP_ID : int 7 7 7 7 7 7 7 7 7 7 ...

$ JAG_NAME : chr "units_pw2_jb7" "units_pw2_jb6" "units_pw2_jb7" "units_pw1_jb7" ...

$ JAG_LOCATION : chr "esse3appjboss07.priv" "esse3appjboss06.priv" "esse3appjboss07.priv"

...

$ CL_IP_PART : chr "79.21.239.xyz" "140.105.167.xyz" "2.34.134.xyz" "140.105.100.xyz"

...

$ CL_TYPE : chr "WEB" "WEB" "WEB" "WEB" ...

$ DTM_CL_CONN : chr "12/1/2015 6:54" "12/1/2015 7:47" "12/1/2015 7:49" "12/1/2015 7:59"

...

$ DTM_CL_REFRESH : chr "12/1/2015 6:54" "12/1/2015 7:47" "12/1/2015 7:49" "12/1/2015 7:59"

...

$ DTM_END_CONNECTION : chr "12/1/2015 7:29" "12/1/2015 8:18" "12/1/2015 8:46" "12/1/2015 8:30"

...

$ HTTP_SESSION_TIMEOUT : int 30 30 30 30 30 30 30 30 30 30 ...

$ SOURCE : chr "ESSE3" "ESSE3" "ESSE3" "ESSE3" ...

$ TOMCAT_LOCATION : chr "127.0.1.1" "127.0.1.1" "127.0.1.1" "127.0.1.1" ...

$ JSESSIONID : chr "FECBB6A88E651BB2A482B7E9FB08B438.esse3-units-prod-03" ...

$ USER_AGENT : int 6462 863 5948 1198 5191 6717 6170 6170 6462 7116 ...

$ UA_STRING : chr "Mozilla/5.0 (Macintosh§ Intel Mac OS X 10.10§ rv:42.0) ..." ...

Si osserva subito come le osservazioni siano 27,514 e come alcune variabili assumano un

valore costate; queste ed altre variabili non verranno utilizzate nel corso dell’analisi, per tale motivo si procede alla ristrutturazione del dataframe con le istruzioni seguenti. > session.dat <- session.dat[c(1 , 14 , 6 ,8 , 10 , 2, 15 , 16 )]

> names(session.dat) <- c("session_id", "jsessionid", "ip_address", "session_start" , "session_end",

"user_id" , "ua_id" , "ua_string")

9

Page 12: Analisi degli Accessi al Sistema di Verbalizzazione Esami Online

Al termine delle istruzioni, la struttura risultante dovrebbe essere simile alla seguente. > str(session.dat)

'data.frame': 27514 obs. of 8 variables :

$ session_id : chr "8ibhjtPbD08NMKTrG74y" "RkJF9m6XevAAnbVCMVcT" "nCdxoswmkHCKBJrjWqVK" ...

$ jsessionid : chr "FECBB6A88E651BB2A482B7E9FB08B438.esse3-units-prod-03" ...

$ ip_address : chr "79.21.239.xyz" "140.105.167.xyz" "2.34.134.xyz" "140.105.100.xyz" ...

$ session_start : chr "12/1/2015 6:54" "12/1/2015 7:47" "12/1/2015 7:49" "12/1/2015 7:59" ...

$ session_end : chr "12/1/2015 7:29" "12/1/2015 8:18" "12/1/2015 8:46" "12/1/2015 8:30" ...

$ user_id : int 110725 112158 115803 110619 111969 114533 111176 112413 112938 160073 ...

$ ua_id : int 6462 863 5948 1198 5191 6717 6170 6170 6462 7116 ...

$ ua_string : chr "Mozilla/5.0 (Macintosh§ Intel Mac OS X 10.10§ rv:42.0) Gecko/20100101 ..."

...

Il significato delle variabili preservate è il seguente.

session id Identificatore univoco della sessione.

jsessionid Identificatore non univoco della sessione; come si vedrà in maggiore dettaglio nel prossimo capitolo, il valore assunto da questa variabile permette di stabilire quali delle sessione autenticate hanno interessato il sistema di verbalizzazione online.

ip address Primi tre byte dell’indirizzo IP dell’host responsabile della sessione.

session start, session end

Istanti di inizio e di fine sessione espressi nel formato “month/day/year hour:minute”.

user id Identificatore dell’utente responsabile della sessione.

ua id, ua string

Identificatore e stringa dello user agent utilizzato dall’utente responsabile della sessione.

1.2. Dataset Cineca All’analisi di questo insieme di dati è stato dedicato l’intero secondo capitolo, perciò in

questo paragrafo verrà riportata solamente una breve descrizione dell’organizzazione del dataset e delle informazioni contenute al suo interno.

I dati forniti da CINECA sono distribuiti su un insieme di file di testo, per i quali in prima approssimazione (si vedrà nel prossimo capitolo come questo non risulta sempre verificato) ciascuna linea rappresenta una richiesta HTTP diretta al sistema di verbalizzazione.

È opportuno specificare fin da ora che cosa si intende per “richiesta HTTP diretta al sistema di verbalizzazione”. Con tale espressione si identificano tutte le richieste dirette al sistema di verbalizzazione e non esclusivamente le effettive verbalizzazioni di un esame; sono comprese quindi, ad esempio, anche le richieste relative alle operazioni di consultazione di verbali, oppure all’inserimento o all’aggiornamento di appelli, etc.

10

Page 13: Analisi degli Accessi al Sistema di Verbalizzazione Esami Online

1.3. Dataset Utenti Anche il dataset utenti si presenta sotto forma di spreadsheet. Ciascuna riga rappresenta uno e

un solo utente tra un sottoinsieme degli utenti (come si osserverà nel capitolo quinto dedicato all’analisi degli utenti) responsabili delle sessioni autenticate al sistema ESSE3.

Convertita la tabella in un file di testo, scegliendo nuovamente il carattere di tabulazione come separatore tra i campi, è possibile importare i dati in R tramite l’istruzione seguente. > user.dat <- read.table("user.txt" , sep = "\t " , header = TRUE , stringsAsFactors = FALSE, quote =

"" )

Al termine dell’importazione, la struttura del dataframe user.dat dovrebbe essere simile alla

seguente. > str(user.dat)

'data.frame': 1075 obs. of 4 variables:

$ ID_USER : int 109762 109794 109815 109825 109871 109873 109891 109919 109936 109937 ...

$ RUOLO_DES: chr "Professore Ordinario" "Co.Co.Co." "Co.Co.Co." "Professore Ordinario" ...

$ DIP : chr "[017080] Dipartimento di Matematica e Geoscienze" ...

$ STRUT_FLG: int 1 0 0 1 0 1 0 0 1 1 ...

Le osservazioni sono 1,075. Si procede alla ristrutturazione del dataframe, aggiungendo due

nuove variabili per ospitare gli identificatori dei ruoli e dei dipartimenti degli utenti. > names(user.dat) <- c( "user_id", "role", "department", "structured")

> roles <- data.frame (role = unique(user.dat$ role), role_id = 1 : length(unique(user.dat$ role)),

role_string = c( "Professore Ordinario", "Co.Co.Co.", "Professore Associato" , "Cat. EP - Area

Tecnica, Tecnico-Scientifica ed Elaborazione Dati" , "Ricercatore Universitario", "Cat. D - Area

Socio-Sanitaria" , "Collaboratore Esperto Linguistico" , "Cat. D - Area Tecnica, Tecnico-Scientifica

ed Elaborazione Dati" , "Cat. C - Area Tecnica, Tecnico-Scientifica ed Elaborazione Dati", "Cat. EP

- Area Medico-Odontoiatrica e Socio-Sanitaria", "Libero Professionista", "Assegnista di Ricerca" ,

"Ricercatore a Tempo Determinato", "Personale Esterno", "Cat. D - Area Amministrativa-Gestionale" ,

"Cat. C - Area Amministrativa" , "Dottorato di Ricerca" , "Lavoratore Autonomo" ), stringsAsFactors =

FALSE)

> departments <- data.frame (department = unique(user.dat $ department), department_id =

ifelse(substr(unique(user.dat $ department), 2, 7 ) == "000000" , NA_character_,

substr(unique(user.dat $ department), 2 , 7 )), department_string = c("Dipartimento di Matematica e

Geoscienze", "Dipartimento di Ingegneria e Architettura" , "Dipartimento di Scienze Chimiche e

Farmaceutiche" , "Dipartimento di Scienze della Vita", "Dipartimento di Scienze Economiche,

Aziendali, Matematiche e Statistiche" , "Dipartimento di Scienze Giuridiche, del Linguaggio,

dell'Interpretazione e della Traduzione", "Dipartimento di Matematica e Informatica" , "Dipartimento

di Studi Umanistici", "Dipartimento Universitario Clinico di Scienze Mediche, Chirurgiche e della

Salute" , "Dipartimento di Scienze Politiche e Sociali", "Dipartimento di Fisica", "Ex Facoltà di

Scienze della Formazione", "Centro Linguistico di Ateneo" , NA_character_, "Dipartimento di

Italianistica Linguistica Comunicazione Spettacolo", "Ex Facoltà di Medicina e Chirurgia" , "Ex

Scuola Superiore di Lingue Moderne per Interpreti e Traduttori" , "Ex Facoltà di Ingegneria",

11

Page 14: Analisi degli Accessi al Sistema di Verbalizzazione Esami Online

"Ufficio Industrial Liaison Office e Placement", "Unità di Staff Servizio di Prevenzione e

Protezione", "Segreteria Didattica DF", "Segreteria Amministrativa DSCF", "Ex Facoltà di

Psicologia"), stringsAsFactors = FALSE)

> user.dat <- merge(merge(user.dat[c( "user_id", "role", "structured")], roles)[- 1 ],

merge(user.dat[c("user_id" , "department", "structured")], departments)[- 1 ])

> user.dat $structured <- as.logical(user.dat$ structured)

Al termine delle istruzioni precedenti, la struttura del dataframe user.dat dovrebbe risultare la

seguente. > str(user.dat)

'data.frame': 1075 obs. of 6 variables :

$ user_id : int 109762 109794 109815 109825 109871 109873 109891 109919 109936 109937

...

$ structured : logi TRUE FALSE FALSE TRUE FALSE TRUE ...

$ role_id : int 1 2 2 1 2 1 2 2 1 1 ...

$ role_string : chr "Professore Ordinario" "Co.Co.Co." "Co.Co.Co." "Professore Ordinario"

...

$ department_id : chr "017080" "042000" "029000" "084000" ...

$ department_string: chr "Dipartimento di Matematica e Geoscienze" "Dipartimento di ..." ...

Il significato delle variabili è il seguente.

user id Identificatore dell’utente.

structured Flag per gli utenti strutturati.

role id, role string

Identificatore e nome del ruolo dell’utente.

department id, department string

Identificatore e nome del dipartimento dell’utente.

1.4. Analisi & Domande L’analisi dei dati oggetto della presente relazione può essere interpretata anche come la

ricerca delle risposte alla seguente lista di domande, in cui gli interrogativi sono raggruppati in base al capitolo della relazione dove compare la risposta.

Per semplicità di riferimento, nel prosieguo del paragrafo si utilizzerà l’espressione “sessioni di verbalizzazione” per indicare tutte le sessioni autenticate al sistema ESSE3 che interessano il sistema di verbalizzazione.

§ 2. Sessioni di Verbalizzazione

○ Qual è la percentuale di sessioni di verbalizzazione?

§ 3. Geolocalizzazione

○ Come sono distribuite spazialmente le sessioni di verbalizzazione?

12

Page 15: Analisi degli Accessi al Sistema di Verbalizzazione Esami Online

○ Qual è la percentuale di sessioni di verbalizzazioni eseguite dall’Italia? ○ Qual è la percentuale di sessioni di verbalizzazioni eseguite dal Friuli-Venezia Giulia? ○ Come sono distribuite le sessioni di verbalizzazione tra le fasce orarie giornaliere? ○ Come sono distribuite le sessioni di verbalizzazione tra giornate feriali e festive?

§ 4. User Agent

○ Qual è la percentuale di sessioni di verbalizzazione eseguite da dispositivi mobile?

§ 5. Statistiche Utenti

○ Qual è la distribuzione delle sessioni di verbalizzazione tra gli utenti aggregati per ruolo? ○ Qual è la distribuzione delle sessioni di verbalizzazione tra gli utenti aggregati per

dipartimento? ○ Qual è la distribuzione delle sessioni di verbalizzazione tra gli utenti aggregati in base al flag

structured ? ○ Per ciascun utente qual è …

… il numero di sessioni? … la percentuale delle sessioni di verbalizzazione? … il numero di indirizzi IP unici da cui sono state eseguite le sessioni di verbalizzazione? … il numero di user agent unici utilizzati durante le sessioni di verbalizzazione? … il numero delle coppie (indirizzo IP, user agent) uniche utilizzate durante le sessioni di verbalizzazione? … il numero di paesi del mondo da cui sono state eseguite le sessioni di verbalizzazione? … la percentuale delle sessioni di verbalizzazione eseguite dall’Italia? … il numero di regioni italiane da cui sono state eseguite le sessioni di verbalizzazione? … la percentuale delle sessioni di verbalizzazione eseguite dal Friuli-Venezia Giulia? … la percentuale delle sessioni di verbalizzazione eseguite in giornate festive? … la durata media in minuti delle sessioni di verbalizzazione?

○ Quali sono le statistiche complessive dei parametri calcolati alla domanda precedente? ○ Quali sono gli utenti che non hanno effettuato sessioni di verbalizzazione? ○ Quali sono gli utenti che hanno effettuato sessioni di verbalizzazione esclusivamente in

giornate festive?

§ 6. Mappa Interattiva

○ Quali sono gli utenti che hanno utilizzato durante le sessioni di verbalizzazione un numero di coppie (indirizzo IP, user agent) tale da non appartenere all’intervallo definito dal primo e dal terzo quartile?

○ Quali sono tra i ricercatori universitari del dipartimento di ingegneria e architettura gli utenti per i quali la percentuale delle sessioni di verbalizzazione effettuate in giornate festive sia superiore al terzo quartile e la durata media delle sessioni di verbalizzazione sia inferiore al primo quartile?

13

Page 16: Analisi degli Accessi al Sistema di Verbalizzazione Esami Online

2. Sessioni di Verbalizzazione

In questo capitolo si vedrà come sia stato possibile individuare tra le sessioni autenticate al sistema ESSE3, quelle inerenti il sistema di verbalizzazione.

Innanzitutto verrà descritta, in maniera più accurata di quanto già fatto nel capitolo precedente, la struttura del dataset cineca evidenziando in particolare alcune “irregolarità” individuate. Dopodichè verrà presentato lo script Bash realizzato per estrarre dal dataset cineca le informazioni utili allo scopo preposto. Infine le informazioni raccolte saranno confrontate con quelle presenti nel dataset sessioni e i risultati saranno esposti con l'ausilio di un grafico a barre.

14

Page 17: Analisi degli Accessi al Sistema di Verbalizzazione Esami Online

2.1. Struttura del Dataset Cineca I dati forniti da CINECA sono distribuiti su un insieme di 244 file di testo e rappresentano un

sottoinsieme delle richieste HTTP dirette al sistema di verbalizzazione registrate nel periodo compreso tra dicembre 2015 e marzo 2016.

2.1.1. Gerarchia e Contenuti L’insieme dei file si presenta con l’organizzazione gerarchica seguente. apache

├── dicembre

│ ├── FE03

│ │ http_units.esse3.cineca.it_ssl_access-20151201.log

│ │ http_units.esse3.cineca.it_ssl_access-20151202.log

┊ ┊ ⋮

│ │ http_units.esse3.cineca.it_ssl_access-20151230.log

│ │ http_units.esse3.cineca.it_ssl_access-20151231.log

│ │

│ └── FE04

│ http_units.esse3.cineca.it_ssl_access-20151201.log

│ http_units.esse3.cineca.it_ssl_access-20151202.log

┊ ⋮

│ http_units.esse3.cineca.it_ssl_access-20151230.log

│ http_units.esse3.cineca.it_ssl_access-20151231.log

├── febbraio

│ ├── FE03

│ │ http_units.esse3.cineca.it_ssl_access-20160201.log

│ │ http_units.esse3.cineca.it_ssl_access-20160202.log

┊ ┊ ⋮

│ │ http_units.esse3.cineca.it_ssl_access-20160228.log

│ │ http_units.esse3.cineca.it_ssl_access-20160229.log

│ │

│ └── FE04

│ http_units.esse3.cineca.it_ssl_access-20160201.log

│ http_units.esse3.cineca.it_ssl_access-20160202.log

┊ ⋮

│ http_units.esse3.cineca.it_ssl_access-20160228.log

│ http_units.esse3.cineca.it_ssl_access-20160229.log

├── gennaio

│ ├── FE03

│ │ http_units.esse3.cineca.it_ssl_access-20160101.log

│ │ http_units.esse3.cineca.it_ssl_access-20160102.log

┊ ┊ ⋮

│ │ http_units.esse3.cineca.it_ssl_access-20160130.log

│ │ http_units.esse3.cineca.it_ssl_access-20160131.log

│ │

│ └── FE04

│ http_units.esse3.cineca.it_ssl_access-20160101.log

│ http_units.esse3.cineca.it_ssl_access-20160102.log

┊ ⋮

│ http_units.esse3.cineca.it_ssl_access-20160130.log

│ http_units.esse3.cineca.it_ssl_access-20160131.log

15

Page 18: Analisi degli Accessi al Sistema di Verbalizzazione Esami Online

└── marzo

├── FE03

│ http_units.esse3.cineca.it_ssl_access-20160301.log

│ http_units.esse3.cineca.it_ssl_access-20160302.log

┊ ⋮

│ http_units.esse3.cineca.it_ssl_access-20160330.log

│ http_units.esse3.cineca.it_ssl_access-20160331.log

└── FE04

http_units.esse3.cineca.it_ssl_access-20160301.log

http_units.esse3.cineca.it_ssl_access-20160302.log

http_units.esse3.cineca.it_ssl_access-20160330.log

http_units.esse3.cineca.it_ssl_access-20160331.log

Si nota come per ciascuna giornata sono presenti due logfile, collocati separatamente nelle cartelle FE03 e FE04. Tali cartelle corrispondono rispettivamente alle due istanze logiche del server Apache preposte all’erogazione dei servizi del sistema ESSE3. Il logfile completo della singola giornata si ottiene dunque concatenando i due file corrispondenti.

Le linee dei logfile hanno tutte in prima approssimazione (si vedrà tra poco come ciò in realtà non risulti sempre verificato) il seguente formato. 87.21.191.xyz - - [01/Dec/2015:14:09:27 +0100] "GET /css/units.css HTTP/1.1" 200 276

"https://esse3.units.it/auth/Login.do" "Mozilla/5.0 (Windows NT 10.0; WOW64; rv:42.0)

Gecko/20100101 Firefox/42.0" 4782089306950F662E67E9DA6414D14D.esse3-units-prod-03 0

I campi tenuti in considerazione nel prosieguo dell’analisi saranno i seguenti.

87.21.191.xyz Indirizzo IP dell’host responsabile della richiesta.

[01/Dec/2015:14:09:27 +0100] Instante in cui la richiesta è stata ricevuta, nel formato

[day/month/year:hour:minute:second zone]

"GET /css/units.css HTTP/1.1"

Richiesta del client.

200

Status code inviato dal server al client.

276

Dimensione in byte dell’oggetto restituito al client oppure il carattere “-” se non viene restituito alcun contenuto.

"https://esse3.units.it/auth/Login.do"

Valore dell’header HTTP referer della richiesta.

"Mozilla/5.0 (Windows NT 10.0; WOW64; rv:42.0) Gecko/20100101 Firefox/42.0"

16

Page 19: Analisi degli Accessi al Sistema di Verbalizzazione Esami Online

UA string del client.

4782089306950F662E67E9DA6414D14D.esse3-units-prod-03 Valore della variabile jsessionid della sessione.

2.1.2. Linee Complete e Linee Incomplete Tutte le linee di ciascun logfile hanno una lunghezza massima di 1,024 caratteri. Tuttavia la

somma dei caratteri di un record può eccedere questo limite; ciò accade tipicamente per le richieste dove i campi request e http referer risultano particolarmente lunghi. In tal caso, il record viene distribuito su più linee in generale non adiacenti l’una all’altra.

A causa di questo comportamento (apparentemente imprevedibile), anche laddove si suppone che la distribuzione sia avvenuta su linee contigue, non è possibile ricostruire un record distribuito su più linee semplicemente concatenando le linee adiacenti ed escludere al contempo la possibilità di introdurre del rumore nell’informazione.

L’analisi procederà distinguendo le linee complete , ovvero le linee ospitanti un record di lunghezza minore o uguale a 1,024 caratteri, dalle linee incomplete , le linee ospitanti parte di un record di lunghezza maggiore a 1,024 caratteri.

2.2. Estrazione dei jsessionid dal Dataset Cineca In questo e nel prossimo paragrafo si cercherà di dare risposta alla domanda “quali sono e

qual è la percentuale delle sessioni autenticate al sistema ESSE3 ad interessare il sistema di verbalizzazione? ”.

Nel capitolo precedente è stata descritta la struttura del dataset sessioni, osservando la presenza della variabile jsessionid nel dataframe session.dat; inoltre, nel paragrafo precedente di questo capitolo, si è osservata la presenza del parametro jsessionid anche nel penultimo campo dei record del dataset cineca. Dunque, intersecando i valori assunti dall’identificatore jsessionid nei due dataset, è possibile isolare le sessioni autenticate al sistema ESSE3 relative al sistema di verbalizzazione.

L’estrazione dei jsessionid dal dataset cineca procederà separando le linee complete da quelle incomplete; nel primo caso l’intera informazione contenuta in un record verrà preservata, nel secondo caso invece si tenterà di estrarre un jsessionid dalle linee incomplete, scartando il resto dell’informazione.

2.2.1. Linee Incomplete Lo script Bash presentato in questo sottoparagrafo ha un duplice obiettivo: primo, riconoscere

e separare le linee complete da quelle incomplete e, secondo, estrarre da quest'ultime - dove possibile - un jsessionid.

Lo script riceve in input il path di una cartella e analizza tutti i logfile in essa contenuti, restituendo per ciascun file analizzato un nuovo logfile in cui vengono preservate tutte e sole le linee complete ed inoltre un ulteriore file unico dove vengono riportati i jsessionid estratti dalle linee incomplete di tutti i logfile analizzati.

17

Page 20: Analisi degli Accessi al Sistema di Verbalizzazione Esami Online

#!/bin/bash

if [[ ! -d $1 ]] ; then

echo " wrong input: \"$1 \" is not a directory"

exit

fi

report= "cineca_report.txt"

if [ ! -f $report ]; then

touch $report

echo -e

"FILE_NAME\tFILE_PATH\tTOTAL_LINES\tCOMPLETE_LINES\tINCOMPLETE_LINES\tJESSIONID_LINES\tDROPPED_LINE

S\tTIMESTAMP_START\tTIMESTAMP_END" >> $report

fi

dir=` pwd `

cd $1

month=` pwd | grep -Eo "dicembre|gennaio|febbraio|marzo"`

FE0x=` pwd | grep -Eo "FE0[3,4]"`

path="apache/" $month"/" $FE0x"/"

jsessionid="cineca_" $month"_" $FE0x"_jsessionid.txt"

rm -f $jsessionid

touch $jsessionid

for log in `ls -1 * .log`

do

timestamp_start=`date`

date= ` echo $log | grep -Eo "[0-9]{8}"`

output= "cineca_"$date "_"$FE0x".log"

rm -f $output

touch $output

tempfile="cineca_" $date"_" $FE0x".$$.tmp"

rm -f $tempfile

touch $tempfile

total_lines= `wc -l $log | sed -e "s/ .*//g"`

i=0 # line counter

m=0 # complete lines

n=0 # incomplete lines

echo -e "> $log\n"

echo -e " START:\t$timestamp_start\n"

echo -e " SCAN FOR COMPLETE/INCOMPLETE LINES:"

echo -e " total lines:\t\t$total_lines"

echo -en " progress:\t\t-"

18

Page 21: Analisi degli Accessi al Sistema di Verbalizzazione Esami Online

while read line ; do

if [[ $line =~ (^[0-9]{1,3}\. [0-9]{1,3} \.[0-9]{1,3} \. [0-9]{1,3} - -

\[ [0-9]{2}/[A-Za-z]{3}/[0-9]{4}:[0-9]{2}:[0-9]{2}:[0-9]{2} \+ [0-9]{4} \] \" . * \" (- | [0-9]+)

(- | [0-9]+) \" . * \" \" . * \" (- | [0-9a-zA-Z.-]+) [0-9]+.{1}$) ]] ; then

# complete line

echo $line | sed -e "s/[\r\n]//g" >> $output

(( m ++ ))

else

# incomplete line

echo $line | sed -e "s/[\r\n]//g" >> $tempfile

(( n ++ ))

fi

(( i ++ ))

echo -en "$i $total_lines" | awk '{printf "\r progress:\t\t%.0f%%", $1/$2 * 100}'

done < $log

echo -e "\n complete lines:\t$m"

echo -e " incomplete lines:\t$n"

i=0 # line counter

j=0 # jsessionid

k=0 # dropped lines

echo -e "\n SCAN FOR LINES WITH ONE OR MORE JSESSIONID:"

echo -en " progress:\t\t-"

while read line ; do

if [[ $line =~ [0-9A-Z]{32}\. esse3-units-prod-0[0-6]{1} ]] ; then

# line with one or more jsessionid

echo $line | grep -Eo "[0-9A-Z]{32}\.esse3-units-prod-0[0-6]{1}" >> $jsessionid

(( j ++ ))

else

# line without jsessionid

(( k ++ ))

fi

(( i ++ ))

echo -en "$i $n" | awk '{printf "\r progress:\t\t%.0f%%", $1/$2 * 100}'

done < $tempfile

echo -e "\n jsessionid lines:\t$j"

echo -e " dropped lines:\t$k\n"

rm -f $tempfile

timestamp_end= `date`

echo -e " END:\t\t$timestamp_end\n"

19

Page 22: Analisi degli Accessi al Sistema di Verbalizzazione Esami Online

echo -e

$log"\t" $path"\t"$total_lines "\t"$m "\t"$n "\t"$j "\t"$k "\t" $timestamp_start"\t" $timestamp_end >>

$report

done

cd $dir

Ad ogni esecuzione lo script aggiorna inoltre il file cineca_report.txt dove vengono riportate,

per ciascun file analizzato, le seguenti informazioni.

file name Nome del logfile.

file path Percorso relativo del logfile all’interno della struttura gerarchica del dataset cineca.

total lines Numero di linee totali del logfile.

complete lines, incomplete lines

Numero di linee rispettivamente complete e incomplete del logfile.

jsessionid lines, dropped lines

Numero di linee incomplete del logfile da cui è stato possibile estrarre uno o più jsessionid e, rispettivamente, da cui non è stato possibile estrarre alcun jsessionid.

timestamp start, timestamp end

Istanti rispettivamente di inizio e fine analisi del logfile.

L’esecuzione dello script per ciascuna cartella “foglia” all’interno della struttura gerarchica

del dataset cineca, comporta la riorganizzazione seguente. apache

├── dicembre

│ ├── FE03

│ │ CINECA_20151201_FE03.log

│ │ CINECA_20151202_FE03.log

┊ ┊ ⋮

│ │ CINECA_20151231_FE03.log

│ │ CINECA_dicembre_FE03_jsessionid.txt

│ │

│ └── FE04

│ CINECA_20151201_FE04.log

│ CINECA_20151202_FE04.log

┊ ⋮

│ CINECA_20151231_FE04.log

│ CINECA_dicembre_FE04_jsessionid.txt

├── febbraio

│ ├── FE03

│ │ CINECA_20160201_FE03.log

│ │ CINECA_20160202_FE03.log

┊ ┊ ⋮

│ │ CINECA_20160229_FE03.log

│ │ CINECA_febbraio_FE03_jsessionid.txt

│ │

20

Page 23: Analisi degli Accessi al Sistema di Verbalizzazione Esami Online

│ └── FE04

│ CINECA_20160201_FE04.log

│ CINECA_20160202_FE04.log

┊ ⋮

│ CINECA_20160229_FE04.log

│ CINECA_febbraio_FE04_jsessionid.txt

├── gennaio

│ ├── FE03

│ │ CINECA_20160101_FE03.log

│ │ CINECA_20160102_FE03.log

┊ ┊ ⋮

│ │ CINECA_20160131_FE03.log

│ │ CINECA_gennaio_FE03_jsessionid.txt

│ │

│ └── FE04

│ CINECA_20160101_FE04.log

│ CINECA_20160102_FE04.log

┊ ⋮

│ CINECA_20160131_FE04.log

│ CINECA_gennaio_FE04_jsessionid.txt

└── marzo

├── FE03

│ CINECA_20160301_FE03.log

│ CINECA_20160302_FE03.log

┊ ⋮

│ CINECA_20160331_FE03.log

│ CINECA_marzo_FE03_jsessionid.txt

└── FE04

CINECA_20160301_FE04.log

CINECA_20160302_FE04.log

CINECA_20160331_FE04.log

CINECA_marzo_FE04_jsessionid.txt

L’analisi di tutti i 244 logfile costituenti il dataset cineca ha prodotto le statistiche globali

riassunte nella tabella seguente. total lines 22,036,131 % su total lines

complete lines 21,694,545 98.45 %

incomplete lines 341,586 1.55 % % su incomplete lines

jsessionid lines 191,933 0.87 % 56.19 %

dropped lines 149,653 0.68 % 43.81 %

A questo punto è possibile importare in R i jsessionid estratti dalle linee incomplete eseguendo le istruzioni seguenti. > cineca_jsessionid <- character( 0 )

> logs <- list.files("apache/" , pattern = "_jsessionid\\ .txt$", full.names = TRUE, recursive =

TRUE)

>

21

Page 24: Analisi degli Accessi al Sistema di Verbalizzazione Esami Online

> for (log in logs) {

+ cineca_jsessionid <- append(

+ cineca_jsessionid,

+ scan(log, what = character())

+ )

+ }

Carenza dati nel periodo 20-30 marzo 2016 L’analisi dei logfile ha evidenziato una carenza di richieste nel periodo compreso dal 20 al 30

marzo 2016. Il grafico a barre seguente illustra la situazione mostrando la percentuale di linee presenti in ciascuna giornata del mese di marzo, calcolata sul numero complessivo di linee presenti nei logfile del mese in esame.

La causa del drastico calo di richieste ipotizzata in sede di analisi - e in seguito confermata dall’Area dei Servizi ICT - è stata il temporaneo dirottamento delle richieste verso un’altra istanza logica Apache, diversa dalle due considerate in questa relazione.

2.2.2. Linee Complete Le informazioni contenute nei logfile prodotti dallo script Bash descritto al sottoparagrafo

precedente possono essere importate in R tramite le istruzioni seguenti. > cineca.dat <- NULL

> logs <- list.files("apache/" , pattern = " \\.log$", full.names = TRUE , recursive = TRUE)

> for (log in logs) {

+ cineca.dat <- rbind(

+ cineca.dat,

+ read.table(log, sep = " ", stringsAsFactors = FALSE )

22

Page 25: Analisi degli Accessi al Sistema di Verbalizzazione Esami Online

+ )

+ }

> cineca.dat$ V4 <- paste(

+ sub( "[", "" , cineca.dat$ V4, fixed = TRUE),

+ sub( "]", "" , cineca.dat$ V5, fixed = TRUE)

+ )

> cineca.dat <- cineca.dat[- c( 2, 3 , 5 , 12)]

> names(cineca.dat) <- c("ip_address" , "timestamp" , "request" , "response_status", "response_size" ,

"http_referer" , "ua_string", "jsessionid")

> cineca.dat$ request[cineca.dat$ request == "-"] <- NA_character_

> cineca.dat$ http_referer[cineca.dat $ http_referer == "-" ] <- NA_character_

> cineca.dat$ jsessionid[cineca.dat $ jsessionid == "-"] <- NA_character_

Al termine dell’importazione e delle operazioni di raffinamento, la struttura del dataframe

cineca.dat dovrebbe essere simile alla seguente. > str(cineca.dat)

'data.frame': 21694545 obs. of 8 variables:

$ ip_address : chr "5.90.78.xyz" "5.90.78.xyz" "5.90.78.xyz" "87.21.23.xyz" ...

$ timestamp : chr "01/Feb/2016:06:29:09 +0100" "01/Feb/2016:06:29:13 +0100" ...

$ request : chr "GET /Guide/PaginaRicercaInse.do HTTP/1.1" ...

$ response_status: int 401 401 401 200 200 200 200 200 200 200 ...

$ response_size : chr "103" "103" "3365" "8329" ...

$ http_referer : chr "https://esse3.units.it/Home.do" "https://dscf.units.it/it/didattica" ...

$ ua_string : chr "Mozilla/5.0 (Linux; Android 5.0; SAMSUNG SM-G900F Build/LRX21T) ..." ...

$ jsessionid : chr NA "3D85ACE08CF5FADB057C4C660AAE0123.esse3-units-prod-06" NA ...

Come si evince dal listato precedente, la variabile jsessionid può risultare not available (NA),

questo capita ad esempio quando il client si autentica tramite smart card; per questo motivo la ricerca dei jsessionid nelle linee complete viene estesa alle variabili request e http referer dove può essere altrettanto presente. > # jsessionid

> cineca_jsessionid <- append(

+ cineca_jsessionid,

+ cineca.dat$ jsessionid[! is.na(cineca.dat $jsessionid)]

+ )

> length <- 52 # jsessionid string length

> # request

> offset <- regexpr(

+ "[0-9A-Z]{32}\\ .esse3-units-prod-0[0-6]{1}" ,

+ cineca.dat$ request[ !is.na(cineca.dat $ request)]

+ )

> cineca_jsessionid <- append(

23

Page 26: Analisi degli Accessi al Sistema di Verbalizzazione Esami Online

+ cineca_jsessionid,

+ substr(

+ cineca.dat $request[ ! is.na(cineca.dat $ request)][offset != - 1],

+ offset[offset != -1 ],

+ offset[offset != -1 ] + length - 1

+ )

+ )

> # http referer

> offset <- regexpr(

+ "[0-9A-Z]{32}\\ .esse3-units-prod-0[0-6]{1}" ,

+ cineca.dat$ http_referer[! is.na(cineca.dat$ http_referer)]

+ )

> cineca_jsessionid <- append(

+ cineca_jsessionid,

+ substr(

+ cineca.dat $http_referer[ !is.na(cineca.dat $ http_referer)][offset != - 1 ],

+ offset[offset != -1 ],

+ offset[offset != -1 ] + length - 1

+ )

+ )

> cineca_jsessionid <- unique(cineca_jsessionid)

> length(cineca_jsessionid)

[ 1 ] 771869

I jsessionid ricavati dalle linee complete vengono aggiunti a quelli precedentemente estratti

dalle linee incomplete e infine si eliminano tutti gli eventuali duplicati, ottenendo così un vettore di 771,869 jsessionid unici estratti dal dataset cineca.

Attacco Shellshock Durante l’importazione delle linee complete si è osservata la presenza di una particolare

richiesta di seguito riportata evidenziando il campo user agent string . 85.25.148.xyz - - [05/Dec/2015:19:11:14 +0100] "GET /hello HTTP/1.0" 404 284 "-" " () { :;}; /bin/bash -c "cd /tmp;lwp-download -a http://188.138.69.229/gnu;curl -O

http://188.138.69.229/gnu;wget http://188.138.69.229/gnu;perl /tmp/gnu*;perl gnu;rm -rf /tmp/gnu*"" - 0

Si tratta di un attacco di tipo shellshock, facilmente riconoscibile per la presenza della

“magic-string” all’inizio del campo evidenziato. Per maggiori informazioni sull’argomento si consiglia la lettura dell’articolo Inside Shellshock: How hackers are using it to exploit systems (di John Graham-Cumming, 30 settembre 2014).

L’osservazione è stata segnalata all’Area dei Servizi ICT, la quale ha preso in carico la questione.

24

Page 27: Analisi degli Accessi al Sistema di Verbalizzazione Esami Online

2.3. Intersezione jsessionid Sessioni / Cineca Si procede estraendo dal dataframe session.dat tutti i jsessionid presenti e rimuovendo gli

eventuali duplicati; si ottiene così un vettore di jsessionid unici estratti dal dataset sessioni. > session_jsessionid <- unique(session.dat$ jsessionid)

> length(session_jsessionid)

[ 1 ] 27490

L’intersezione dei jsessionid estratti dal dataset sessioni con quelli estratti dal dataset cineca,

restituirà dunque tutti (ad eccezione degli eventuali jsessionid andati persi durante l’analisi delle linee incomplete) e soli i jsessionid delle sessioni autenticate al sistema ESSE3 e relative al sistema di verbalizzazione. > tmp <- intersect(session_jsessionid, cineca_jsessionid)

> length(tmp)

[ 1 ] 24670

Viene infine aggiunta la variabile logica verbalization al dataframe session.dat, la quale

assume il valore true per le sessioni autenticate al sistema ESSE3 e relative al sistema di verbalizzazione. > tmp <- intersect(session_jsessionid, cineca_jsessionid)

> tmp <- data.frame(

+ "jsessionid" = tmp,

+ "verbalization" = rep(TRUE, length(tmp)),

+ stringsAsFactors = FALSE

+ )

> session.dat <- merge(session.dat, tmp, all.x = TRUE)

> session.dat$ verbalization[is.na(session.dat$ verbalization)] <- FALSE

Quindi si ricava il numero di sessioni autenticate al sistema ESSE3 e relative al sistema di

verbalizzazione e se ne calcola il valore percentuale sul numero totale di sessioni presenti nel dataframe session.dat. > x <- length(session.dat$ verbalization[session.dat$ verbalization == TRUE])

> x

[ 1 ] 24689

> paste(round(x / nrow(session.dat) * 100 , 2), "%" )

[ 1 ] "89.73 %"

Si traccia infine un grafico a barre (o barplot) per riassumere in maniera visiva il risultato

ottenuto. > tmp <- aggregate(session.dat $verbalization, list (session.dat$ verbalization), "length" )

> tmp <- tmp[order(tmp $ x, decreasing = TRUE ),]

25

Page 28: Analisi degli Accessi al Sistema di Verbalizzazione Esami Online

> tmp <- cbind(tmp, "pct" = round(tmp $x / nrow(session.dat) * 100, 2 ))

> # windows(1024,720)

> par(mar = c( 1.5, . 5 , .5 , . 5 ))

> x <- barplot(tmp$ pct, names.arg = c("verbalization == TRUE" , "verbalization == FALSE"), col =

c( "#2196F3", "#F44336" ), border = 0 , xlim = c(0 , 4 ), ylim = c( 0 , 100 ), axes = FALSE , space = 0.05 ,

mgp = c( 3 , 0.25 , 0 ), xaxs = "i", font = 2 )

> text(x, tmp$ pct, paste(tmp $ pct, "%" ), pos = 3 , cex = 2 )

Si ricorda, dopo averlo puntualizzato nel primo capitolo, cosa si intende per “sessione relativa al sistema di verbalizzazione”; con tale espressioni si indicano tutte le sessioni afferenti al sistema di verbalizzazione e non esclusivamente quelle relative all’effettiva verbalizzazione di un esame; comprese quindi, ad esempio, le sessioni in cui l’utente ha eseguito operazioni quali la consultazione di verbali, oppure l’inserimento o l’aggiornamento di un appello, etc.

Nel prosieguo dell’analisi verranno tenute in considerazione - salvo dove opportunamente segnalato - le sole sessioni autenticate al sistema ESSE3 e relative al sistema di verbalizzazione, alle quali, per comodità e facilità di lettura, ci si riferirà da qui in avanti semplicemente con l’espressione “sessioni di verbalizzazione”.

26

Page 29: Analisi degli Accessi al Sistema di Verbalizzazione Esami Online

27

Page 30: Analisi degli Accessi al Sistema di Verbalizzazione Esami Online

3. Geolocalizzazione

In questo terzo capitolo si cercherà principalmente di rispondere alla domanda “come sono distribuite spazialmente le sessioni di verbalizzazione? ”.

Per dare risposta a questo interrogativo, saranno dapprima convertiti in coordinate geografiche tutti gli indirizzi IP presenti nel dataset sessioni; le posizioni così individuate verranno contrassegnate su una mappa e successivamente il campo di ricerca sarà ristretto alle sole sessioni di verbalizzazione.

Si vedrà come, grazie alla geolocalizzazione, sarà inoltre possibile correggere i fusi orari degli istanti di inizio e di fine sessione; ciò consentirà di calcolare le corrette distribuzioni delle sessioni di verbalizzazione tra fasce orarie giornaliere come quella tra giornate feriali e festive.

28

Page 31: Analisi degli Accessi al Sistema di Verbalizzazione Esami Online

3.1. Da Indirizzi IP a Coordinate Geografiche Per la trasformazione degli indirizzi IP in coordinate geografiche è stato utilizzato il pacchetto

R rgeolocate . Con la serie di istruzioni seguenti si estraggono innanzitutto gli indirizzi IP dal dataframe

session.dat, eliminando gli eventuali duplicati; si ottiene così un vettore di 2,802 indirizzi IP unici. La stringa “xyz”, presente al posto dell’ultimo byte di ciascun indirizzo, viene sostituita con

un byte nullo. La funzione ip_api realizza la traduzione degli indirizzi e restituisce i risultati della conversione sotto forma di dataframe, al quale viene aggiunta la variabile ip_address . Infine le osservazioni vengono disposte in ordine crescente di indirizzo IP. > ip <- unique(session.dat$ ip_address)

> length(ip)

[ 1 ] 2802

> tmp <- sub("xyz", "000", ip, fixed = TRUE )

> library(rgeolocate)

> ip.dat <- ip_api(tmp, delay = TRUE )

> ip.dat <- cbind( "ip_address" = ip, ip.dat, stringsAsFactors = FALSE)

> # order by ip address

> tmp <- strsplit(ip.dat$ ip_address, ".", fixed = TRUE)

> tmp <- unlist(lapply(tmp, function (x) {

+ paste(sapply(x, function (x) {

+ paste(paste(rep( "0", 3 - nchar(x)), collapse = "" ), x, sep = "" )

+ }), collapse = "" )

+ }))

> ip.dat <- ip.dat[order(tmp),]

> rownames(ip.dat) <- 1 :nrow(ip.dat)

Il dataframe ip.dat ottenuto ha dunque la struttura seguente. > str(ip.dat)

'data.frame': 2802 obs. of 14 variables :

$ ip_address : chr "1.4.208.xyz" "2.32.116.xyz" "2.32.125.xyz" "2.32.219.xyz" ...

$ as_code : chr "AS23969 TOT Public Company Limited" "AS30722 VODAFONE-IT-ASN" ...

$ city_name : chr "Bangkok" "Udine" "Venice" "Milan" ...

$ country_name: chr "Thailand" "Italy" "Italy" "Italy" ...

$ country_code: chr "TH" "IT" "IT" "IT" ...

$ isp : chr "TOT" "Vodafone Italia DSL" "Vodafone Italia DSL" "Vodafone Italia DSL" ...

$ latitude : chr "13.7563" "46.065" "45.4371" "45.4667" ...

$ longitude : chr "100.502" "13.2587" "12.3327" "9.2" ...

29

Page 32: Analisi degli Accessi al Sistema di Verbalizzazione Esami Online

$ organisation: chr "TOT" "Vodafone Italia DSL" "Vodafone Italia DSL" "Vodafone Italia DSL" ...

$ region_code : chr "10" "36" "34" "25" ...

$ region_name : chr "Bangkok" "Friuli-Venezia Giulia" "Veneto" "Lombardia" ...

$ timezone : chr "Asia/Bangkok" "Europe/Rome" "Europe/Rome" "Europe/Rome" ...

$ zip_code : chr NA "33100" "30135" "20123" ...

$ status : chr "success" "success" "success" "success" ...

Infine si calcola il numero di posizioni geografiche uniche ricavate. > tmp <- unique(ip.dat[c("latitude" , "longitude")])

> nrow(tmp)

[ 1 ] 672

3.2. Posizione delle Sessioni di Verbalizzazione Le sessioni di verbalizzazione sono distribuite nei paesi del mondo seguenti. > tmp <- merge(

+ session.dat[session.dat$ verbalization == TRUE , c("session_id", "ip_address")],

+ ip.dat[c("ip_address", "country_name" )]

+ )

> tmp <- aggregate(tmp $ session_id, list(tmp $country_name), "length" )

> tmp <- cbind(tmp, paste(format(round(tmp $ x / sum(tmp $ x) * 100 , 3 ), nsmall = 3 ), "%"))

> names(tmp) <- c("country" , "sessions", "sessions pct" )

> tmp

country sessions sessions pct

1 Albania 5 0.020 %

2 Argentina 1 0.004 %

3 Austria 70 0.284 %

4 Belgium 10 0.041 %

5 Belize 1 0.004 %

6 Chile 1 0.004 %

7 Croatia 9 0.036 %

8 Cyprus 3 0.012 %

9 Czech Republic 6 0.024 %

10 Finland 3 0.012 %

11 France 50 0.203 %

12 Germany 22 0.089 %

13 Greece 2 0.008 %

14 Hungary 4 0.016 %

15 India 2 0.008 %

16 Iran 3 0.012 %

17 Italy 24310 98.465 %

18 Japan 15 0.061 %

19 Kosovo 5 0.020 %

20 Netherlands 8 0.032 %

21 Portugal 3 0.012 %

30

Page 33: Analisi degli Accessi al Sistema di Verbalizzazione Esami Online

22 San Marino 1 0.004 %

23 Slovenia 12 0.049 %

24 Spain 13 0.053 %

25 Switzerland 11 0.045 %

26 Thailand 2 0.008 %

27 United Arab Emirates 3 0.012 %

28 United Kingdom 84 0.340 %

29 United States 30 0.122 %

I risultati ottenuti vengono visualizzati su una mappa sfruttando le API di Google Charts.

Per quanto riguarda le 24,310 sessioni di verbalizzazione italiane, esse sono distribuite nelle regioni seguenti. > tmp <- merge(

+ session.dat[session.dat$ verbalization == TRUE , c("session_id", "ip_address")],

+ ip.dat[ip.dat$ country_name == "Italy" , c("ip_address" , "region_name")]

+ )

> tmp <- aggregate(tmp $ session_id, list(tmp $region_name), "length" )

> tmp <- cbind(tmp, paste(format(round(tmp $ x / sum(tmp $ x) * 100 , 3 ), nsmall = 3 ), "%"))

> names(tmp) <- c("region" , "sessions", "sessions pct" )

> tmp

region sessions sessions pct

1 Abruzzo 17 0.070 %

2 Campania 72 0.296 %

3 Emilia-Romagna 270 1.111 %

31

Page 34: Analisi degli Accessi al Sistema di Verbalizzazione Esami Online

4 Friuli-Venezia Giulia 20278 83.414 %

5 Lazio 512 2.106 %

6 Liguria 26 0.107 %

7 Lombardia 652 2.682 %

8 Marche 37 0.152 %

9 Molise 2 0.008 %

10 Piemonte 84 0.346 %

11 Puglia 25 0.103 %

12 Sardegna 56 0.230 %

13 Sicilia 59 0.243 %

14 Toscana 274 1.127 %

15 Trentino-Alto Adige 36 0.148 %

16 Umbria 10 0.041 %

17 Veneto 1900 7.816 %

Anche in questo caso, i risultati vengono ancor meglio apprezzati se visualizzati direttamente

su una mappa.

3.3. Aggiornamento delle Time Zone I valori delle variabili session start e session end all’interno del dataframe session.dat sono

tutti uniformati rispetto al medesimo fuso orario, il Central European Time o CET (UTC+1) / Central European Summer Time o CEST (UTC+2), ossia il fuso orario impostato dal server al momento della registrazione la sessione.

I nuovi dati a disposizione consentono di aggiornare i valori delle variabili di inizio e di fine sessione, “denormalizzando” i fusi orari in base alla posizione geografica della sessione.

32

Page 35: Analisi degli Accessi al Sistema di Verbalizzazione Esami Online

> tmp <- merge(

+ session.dat[c("session_id" , "ip_address", "session_start" , "session_end")],

+ ip.dat[c("ip_address", "timezone" )]

+ )

> tmp <- apply(tmp, 1 , function (x) {

+ c(

+ x[ "session_id"],

+ format(

+ as.POSIXct(x["session_start" ], "%m/%d/%Y %H:%M" , tz = "Europe/Rome"),

+ tz = x[ "timezone"],

+ format = "%Y/%m/%d %H:%M"

+ ),

+ format(

+ as.POSIXct(x["session_end"], "%m/%d/%Y %H:%M", tz = "Europe/Rome"),

+ tz = x[ "timezone"],

+ format = "%Y/%m/%d %H:%M"

+ )

+ )

+ })

> tmp <- t(tmp)

> tmp <- as.data.frame(tmp, stringsAsFactors = FALSE)

> str(tmp)

'data.frame': 27514 obs. of 3 variables :

$ session_id : chr "nCSlagACJpWBKjiTQwDn" "0s8qrpI63SiWEeVPPjby" "raBDAjQvZ9EyxDOTnDlX" ...

$ session_start : chr "2016/01/03 17:13" "2016/02/14 15:35" "2016/02/24 10:13" "2016/02/11 09:57"

...

$ session_end : chr "2016/01/03 17:17" "2016/02/14 16:05" "2016/02/24 10:45" "2016/02/11 10:02"

...

session.dat <- merge(

session.dat[setdiff(names(session.dat), c("session_start" , "session_end"))],

tmp

)

Al termine delle istruzioni precedenti, la struttura del dataframe session.dat dovrebbe essere

simile alla seguente, dove il valore delle variabili session start e session end è stato corretto. > str(session.dat)

'data.frame': 27514 obs. of 9 variables :

$ session_id : chr "00IvNTlsKJdSSy72ekW6" "00jSYWnHLp2vIbT18Yej" "00NDik575H6ge5b9upUI" ...

$ jsessionid : chr "6982D50C88356887DC375C9FCDCD278E.esse3-units-prod-03" ...

$ ip_address : chr "140.105.162.xyz" "140.105.251.xyz" "82.58.203.xyz" "140.105.224.xyz" ...

$ user_id : int 114926 110414 115157 112099 110286 113107 110725 110714 111515 111967 ...

$ ua_id : int 9257 13 8883 5224 14 505 6462 8105 8099 6582 ...

33

Page 36: Analisi degli Accessi al Sistema di Verbalizzazione Esami Online

$ ua_string : chr "Mozilla/5.0 (Windows NT 6.1§ WOW64) AppleWebKit/537.36 ..." ...

$ verbalization : logi TRUE TRUE TRUE TRUE TRUE TRUE ...

$ session_start : chr "2016/02/03 13:21" "2016/02/01 16:45" "2016/02/25 20:43" "2016/02/09 19:08"

...

$ session_end : chr "2016/02/03 13:27" "2016/02/01 17:58" "2016/02/25 22:03" "2016/02/09 19:38"

...

3.3.1. Fasce Orarie Si può ora procedere al calcolo della distribuzione delle sessioni di verbalizzazione nelle fasce

orarie giornaliere. > time_bands <- rep(0 , 24)

> names(time_bands) <- c(

+ paste( "0", 0 :9 , ":00-0" , 0 :9 , ":59", sep = "" ),

+ paste( 10: 23 , ":00-" , 10 : 23 , ":59" , sep = "" )

+ )

> tmp <- session.dat[session.dat$ verbalization == TRUE, c("session_start" , "session_end")]

> tmp <- apply(tmp, 1 , function (x) { paste(as.numeric(substr(x, 12 , 13)), collapse = " " ) })

> for (x in tmp) {

+ x <- as.numeric(unlist(strsplit(x, " " , fixed = TRUE )))

+ if (x[ 1] == x[2 ])

+ time_bands[x[1 ] + 1 ] <- time_bands[x[ 1] + 1 ] + 1

+ else if (x[ 1 ] < x[ 2 ])

+ time_bands[x[1 ] : x[2 ] + 1 ] <- time_bands[x[ 1 ] :x[ 2 ] + 1 ] + 1

+ else # x[1] > x[2]

+ time_bands[c(x[1 ] :23 , 0 : x[2 ]) + 1 ] <- time_bands[c(x[1 ] : 23 , 0 : x[ 2]) + 1 ] + 1

+ }

> data.frame(

+ "sessions" = time_bands,

+ "sessions_pct" = paste(format(round(time_bands / length(tmp) * 100 , 2 ), nsmall = 2 ), "%")

+ )

sessions sessions pct

00:00-00:59 238 0.96 %

01:00-01:59 54 0.22 %

02:00-02:59 20 0.08 %

03:00-03:59 13 0.05 %

04:00-04:59 17 0.07 %

05:00-05:59 16 0.06 %

06:00-06:59 96 0.39 %

07:00-07:59 377 1.53 %

08:00-08:59 1533 6.21 %

09:00-09:59 3145 12.74 %

10:00-10:59 3865 15.65 %

11:00-11:59 3868 15.67 %

34

Page 37: Analisi degli Accessi al Sistema di Verbalizzazione Esami Online

12:00-12:59 3464 14.03 %

13:00-13:59 2796 11.32 %

14:00-14:59 2543 10.30 %

15:00-15:59 2822 11.43 %

16:00-16:59 2839 11.50 %

17:00-17:59 2529 10.24 %

18:00-18:59 2045 8.28 %

19:00-19:59 1341 5.43 %

20:00-20:59 856 3.47 %

21:00-21:59 754 3.05 %

22:00-22:59 655 2.65 %

23:00-23:59 513 2.08 %

Infine i risultati ottenuti vengono visualizzati tramite un barplot. > tmp <- data.frame(

+ "sessions" = time_bands,

+ "pct" = round(time_bands / length(tmp) * 100 , 2)

+ )

> par(mar = c( 1.1, 2.6 , .5 , . 5 ))

> barplot(tmp$ pct, names.arg = substr(rownames(tmp), 1 , 5 ), col = "#00FFFF" , border = "#00FFFF" ,

ylim = c( 0 , 16 ), axes = FALSE , mgp = c(3 , . 1, 0 ), xaxs = "i", cex.names = 1 , family = "roboto" ,

col.axis = "#595959" , panel.first = grid(0 , 32 , col = "#EEEEEE" , lty = "solid" ), xpd = FALSE, width

= rep(1 , 24), space = . 1)

> axis(2 , yaxp = c(0 , 16, 16 ), line = 0.25 , cex.axis = 1 , lwd = .5 , family = "roboto", col =

"#595959" , col.ticks = "#595959", col.axis = "#595959" , las = 2 , tck = - 0.01 , adj = 0, hadj = . 75 ,

labels = paste( 0 :16 , "%", sep = "" ), at = 0 :16 )

35

Page 38: Analisi degli Accessi al Sistema di Verbalizzazione Esami Online

3.3.2. Giornate Feriali / Festive In questo sottoparagrafo si distingueranno le sessioni eseguite durante le giornate feriali da

quelle svolte durante le giornate festive. Innanzitutto viene aggiunta la variabile logica holiday al dataframe session.dat, la quale

assumerà il valore true se la sessione si trova in almeno uno dei seguenti casi.

○ La sessione si è svolta (anche parzialmente) durante un sabato.

○ La sessione si è svolta (anche parzialmente) durante una domenica.

○ La sessione proviene dall’Italia e si è svolta (anche parzialmente) durante una delle seguenti giornate.

08/12/2015 Immaculate Conception 25/12/2015 Christmas Day 26/12/2015 St. Stephen's Day 01/01/2016 New Year's Day 06/01/2016 Epiphany 27/03/2016 Easter Day 28/03/2016 Easter Monday

Se la sessione non dovesse ricadere in nessuno dei casi precedenti, allora la variabile holiday assumerà il valore false . > holidays <-

c( "08/12/2015", "25/12/2015", "26/12/2015", "01/01/2016", "06/01/2016", "27/03/2016", "28/03/2016")

> names(holidays) <- c( "Immaculate Conception", "Christmas Day" , "St. Stephen's Day" , "New Year's

Day", "Epiphany" , "Easter Day" , "Easter Monday" )

> tmp <- merge(

+ session.dat[c("session_id" , "ip_address", "session_start" , "session_end")],

+ ip.dat[c("ip_address", "country_name" , "timezone")]

+ )

> tmp <- apply(tmp, 1 , function (x) {

+ flag <- FALSE

+

+ # saturday

+ if (

+ format(as.POSIXct(x["session_start" ], tz = x["timezone" ]), "%u" ) == 6

+ | format(as.POSIXct(x[ "session_end"], tz = x["timezone" ]), "%u" ) == 6

+ )

+ flag <- TRUE

+

+ # sunday

+ if (

36

Page 39: Analisi degli Accessi al Sistema di Verbalizzazione Esami Online

+ format(as.POSIXct(x["session_start" ], tz = x["timezone" ]), "%u" ) == 7

+ | format(as.POSIXct(x[ "session_end"], tz = x["timezone" ]), "%u" ) == 7

+ )

+ flag <- TRUE

+

+ # italian holiday

+ if (

+ x[ "country_name" ] == "Italy" & (

+ any(format(as.POSIXct(x["session_start" ], tz = x[ "timezone"]), "%d/%m/%Y" ) == holidays)

+ | any(format(as.POSIXct(x["session_end"], tz = x[ "timezone"]), "%d/%m/%Y" ) == holidays)

+ )

+ )

+ flag <- TRUE

+

+ c(x[ "session_id"], flag)

+ })

> tmp <- t(tmp)

> tmp <- data.frame(

+ "session_id" = tmp[,1 ],

+ "holiday" = as.logical(tmp[,2 ]), stringsAsFactors = FALSE

+ )

> session.dat <- merge(session.dat, tmp)

Al termine delle istruzioni precedenti, la struttura del dataframe session.dat dovrebbe essere

simile alla seguente. > str(session.dat)

'data.frame': 27514 obs. of 10 variables :

$ session_id : chr "00IvNTlsKJdSSy72ekW6" "00jSYWnHLp2vIbT18Yej" "00NDik575H6ge5b9upUI" ...

$ jsessionid : chr "6982D50C88356887DC375C9FCDCD278E.esse3-units-prod-03" ...

$ ip_address : chr "140.105.162.xyz" "140.105.251.xyz" "82.58.203.xyz" "140.105.224.xyz" ...

$ user_id : int 114926 110414 115157 112099 110286 113107 110725 110714 111515 111967 ...

$ ua_id : int 9257 13 8883 5224 14 505 6462 8105 8099 6582 ...

$ ua_string : chr "Mozilla/5.0 (Windows NT 6.1§ WOW64) AppleWebKit/537.36 ..." ...

$ verbalization : logi TRUE TRUE TRUE TRUE TRUE TRUE ...

$ session_start : chr "2016/02/03 13:21" "2016/02/01 16:45" "2016/02/25 20:43" "2016/02/09 19:08"

...

$ session_end : chr "2016/02/03 13:27" "2016/02/01 17:58" "2016/02/25 22:03" "2016/02/09 19:38"

...

$ holiday : logi FALSE FALSE FALSE FALSE FALSE FALSE ...

Si procede ora al calcolo della distribuzione delle sessioni di verbalizzazione tra giornate

feriali e festive. > tmp <- session.dat[session.dat$ verbalization == TRUE, "holiday"]

> tmp <- aggregate(tmp, list (tmp), "length" )

37

Page 40: Analisi degli Accessi al Sistema di Verbalizzazione Esami Online

>

> data.frame(

+ "holiday" = tmp[,1 ],

+ "sessions" = tmp[,2 ],

+ "sessions_pct" = paste(round(tmp $ x / sum(tmp $ x) * 100 , 2 ), "%")

+ )

holiday sessions sessions_pct

1 FALSE 22241 90.08 %

2 TRUE 2448 9.92 %

Infine i risultati ottenuti vengono visualizzati tramite un barplot. > tmp <- cbind(tmp, round(tmp $ x / sum(tmp $ x) * 100 , 2 ))

> names(tmp) <- c("holiday" , "sessions", "pct")

> par(mar = c( 2.5, . 5 , .5 , . 5 ))

> x <- barplot(tmp$ pct, names.arg = c("holiday == FALSE" , "holiday == TRUE" ), col = c("#CCCCCC" ,

"#FFFF00" ), border = 0 , xlim = c(0 , 4 ), ylim = c( 0 , 100 ), axes = FALSE , space = 0.05 , mgp = c( 3 , 1,

0 ), xaxs = "i" , cex.names = 1.8, family = "roboto" , col.axis = "#595959")

> text(x, tmp$ pct, paste(tmp $ pct, "%" ), pos = 3 , cex = 2 , family = "roboto" , col = "#595959")

38

Page 41: Analisi degli Accessi al Sistema di Verbalizzazione Esami Online

39

Page 42: Analisi degli Accessi al Sistema di Verbalizzazione Esami Online

4. User Agent

Lo studio degli user agent è stato basato sull’analisi delle user agent string (UA string), compito, quest’ultimo, che può risultare alquanto spinoso. Infatti, pur essendo presente una certa uniformità tra le stringhe prodotte dai vari agent (tipicamente un browser), resta tuttavia un’impresa ardua riconoscere ed interpretare correttamente ogni singolo token presente in un insieme eterogeneo di UA string.

In rete sono disponibili diversi servizi gratuiti che tentano di automatizzare il processo di scomposizione in token di una UA string, nessuno dei quali tuttavia risulta esente da difetti. Per l’analisi presentata in questo capitolo verranno considerati tre diversi servizi disponibili in rete e si procederà ad un confronto diretto dei risultati ottenuti con le UA string di partenza; le informazioni così selezionate costituiranno un nuovo dataset.

40

Page 43: Analisi degli Accessi al Sistema di Verbalizzazione Esami Online

4.1. Scomposizione delle UA string in Token Innanzitutto si estraggono dal dataframe session.dat la coppia di variabili ua id e ua string ,

eliminando tutti gli eventuali duplicati e i valori not available (NA). > tmp <- unique(session.dat[c( "ua_id" , "ua_string" )])

> tmp <- tmp[! is.na(tmp $ua_id),]

> tmp <- tmp[order(tmp $ ua_id),]

> tmp$ ua_string <- gsub("§" , ";", tmp $ua_string, fixed = TRUE)

> str(tmp)

'data.frame': 917 obs. of 2 variables:

$ ua_id : int 7 8 12 13 14 15 22 23 28 32 ...

$ ua_string: chr "Mozilla/5.0 (iPhone; CPU iPhone OS 7_1 like Mac OS X) ..." ...

Gli user agent distinti da analizzare sono quindi 917.

4.1.1. Parsing: Primo Metodo I primi due servizi scelti operano in modo simile; entrambi offrono una API molto semplice,

dove la UA string viene trasmessa tramite una richiesta HTTP, la cui risposta viene fornita sotto forma di JSON. Segue un esempio esplicativo dell’applicazione di entrambi i servizi.

1° SERVIZIO 2° SERVIZIO

useragentapi.com useragentstring.com

FORMATO RICHIESTA FORMATO RICHIESTA

https://useragentapi.com/api/v3/json/ APIKEY/USERAGENT

http://www.useragentstring.com/?uas= USERAGENT&getJSON=all

ESEMPIO UA STRING

Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.116 Safari/537.36

RICHIESTA RICHIESTA

https://useragentapi.com/api/v3/json/ APIKEY/Mozilla%2F5.0+%28Windows+NT+10.0%3B+Win64%3B+x64%29+AppleWebKit%2F537.36+%28KHTML%2C+like+Gecko%29+Chrome%2F53.0.2785.116+Safari%2F537.36

http://www.useragentstring.com/?uas=Mozilla%2F5.0+%28Windows+NT+10.0%3B+Win64%3B+x64%29+AppleWebKit%2F537.36+%28KHTML%2C+like+Gecko%29+Chrome%2F53.0.2785.116+Safari%2F537.36&getJSON=all

RISPOSTA RISPOSTA

{ "data": { "platform_name": "Windows 10", "platform_version": "Windows NT 10.0", "platform_type": "Desktop",

{ "agent_type": "Browser", "agent_name": "Chrome", "agent_version": "53.0.2785.116", "os_type": "Windows",

41

Page 44: Analisi degli Accessi al Sistema di Verbalizzazione Esami Online

"browser_name": "Chrome", "browser_version": "53.0.2785.116", "engine_name": "WebKit", "engine_version": "537.36" } }

"os_name": "Windows 10", "os_versionName": "", "os_versionNumber": "", "os_producer": "", "os_producerURL": "", "linux_distibution": "Null", "agent_language": "", "agent_languageTag": "" }

Le coppie (richiesta, risposta) HTTP possono essere gestite direttamente in R con le seguenti

istruzioni. > # 1st service: useragentapi.com

> apikey <- # ...

> request <- paste(

+ "https://useragentapi.com/api/v3/json/" ,

+ apikey, "/" ,

+ sapply(tmp$ ua_string, "URLencode" , reserved = TRUE),

+ sep = ""

+ )

> tmp <- cbind(tmp, request)

> ua_1 <- apply(head(tmp), 1 , function (x) { c( x[ "ua_id" ], unlist(content(GET(x["request" ]))) ) })

> ua_1 <- t(ua_1)

> ua_1 <- as.data.frame(ua_1, stringsAsFactors = FALSE)

> ua_1$ ua_id <- as.numeric(ua_1$ ua_id)

> str(ua_1)

'data.frame': 6 obs. of 8 variables:

$ ua_id : num 7 8 12 13 14 15

$ data.platform_name : chr "Apple iPhone" "Nokia" "Windows 7" "Windows" ...

$ data.platform_version: chr "7" "630" "Windows NT 6.1" "7" ...

$ data.platform_type : chr "Mobile" "Mobile" "Desktop" "Desktop" ...

$ data.browser_name : chr "Safari" "Internet Explorer" "Firefox" "Internet Explorer" ...

$ data.browser_version : chr "9537.53" "11.0" "40.0" "11.0" ...

$ data.engine_name : chr "WebKit" "WebKit" "Gecko" "Gecko" ...

$ data.engine_version : chr "537.51.2" "537" "40.0" "11.0" ...

> # 2nd service: useragentstring.com

> tmp <- tmp[-3 ]

> request <- paste(

+ "http://www.useragentstring.com/?uas=" ,

+ sapply(tmp$ ua_string, "URLencode" , reserved = TRUE),

+ "&getJSON=all" ,

+ sep = ""

+ )

42

Page 45: Analisi degli Accessi al Sistema di Verbalizzazione Esami Online

> tmp <- cbind(tmp, request)

> ua_2 <- apply(head(tmp), 1 , function (x) { c( x[ "ua_id" ], unlist(content(GET(x["request" ]))) ) })

> ua_2 <- t(ua_2)

> ua_2 <- as.data.frame(ua_2, stringsAsFactors = FALSE)

> ua_2$ ua_id <- as.numeric(ua_2$ ua_id)

> str(ua_2)

'data.frame': 6 obs. of 13 variables:

$ ua_id : num 7 8 12 13 14 15

$ agent_type : chr "Browser" "Browser" "Browser" "Browser" ...

$ agent_name : chr "Safari" "IE Mobile" "Firefox" "Internet Explorer" ...

$ agent_version : chr "7.0" "11.0" "40.0" "11.0" ...

$ os_type : chr "Macintosh" "Android" "Windows" "Windows" ...

$ os_name : chr "iPhone OS" "Android" "Windows 7" "Windows 7" ...

$ os_versionName : chr "" "" "" "" ...

$ os_versionNumber : chr "7_1" "4.0" "" "" ...

$ os_producer : chr "" "" "" "" ...

$ os_producerURL : chr "" "" "" "" ...

$ linux_distibution: chr "Null" "Null" "Null" "Null" ...

$ agent_language : chr "" "Ossetian" "" "" ...

$ agent_languageTag: chr "" "OS" "" "" ...

4.1.2. Parsing: Secondo Metodo Per il terzo parsing delle UA string è stato realizzato un semplice script (in JavaScript) che

sfrutta le API messe a disposizione dal servizio github.com/3rd-Eden/useragent . // parser.js

var loader = require( 'csv-load-sync' );

var useragent = require('useragent' );

useragent( true);

function split(line) {

return line.split( ' \t ');

}

var results = loader( 'ua.txt' , {

getColumns: split

});

console.log( "%s \t%s \t %s \t%s \t %s\t %s \t %s\t %s \t%s \t %s",

"ua_id" ,

"device_family",

"device_version",

"device_string",

"agent_family" ,

43

Page 46: Analisi degli Accessi al Sistema di Verbalizzazione Esami Online

"agent_version",

"agent_string" ,

"os_family",

"os_version",

"os_string"

);

results.forEach( function (obj) {

var ua = useragent.parse(obj.ua_string);

console.log( "%s\t %s \t %s\t %s \t%s \t %s \t%s \t %s\t %s \t%s" ,

obj.ua_id,

ua.device.family,

ua.device.toVersion(),

ua.device. toString (),

ua.family,

ua.toVersion(),

ua.toAgent(),

ua.os.family,

ua.os.toVersion(),

ua.os. toString()

);

});

Innanzitutto le UA string vengono esportate da R tramite l’istruzione seguente. > write.table(tmp[1 : 2 ], "ua.txt" , sep = " \t ", row.names = FALSE , quote = FALSE)

Lo script viene eseguito da una shell Bash. $ node parser.js > ua_3.txt

Infine i risultati ottenuti vengono importanti in R. # 3rd service: github.com/3rd-Eden/useragent

ua_3 <- read.table("ua_3.txt" , sep = "\t " , header = TRUE , stringsAsFactors = FALSE)

4.2. UA string: dalla Versione “fat” alla “slim” Per agevolare il confronto tra i valori restituiti dai servizi di parsing illustrati al paragrafo

precedente, i risultati ottenuti vengono affiancati in un unico dataframe, il quale viene esportato da R per consentirne la visualizzazione mediante un software per la gestione di fogli di calcolo oppure per la stampa cartacea. > names(ua_1)[-1 ] <- sub("data." , "ua_1." , names(ua_1)[ -1 ], fixed = TRUE)

> names(ua_2)[-1 ] <- paste( "ua_2." , names(ua_2)[ -1 ], sep = "" )

> names(ua_3)[-1 ] <- paste( "ua_3." , names(ua_3)[ -1 ], sep = "" )

44

Page 47: Analisi degli Accessi al Sistema di Verbalizzazione Esami Online

> tmp <- cbind(ua_1, ua_2[-1 ], ua_3[ -1])

> write.table(tmp, "ua.txt" , sep = " \t" , row.names = FALSE, quote = FALSE)

L’analisi è proseguita mediante la redazione di uno spreadsheet in cui ciascuna riga

rappresenta uno user agent e le colonne compilate ospitano i valori seguenti.

device type Tipo del dispositivo; la variabile può assumere uno dei seguenti valori: desktop | desktop tablet | mobile | tablet .

device name Produttore e modello del dispositivo; valore presente esclusivamente per alcuni dispositivi del tipo mobile e tablet .

agent family Nome del browser; più in generale nome dell’applicazione client responsabile della sessione.

agent version Versione del browser ridotta alla forma major_number.minor_number ; più in generale versione dell’applicazione client responsabile della sessione.

os family Nome del sistema operativo.

os version Versione del sistema operativo ridotta alla forma major_number.minor_number .

os name Nome commerciale della versione del sistema operativo oppure nome della distribuzione nel caso di sistemi Linux.

Nella compilazione della tabella si sono adottate le seguenti convenzioni per decretare il tipo di dispositivo e la versione del browser / sistema operativo.

○ Al campo device type viene assegnato il valore desktop tablet ogniqualvolta nella UA string

analizzata è presente almeno uno dei token “Touch” e “Tablet PC”.

45

Page 48: Analisi degli Accessi al Sistema di Verbalizzazione Esami Online

○ Le versioni del browser e del sistema operativo sono state troncate, preservando soltanto i primi due numeri denominati rispettivamente major_number e minor_number .

Lo spreadsheet costruito, dopo essere stato convertito in formato testo (scegliendo il carattere

di tabulazione come separatore tra i campi), viene importato in R. Per ciascuna osservazione viene generata una UA string compatta concatenando i valori delle

variabili definite precedentemente. La versione “slim” della UA string assumerà perciò in generale la forma seguente.

device_type (device_name) agent_family agent_version os_family os_version (os_name)

Segue un esempio del passaggio dalla forma “fat” a quella “slim” per una UA string.

UA string “fat”

UA string “slim”

Mozilla/5.0 (Linux; Android 6.0.1; Nexus 5 Build/MMB29S) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/47.0.2526.83 Mobile Safari/537.36

mobile (LG Nexus 5) Chrome 47.0 Android 6.0

(Marshmallow)

Le informazioni sugli user agent vengono quindi distribuite su due dataframe distinti,

contenenti rispettivamente la versione fat e slim delle UA string. > ua_fat.dat <- read.table("ua.txt" , sep = "\t " , header = TRUE , na.strings = "", colClasses =

c( "integer", rep("character" , 8)))

> # ua string slim

> tmp <- ua_fat.dat

> tmp$ device_name[! is.na(tmp $ device_name)] <- paste(

+ "(" , tmp$ device_name[! is.na(tmp $ device_name)], ")" , sep = ""

+ )

> tmp$ os_name[ ! is.na(tmp$ os_name)] <- paste("(" , tmp$ os_name[ ! is.na(tmp$ os_name)], ")", sep = "" )

> tmp <- apply(tmp, 1 , function (x) {

+ c( x[ "ua_fat_id"], "ua_slim_string" = paste(x[ 3: 9 ], collapse = " " ) )

+ })

> tmp <- t(tmp)

> tmp <- as.data.frame(tmp, stringsAsFactors = FALSE)

> tmp$ ua_fat_id <- as.integer(tmp $ ua_fat_id)

> tmp$ ua_slim_string <- gsub(" NA", "", tmp $ua_slim_string, fixed = TRUE)

> ua_fat.dat <- merge(ua_fat.dat, tmp)

> ua_slim.dat <- unique(ua_fat.dat[3 : 10])

> ua_slim.dat <- cbind( "ua_slim_id" = 1 : nrow(ua_slim.dat), ua_slim.dat)

> rownames(ua_slim.dat) <- 1 : nrow(ua_slim.dat)

> ua_fat.dat <- merge(

+ ua_fat.dat[c("ua_fat_id" , "ua_fat_string", "ua_slim_string" )],

+ ua_slim.dat[c("ua_slim_id" , "ua_slim_string" )]

+ )

46

Page 49: Analisi degli Accessi al Sistema di Verbalizzazione Esami Online

> ua_fat.dat <- ua_fat.dat[order(ua_fat.dat $ua_fat_id), -1]

> rownames(ua_fat.dat) <- 1 : nrow(ua_fat.dat)

La struttura dei due dataframe definiti nelle istruzioni precedenti risulta la seguente. > str(ua_fat.dat)

'data.frame': 917 obs. of 3 variables:

$ ua_fat_id : int 7 8 12 13 14 15 22 23 28 32 ...

$ ua_fat_string : chr "Mozilla/5.0 (iPhone; CPU iPhone OS 7_1 like Mac OS X) ..." ...

$ ua_slim_id : int 1 2 3 4 5 6 7 8 9 10 ...

> str(ua_slim.dat)

'data.frame': 392 obs. of 9 variables:

$ ua_slim_id : int 1 2 3 4 5 6 7 8 9 10 ...

$ device_type : chr "mobile" "mobile" "desktop" "desktop" ...

$ device_name : chr "Apple iPhone" "Nokia Lumia 630" NA NA ...

$ agent_family : chr "Safari" "Internet Explorer" "Firefox" "Internet Explorer" ...

$ agent_version : chr "7.0" "11.0" "40.0" "11.0" ...

$ os_family : chr "iOS" "Windows Phone" "Windows NT" "Windows NT" ...

$ os_version : chr "7.1" "8.1" "6.1" "6.1" ...

$ os_name : chr NA NA "Windows 7" "Windows 7" ...

$ ua_slim_string: chr "mobile (Apple iPhone) Safari 7.0 iOS 7.1" ...

Si hanno in definitiva 392 UA string slim distinte. Da qui in avanti saranno prese in considerazione esclusivamente le UA string slim; per tale motivo si provvede ad aggiornare il dataframe session.dat sostituendo i riferimenti agli user agent nella versione fat con quelli nella versione slim. tmp <- merge(

session.dat[c( "session_id", "ua_id" )],

ua_fat.dat[c( "ua_fat_id", "ua_slim_id")],

by.x = "ua_id" , by.y = "ua_fat_id"

)

session.dat <- merge(

session.dat[setdiff(names(session.dat), c("ua_id", "ua_string" ))],

tmp[-1 ],

all.x = TRUE

)

> str(session.dat)

'data.frame': 27514 obs. of 9 variables :

$ session_id : chr "00IvNTlsKJdSSy72ekW6" "00jSYWnHLp2vIbT18Yej" "00NDik575H6ge5b9upUI" ...

$ jsessionid : chr "6982D50C88356887DC375C9FCDCD278E.esse3-units-prod-03" ...

$ ip_address : chr "140.105.162.xyz" "140.105.251.xyz" "82.58.203.xyz" "140.105.224.xyz" ...

$ user_id : int 114926 110414 115157 112099 110286 113107 110725 110714 111515 111967 ...

$ verbalization : logi TRUE TRUE TRUE TRUE TRUE TRUE ...

$ session_start : chr "2016/02/03 13:21" "2016/02/01 16:45" "2016/02/25 20:43" "2016/02/09 19:08"

...

$ session_end : chr "2016/02/03 13:27" "2016/02/01 17:58" "2016/02/25 22:03" "2016/02/09 19:38"

47

Page 50: Analisi degli Accessi al Sistema di Verbalizzazione Esami Online

...

$ holiday : logi FALSE FALSE FALSE FALSE FALSE FALSE ...

$ ua_slim_id : int 308 4 101 191 5 70 207 267 234 216 ...

4.3. Sessioni di Verbalizzazione: Desktop / Mobile A conclusione di questo capitolo, si cerca una risposta alla domanda “qual è la percentuale di

sessioni di verbalizzazione eseguite da dispositivi mobile? ”. > tmp <- merge(session.dat[session.dat$ verbalization == TRUE, ], ua_slim.dat, all.x = TRUE )

> tmp <- tmp$ device_type

> tmp <- ifelse(is.na(tmp), "NA", ifelse(tmp == "mobile" | tmp == "tablet" , "mobile" , "desktop" ))

> tmp <- aggregate(tmp, by = list(tmp), "length" )

> data.frame(

+ "device" = tmp[,1 ],

+ "sessions" = tmp[,2 ],

+ "sessions_pct" = paste(round(tmp $ x / sum(tmp $ x) * 100 , 2 ), "%")

+ )

device sessions sessions_pct

1 desktop 24138 97.77 %

2 mobile 549 2.22 %

3 NA 2 0.01 %

Si osserva dunque una percentuale di sessioni di verbalizzazione provenienti da dispositivi

mobile molto bassa e inoltre la presenza di due osservazioni in cui lo user agent risulta not available . Infine si visualizzano i risultati ottenuti mediante un grafico a barre. > tmp <- cbind(tmp, round(tmp $ x / sum(tmp $ x) * 100 , 2 ))

> names(tmp) <- c("device" , "sessions", "pct")

> par(mar = c( 2.5, . 5 , 1, . 5 ))

> x <- barplot(tmp$ pct, names.arg = c("desktop" , "mobile" , "NA" ), col = c( "#00FFFF" , "#FF00FF" ,

"#595959" ), border = NA , xlim = c( 0 , 4), ylim = c( 0, 100 ), axes = FALSE, space = 0.05, mgp = c( 3 ,

1 , 0), xaxs = "i", cex.names = 1.8 , family = "roboto" , col.axis = "#595959" )

> text(x, tmp$ pct, paste(tmp $ pct, "%" ), pos = 3 , cex = 2 , family = "roboto" , col = "#595959", xpd =

TRUE)

48

Page 51: Analisi degli Accessi al Sistema di Verbalizzazione Esami Online

49

Page 52: Analisi degli Accessi al Sistema di Verbalizzazione Esami Online

5. Statistiche Utenti

Questo capitolo è dedicato allo studio degli utenti responsabili delle sessioni autenticate al sistema ESSE3 e con esso si conclude la parte della relazione dedicata all’analisi dei dati.

Gli utenti verranno dapprima aggregati in base ad una caratteristica comunque, quale il ruolo, il dipartimento o il valore del flag structured , calcolando in particolare la distribuzione delle sessioni di verbalizzazione tra le diverse classi.

Si passerà poi all’analisi individuale degli utenti, definendo diversi parametri e ricavando infine delle statistiche complessive.

50

Page 53: Analisi degli Accessi al Sistema di Verbalizzazione Esami Online

5.1. Statistiche Aggregate Nel primo paragrafo verranno analizzati gli utenti aggregati in base al ruolo, in base al

dipartimento e in base al valore del flag structured . Per ciascuna categoria verranno calcolati il numero di utenti, il numero di sessioni e il numero di sessioni di verbalizzazione.

5.1.1. Ruoli Nel dataset user.dat sono presenti i 18 ruoli seguenti. > role.dat <- unique(user.dat[order(user.dat$ role_id), c( "role_id", "role_string")])

> rownames(role.dat) <- 1 : nrow(role.dat)

> user.dat <- user.dat[setdiff(names(user.dat), "role_string")]

> role.dat

role_id role_string

1 1 Professore Ordinario

2 2 Co.Co.Co.

3 3 Professore Associato

4 4 Cat. EP - Area Tecnica, Tecnico-Scientifica ed Elaborazione Dati

5 5 Ricercatore Universitario

6 6 Cat. D - Area Socio-Sanitaria

7 7 Collaboratore Esperto Linguistico

8 8 Cat. D - Area Tecnica, Tecnico-Scientifica ed Elaborazione Dati

9 9 Cat. C - Area Tecnica, Tecnico-Scientifica ed Elaborazione Dati

10 10 Cat. EP - Area Medico-Odontoiatrica e Socio-Sanitaria

11 11 Libero Professionista

12 12 Assegnista di Ricerca

13 13 Ricercatore a Tempo Determinato

14 14 Personale Esterno

15 15 Cat. D - Area Amministrativa-Gestionale

16 16 Cat. C - Area Amministrativa

17 17 Dottorato di Ricerca

18 18 Lavoratore Autonomo

Si vuole calcolare il numero di utenti, il numero di sessioni e il numero di sessioni di

verbalizzazione per ciascun ruolo. > tmp <- merge(unique(session.dat["user_id" ]), user.dat, all.x = TRUE )

> tmp <- tmp$ role_id

> tmp[is.na(tmp)] <- - 1

> roles <- aggregate(tmp, list ("role" = tmp), "length" )

> tmp <- merge(session.dat, user.dat, all.x = TRUE )

> tmp <- tmp[c( "verbalization" , "role_id" )]

> tmp$ role_id[is.na(tmp $role_id)] <- -1

51

Page 54: Analisi degli Accessi al Sistema di Verbalizzazione Esami Online

> roles <- merge(

+ roles,

+ aggregate(tmp$ role_id, list( "role" = tmp$ role_id), "length" ),

+ by.x = "role", by.y = "role",

+ all.x = TRUE

+ )

> roles <- merge(

+ roles,

+ aggregate(

+ tmp $ role_id[tmp$ verbalization == TRUE ],

+ list ("role" = tmp $role_id[tmp $ verbalization == TRUE ]),

+ "length"

+ ),

+ by.x = "role", by.y = "role",

+ all.x = TRUE

+ )

> names(roles)[ - 1] <- c("users" , "sessions" , "verbalizations" )

> roles$ role[roles$ role == - 1 ] <- NA

> roles <- roles[order(roles$ role),]

> rownames(roles) <- 1 : nrow(roles)

> data.frame(

+ roles[ "role"],

+ roles[ "users"],

+ "users_pct" = paste(round(roles $ users / sum(roles$ users) * 100, 2 ), "%" ),

+ roles[ "sessions"],

+ "sessions_pct" = paste(round(roles$ sessions / sum(roles$ sessions) * 100 , 3), "%" ),

+ roles[ "verbalizations"],

+ "verbalizations_pct" = paste(round(roles$ verbalizations / sum(roles$ verbalizations) * 100,

3 ), "%")

+ )

role users users_pct sessions sessions_pct verbalizations verbalizations_pct

1 1 159 14.64 % 6070 22.061 % 5457 22.103 %

2 2 355 32.69 % 4768 17.329 % 4338 17.571 %

3 3 259 23.85 % 9173 33.339 % 8149 33.007 %

4 4 1 0.09 % 21 0.076 % 21 0.085 %

5 5 208 19.15 % 6173 22.436 % 5535 22.419 %

6 6 2 0.18 % 16 0.058 % 14 0.057 %

7 7 14 1.29 % 271 0.985 % 252 1.021 %

8 8 1 0.09 % 12 0.044 % 11 0.045 %

9 9 2 0.18 % 5 0.018 % 4 0.016 %

10 10 1 0.09 % 9 0.033 % 9 0.036 %

11 11 10 0.92 % 141 0.512 % 128 0.518 %

12 12 21 1.93 % 114 0.414 % 101 0.409 %

13 13 31 2.85 % 617 2.242 % 559 2.264 %

52

Page 55: Analisi degli Accessi al Sistema di Verbalizzazione Esami Online

14 14 1 0.09 % 1 0.004 % 1 0.004 %

15 15 1 0.09 % 17 0.062 % 13 0.053 %

16 16 3 0.28 % 4 0.015 % 4 0.016 %

17 17 5 0.46 % 29 0.105 % 26 0.105 %

18 18 1 0.09 % 1 0.004 % 1 0.004 %

19 NA 11 1.01 % 72 0.262 % 66 0.267 %

Infine i risultati ottenuti vengono visualizzati con l’ausilio di un grafico a barre. > tmp <- matrix(

+ c(

+ round(roles$ users / sum(roles $ users) * 100 , 2),

+ round(roles$ sessions / sum(roles$ sessions) * 100 , 3 ),

+ round(roles$ verbalizations / sum(roles$ verbalizations) * 100, 3 )

+ ),

+ nrow = 19 , ncol = 3 ,

+ dimnames = list(roles$ role, c( "users_pct", "sessions_pct" , "verbalizations_pct"))

+ )

> colnames(tmp) <- c( "% users" , "% sessions", "% verbalizations" )

> par(mar = c( 1.25, 2.6 , .5 , . 5))

> barplot(t(tmp), names.arg = ifelse(is.na(roles$ role), "NA", roles $ role), beside = TRUE, col =

c( "#FFFF00", "#00FFFF" , "#FF00FF" ), border = NA , ylim = c(0 , 34 ), family = "roboto" , col.axis =

"#595959" , mgp = c(3 , . 2, 0 ), panel.first = grid(0 , 34 , col = "#EEEEEE", lty = "solid"), xpd =

FALSE, xaxs = "i", axes = FALSE)

> axis(2 , yaxp = c(0 , 34, 17 ), las = 2, line = 0.25, family = "roboto" , col = "#595959" , col.ticks

= "#595959", col.axis = "#595959" , labels = paste( 0: 17 * 2, "%" , sep = "" ), at = 0 : 17 * 2 , tck =

- 0.01, hadj = . 75)

> par(family = "roboto" )

> legend( "topright", colnames(tmp), bty = "n", text.col = "#595959" , x.intersp = 1 , y.intersp =

1.5 , pch = 19 , col = c( "#FFFF00", "#00FFFF" , "#FF00FF" ), pt.cex = 2 )

53

Page 56: Analisi degli Accessi al Sistema di Verbalizzazione Esami Online

5.1.2. Dipartimenti Nel dataset user.dat sono presenti i 22 dipartimenti seguenti. > department.dat <- unique(user.dat[order(user.dat $department_id, na.last = NA), c( "department_id",

"department_string")])

> rownames(department.dat) <- 1: nrow(department.dat)

> user.dat <- user.dat[setdiff(names(user.dat), "department_string")]

> department.dat

dep_id department_string

1 000530 Unità di Staff Servizio di Prevenzione e Protezione

2 000555 Ufficio Industrial Liaison Office e Placement

3 008000 Ex Facoltà di Psicologia

4 009000 Ex Facoltà di Scienze della Formazione

5 010000 Ex Scuola Superiore di Lingue Moderne per Interpreti e Traduttori

6 010690 Centro Linguistico di Ateneo

7 011000 Ex Facoltà di Ingegneria

8 014467 Dipartimento di Fisica

9 014468 Dipartimento di Scienze Politiche e Sociali

10 014472 Dipartimento di Scienze Economiche, Aziendali, Matematiche e Statistiche

11 015000 Ex Facoltà di Medicina e Chirurgia

12 017074 Dipartimento Universitario Clinico di Scienze Mediche, Chirurgiche e della Salute

13 017078 Dipartimento di Scienze Giuridiche, del Linguaggio, dell’Interpretazione e della

Traduzione

14 017080 Dipartimento di Matematica e Geoscienze

15 017081 Dipartimento di Studi Umanistici

16 023000 Dipartimento di Matematica e Informatica

17 028654 Segreteria Didattica DF

18 028663 Segreteria Amministrativa DSCF

19 029000 Dipartimento di Scienze Chimiche e Farmaceutiche

54

Page 57: Analisi degli Accessi al Sistema di Verbalizzazione Esami Online

20 042000 Dipartimento di Ingegneria e Architettura

21 056000 Dipartimento di Italianistica Linguistica Comunicazione Spettacolo

22 084000 Dipartimento di Scienze della Vita

Si vuole calcolare il numero di utenti, il numero di sessioni e il numero di sessioni di

verbalizzazione per ciascun dipartimento. > tmp <- merge(unique(session.dat["user_id" ]), user.dat, all.x = TRUE )

> tmp <- tmp$ department_id

> tmp[is.na(tmp)] <- - 1

> departments <- aggregate(tmp, list ( "department" = tmp), "length" )

> tmp <- merge(session.dat, user.dat, all.x = TRUE )

> tmp <- tmp[c( "verbalization" , "department_id" )]

> tmp$ department_id[is.na(tmp $ department_id)] <- - 1

> departments <- merge(

+ departments,

+ aggregate(tmp$ department_id, list ("department" = tmp $ department_id), "length" ),

+ by.x = "department" , by.y = "department",

+ all.x = TRUE

+ )

> departments <- merge(

+ departments,

+ aggregate(

+ tmp $ department_id[tmp$ verbalization == TRUE ],

+ list ("department" = tmp $ department_id[tmp$ verbalization == TRUE ]),

+ "length"

+ ),

+ by.x = "department" , by.y = "department",

+ all.x = TRUE

+ )

> names(departments)[- 1 ] <- c( "users" , "sessions" , "verbalizations" )

> departments$ department[departments $ department == - 1 ] <- NA

> departments <- departments[order(departments$ department),]

> rownames(departments) <- 1 : nrow(departments)

> data.frame(

+ departments[ "department"],

+ departments[ "users" ],

+ "users_pct" = paste(round(departments $ users / sum(departments$ users) * 100, 2 ), "%"),

+ departments[ "sessions"],

+ "sessions_pct" = paste(format(round(departments$ sessions / sum(departments$ sessions) * 100 , 3 ),

nsmall = 3 ), "%" ),

+ departments[ "verbalizations"],

55

Page 58: Analisi degli Accessi al Sistema di Verbalizzazione Esami Online

+ "verbalizations_pct" = ifelse(is.na(departments$ verbalizations), NA,

paste(format(round(departments $verbalizations / sum(departments $ verbalizations, na.rm = TRUE) *

100 , 3 ), nsmall = 3 ), "%"))

+ )

department users users_pct sessions sessions_pct verbalizations verbalizations_pct

1 000530 1 0.09 % 2 0.007 % 2 0.008 %

2 000555 1 0.09 % 17 0.062 % 13 0.053 %

3 008000 1 0.09 % 4 0.015 % 4 0.016 %

4 009000 1 0.09 % 7 0.025 % 7 0.028 %

5 010000 3 0.28 % 7 0.025 % 5 0.020 %

6 010690 14 1.29 % 271 0.985 % 252 1.021 %

7 011000 1 0.09 % 12 0.044 % 12 0.049 %

8 014467 58 5.34 % 1778 6.462 % 1615 6.541 %

9 014468 54 4.97 % 2245 8.159 % 1975 8.000 %

10 014472 74 6.81 % 2626 9.544 % 2418 9.794 %

11 015000 2 0.18 % 2 0.007 % NA <NA>

12 017074 187 17.22 % 2397 8.712 % 2142 8.676 %

13 017078 94 8.66 % 2627 9.548 % 2346 9.502 %

14 017080 74 6.81 % 2607 9.475 % 2359 9.555 %

15 017081 162 14.92 % 3768 13.695 % 3314 13.423 %

16 023000 2 0.18 % 35 0.127 % 35 0.142 %

17 028654 1 0.09 % 1 0.004 % 1 0.004 %

18 028663 1 0.09 % 1 0.004 % 1 0.004 %

19 029000 59 5.43 % 1816 6.600 % 1631 6.606 %

20 042000 156 14.36 % 3573 12.986 % 3193 12.933 %

21 056000 1 0.09 % 2 0.007 % 2 0.008 %

22 084000 119 10.96 % 3568 12.968 % 3227 13.071 %

23 <NA> 20 1.84 % 148 0.538 % 135 0.547 %

Infine i risultati ottenuti vengono visualizzati con l’ausilio di un grafico a barre. > tmp <- matrix(

+ c(

+ round(departments $users / sum(departments $ users) * 100, 3 ),

+ round(departments $sessions / sum(departments $sessions) * 100, 3 ),

+ ifelse(is.na(departments $verbalizations), NA , round(departments$ verbalizations /

sum(departments $ verbalizations, na.rm = TRUE) * 100, 3 ))

+ ),

+ nrow = 23 , ncol = 3 , dimnames = list(departments$ department, c( "users_pct", "sessions_pct" ,

"verbalizations_pct"))

+ )

> colnames(tmp) <- c( "% users" , "% sessions", "% verbalizations" )

> par(mar = c( 1.25, 2.6 , .5 , . 5))

> x <- barplot(t(tmp), names.arg = ifelse(is.na(rownames(tmp)), "NA", rownames(tmp)), beside =

TRUE, col = c( "#FFFF00" , "#00FFFF" , "#FF00FF"), border = NA, ylim = c( 0, 18 ), family = "roboto" ,

col.axis = "#595959" , mgp = c( 3, . 2 , 0), panel.first = grid(0 , 36, col = "#EEEEEE" , lty = "solid" ),

xpd = FALSE, xaxs = "i" , axes = FALSE , cex.names = .825 )

56

Page 59: Analisi degli Accessi al Sistema di Verbalizzazione Esami Online

> axis(2 , yaxp = c(0 , 18, 18 ), las = 2, line = 0.25, family = "roboto" , col = "#595959" , col.ticks

= "#595959", col.axis = "#595959" , labels = paste( 0: 18 , "%", sep = "" ), at = 0: 18 , tck = - 0.01,

hadj = . 75 )

> par(family = "roboto" )

> legend( "topright", colnames(tmp), bty = "n", text.col = "#595959" , x.intersp = 1 , y.intersp =

1.5 , pch = 19 , col = c( "#FFFF00", "#00FFFF" , "#FF00FF" ), pt.cex = 2 )

> text(x, ifelse(is.na(t(tmp)), 0 , NA ), ifelse(is.na(t(tmp)), "0", NA ), pos = 3 , cex = . 825, family

= "mono", col = "#FF00FF", xpd = TRUE )

5.1.3. Utenti Strutturati Si vuole calcolare il numero di utenti, il numero di sessioni e il numero di sessioni di

verbalizzazione per gli utenti strutturati e non strutturati. > tmp <- merge(unique(session.dat["user_id" ]), user.dat, all.x = TRUE )

> tmp <- tmp$ structured

> tmp[is.na(tmp)] <- - 1

> structured <- aggregate(tmp, list ( "flag" = tmp), "length" )

> tmp <- merge(session.dat, user.dat, all.x = TRUE )

> tmp <- tmp[c( "verbalization" , "structured")]

> tmp$ structured[is.na(tmp$ structured)] <- -1

> structured <- merge(

+ structured,

+ aggregate(tmp$ structured, list( "flag" = tmp$ structured), "length" ),

+ by.x = "flag", by.y = "flag",

+ all.x = TRUE

+ )

57

Page 60: Analisi degli Accessi al Sistema di Verbalizzazione Esami Online

> structured <- merge(

+ structured,

+ aggregate(

+ tmp $ structured[tmp$ verbalization == TRUE],

+ list ("flag" = tmp $structured[tmp $ verbalization == TRUE]),

+ "length"

+ ),

+ by.x = "flag", by.y = "flag",

+ all.x = TRUE

+ )

> names(structured)[- 1 ] <- c( "users" , "sessions" , "verbalizations")

> structured$ flag[structured $ flag == -1 ] <- NA

> structured$ flag <- as.logical(structured $ flag)

> structured <- structured[order(structured $flag, decreasing = TRUE),]

> rownames(structured) <- 1 : nrow(structured)

> data.frame(

+ structured[ "flag"],

+ structured[ "users" ],

+ "users_pct" = paste(round(structured $ users / sum(structured $ users) * 100 , 2 ), "%"),

+ structured[ "sessions"],

+ "sessions_pct" = paste(format(round(structured $sessions / sum(structured $ sessions) * 100, 2 ),

nsmall = 2 ), "%" ),

+ structured[ "verbalizations"],

+ "verbalizations_pct" = paste(format(round(structured $ verbalizations /

sum(structured $ verbalizations) * 100 , 2 ), nsmall = 2 ), "%")

+ )

flag users users_pct sessions sessions_pct verbalizations verbalizations_pct

1 TRUE 701 64.55 % 22457 81.62 % 20085 81.35 %

2 FALSE 374 34.44 % 4985 18.12 % 4538 18.38 %

3 NA 11 1.01 % 72 0.26 % 66 0.27 %

Infine i risultati ottenuti vengono visualizzati con l’ausilio di un grafico a barre. > tmp <- matrix(

+ c(

+ round(structured $ users / sum(structured$ users) * 100, 2 ),

+ round(structured $ sessions / sum(structured $ sessions) * 100 , 2 ),

+ round(structured $ verbalizations / sum(structured $ verbalizations) * 100 , 2 )

+ ),

+ nrow = 3 , ncol = 3 , dimnames = list(structured $flag, c( "users_pct" , "sessions_pct" ,

"verbalizations_pct"))

+ )

> colnames(tmp) <- c( "% users" , "% sessions", "% verbalizations" )

> par(mar = c( 1.75, . 5 , .5 , . 5 ))

58

Page 61: Analisi degli Accessi al Sistema di Verbalizzazione Esami Online

> x <- barplot(t(tmp), names.arg = c( "structured == FALSE", "structured == TRUE", "NA"), beside =

TRUE, col = c( "#FFFF00" , "#00FFFF" , "#FF00FF"), border = NA, ylim = c( 0, 85 ), family = "roboto" ,

col.axis = "#595959" , mgp = c( 3, . 75 , 0 ), xpd = TRUE, xaxs = "i" , axes = FALSE, cex.names = 1.5 ,

xlim = c( 0 , 16 ))

> par(family = "roboto" )

> legend( "topright", colnames(tmp), bty = "n", text.col = "#595959" , x.intersp = 1 , y.intersp =

1.5 , pch = 19 , col = c( "#FFFF00", "#00FFFF" , "#FF00FF" ), pt.cex = 2 )

> text(x, t(tmp), paste(t(tmp), "%" , sep = ""), pos = 3 , cex = 1.25, col = "#595959" , xpd = TRUE )

5.2. Caratterizzazione degli Utenti In questo paragrafo si prenderanno in considerazione gli utenti singolarmente. Per ciascun

utente si calcoleranno le seguenti statistiche, le quali saranno aggiunte come variabili al dataframe user.dat.

session Numero di sessioni eseguite dall’utente.

verbalization Numero di sessioni di verbalizzazione eseguite dall’utente.

ip address Numero di indirizzi IP unici da cui l’utente ha eseguito sessioni di verbalizzazione.

ua slim Numero di user agent unici con cui l’utente ha eseguito sessioni di verbalizzazione.

pair (ip, ua) Numero di coppie (indirizzo ip, user agent) uniche con cui l’utente ha eseguito sessioni di verbalizzazione.

country Numero di paesi del mondo da cui l’utente ha eseguito sessioni di verbalizzazione.

59

Page 62: Analisi degli Accessi al Sistema di Verbalizzazione Esami Online

italy Numero di sessioni di verbalizzazione eseguite dall’utente in Italia.

region Numero di regioni italiane da cui l’utente ha eseguito sessioni di verbalizzazione.

fvg Numero di sessioni di verbalizzazione eseguite dall’utente in Friuli-Venezia Giulia.

holiday Numero di sessioni di verbalizzazione eseguite dall’utente in giornate festive.

mean duration Durata media in minuti delle sessioni di verbalizzazione dell’utente.

Perciò tutte le statistiche, ad eccezione del numero complessivo di sessioni eseguite

dall’utente, saranno calcolate considerando esclusivamente le sessioni di verbalizzazione.

5.2.1. Statistiche Individuali In questo sottoparagrafo si vedrà come calcolare le statistiche elencate precedentemente,

come esportare i risultati e visualizzarli sotto forma di foglio di calcolo. > stat <- NULL

> for (user_id in unique(session.dat $ user_id)) {

+ x <- rep(NA_real_, 11)

+ names(x) <- c("session" , "verbalization", "ip_address", "ua_slim" , "pair_ip_ua" , "country" ,

"italy" , "region", "fvg", "holiday" , "mean_duration")

+

+ x["session" ] <- nrow(session.dat[session.dat $ user_id == user_id, ])

+

+ tmp <- merge(

+ session.dat[session.dat $ user_id == user_id & session.dat$ verbalization == TRUE , ],

+ ip.dat

+ )

+

+ x["verbalization" ] <- nrow(tmp)

+

+ if (x[ "verbalization"] != 0) {

+ x[ "ip_address"] <- length(unique(tmp $ ip_address))

+ x[ "ua_slim"] <- length(unique(tmp$ ua_slim_id))

+ x[ "pair_ip_ua"] <- nrow(unique(tmp[c( "ip_address", "ua_slim_id")]))

+ x[ "country"] <- length(unique(tmp$ country_name))

+ x[ "italy" ] <- nrow(tmp[tmp$ country_name == "Italy", ])

+ x[ "region" ] <- length(unique(tmp[tmp $ country_name == "Italy" , "region_name"]))

+ x[ "fvg"] <- nrow(tmp[tmp $region_name == "Friuli-Venezia Giulia" , ])

+ x[ "holiday"] <- nrow(tmp[tmp$ holiday == TRUE , ])

+ x[ "mean_duration" ] <- round(mean(apply(tmp, 1, function (x) {

+ difftime(x["session_end"], x[ "session_start"], x[ "timezone"], units = "mins" )

+ })), 2 )

60

Page 63: Analisi degli Accessi al Sistema di Verbalizzazione Esami Online

+ }

+

+ stat <- rbind(stat, data.frame ( "user_id" = user_id, t(x)))

+ }

> user.dat <- merge(user.dat, stat, all.y = TRUE )

Al termine delle istruzioni precedenti la struttura del dataframe user.dat dovrebbe essere

simile alla seguente. > str(user.dat)

'data.frame': 1086 obs. of 15 variables :

$ user_id : int 109762 109794 109815 109825 109871 109873 109891 109919 109936 109937 ...

$ structured : logi TRUE FALSE FALSE TRUE FALSE TRUE ...

$ role_id : int 1 2 2 1 2 1 2 2 1 1 ...

$ department_id : chr "017080" "042000" "029000" "084000" ...

$ session : num 2 1 7 22 49 23 14 32 59 11 ...

$ verbalization : num 2 1 6 22 43 23 12 23 55 10 ...

$ ip_address : num 2 1 4 9 11 5 6 7 11 8 ...

$ ua_slim : num 2 1 2 4 4 2 7 4 3 2 ...

$ pair_ip_ua : num 2 1 4 9 12 5 10 11 12 9 ...

$ country : num 1 1 1 1 1 1 1 1 1 1 ...

$ italy : num 2 1 6 22 43 23 12 23 55 10 ...

$ region : num 1 1 1 3 1 2 2 1 2 1 ...

$ fvg : num 2 1 6 20 43 20 8 23 54 10 ...

$ holiday : num 0 0 2 4 4 0 2 0 5 1 ...

$ mean_duration : num 19 64 7.33 33.18 20.02 ...

I dati vengono infine esportati da R e collocati in uno spreadsheet, al quale vengono aggiunti

anche alcuni campi percentuali basati sui valori delle rispettive colonne. > tmp <- data.frame(

+ user.dat,

+ "verbalization_pct" = NA_real_,

+ "italy_pct" = NA_real_,

+ "fvg_pct" = NA_real_,

+ "holiday_pct" = NA_real_

+ )

> x <- user.dat $ verbalization != 0

> tmp$ verbalization_pct[x] <- round(user.dat$ verbalization[x] / user.dat$ session[x] * 100 , 2 )

> tmp$ italy_pct[x] <- round(user.dat $ italy[x] / user.dat $ verbalization[x] * 100, 2 )

> tmp$ fvg_pct[x] <- round(user.dat $ fvg[x] / user.dat$ verbalization[x] * 100 , 2)

> tmp$ holiday_pct[x] <- round(user.dat$ holiday[x] / user.dat $ verbalization[x] * 100 , 2 )

> tmp <- tmp[c( 1 , 3 : 4 , 2, 5 : 6 , 16 , 7 : 11, 17 , 12 : 13 , 18 , 14, 19 , 15)]

> write.table(tmp, "user.txt" , sep = "\t " , quote = FALSE , row.names = FALSE)

61

Page 64: Analisi degli Accessi al Sistema di Verbalizzazione Esami Online

Casi Particolari Riordinando semplicemente le righe dello spreadsheet è immediato individuare, ad esempio,

gli utenti che non hanno effettuato sessioni di verbalizzazione.

Oppure gli utenti che hanno effettuato sessioni di verbalizzazione esclusivamente in giornate festive.

62

Page 65: Analisi degli Accessi al Sistema di Verbalizzazione Esami Online

5.2.2. Statistiche Complessive In quest’ultima sezione saranno calcolate delle statistiche complessive per gli utenti

(nell’ordine minimo , primo quartile , mediana , media , terzo quartile , massimo e deviazione standard ) a partire dai risultati ottenuti al sottoparagrafo precedente. > stat <- apply(tmp[5 : 19], 2 , "summary")

> stat$ session <- append(stat $ session, NA )

> stat$ verbalization <- append(stat$ verbalization, NA )

> x <- c(names(stat$ ip_address), "SD" )

> stat <- t(sapply(stat, "as.matrix" ))

> stat <- cbind(stat, apply(tmp[5 : 19 ], 2 , "sd", na.rm = TRUE))

> colnames(stat) <- x

> stat <- round(stat, 2 )

> stat

Min. 1st Qu. Median Mean 3rd Qu. Max. NA's SD

session 1 7.00 18.00 25.34 36.00 260 NA 26.64

verbalization 0 6.00 17.00 22.73 32.00 226 NA 24.13

verbalization_pct 20 85.71 92.73 90.29 100.00 100 13 11.53

ip_address 1 2.00 4.00 6.61 9.00 57 13 7.17

ua_slim 1 2.00 3.00 3.50 5.00 16 13 2.33

pair_ip_ua 1 3.00 6.00 8.34 11.00 75 13 8.61

country 1 1.00 1.00 1.11 1.00 4 13 0.37

italy 0 6.00 16.00 22.66 32.00 226 13 24.03

italy_pct 0 100.00 100.00 98.31 100.00 100 13 8.17

region 0 1.00 1.00 1.68 2.00 8 13 1.02

fvg 0 4.00 12.00 18.90 27.00 214 13 21.32

63

Page 66: Analisi degli Accessi al Sistema di Verbalizzazione Esami Online

fvg_pct 0 68.42 98.25 80.24 100.00 100 13 30.58

holiday 0 0.00 1.00 2.28 3.00 69 13 4.60

holiday_pct 0 0.00 4.08 9.89 14.29 100 13 16.36

mean_duration 0 16.60 30.52 29.16 37.39 446 13 21.28

Percentili Infine vengono calcolati i percentili per ciascuna statistica calcolata al sottoparagrafo

precedente ed esportati per poter essere visualizzati sotto forma di spreadsheet. > x <- apply(tmp[5 : 19 ], 2 , "quantile" , probs = seq(0 , 1 , 0.01), na.rm = TRUE , type = 2 )

> x <- round(x, 2)

> write.table(x, "quantiles.txt" , sep = " \t ", quote = FALSE, row.names = TRUE )

64

Page 67: Analisi degli Accessi al Sistema di Verbalizzazione Esami Online

65

Page 68: Analisi degli Accessi al Sistema di Verbalizzazione Esami Online

6. Mappa Interattiva

L’analisi dei dati è stata affiancata dall’implementazione di un’applicazione web allo scopo di fornire uno strumento per il monitoraggio delle sessioni e in particolare per l’individuazione di eventuali “anomalie” tra le sessioni di verbalizzazione oppure per l’osservazione di “comportamenti atipici” da parte degli utenti.

In quest’ultimo capitolo verranno illustrate le principali funzioni offerte dall’applicazione realizzata e ne verrà descritta la struttura, con particolare attenzione al database relazionale a cui l’applicazione si appoggia.

Infine si vedrà come i dati sono stati preparati tramite R e successivamente integrati nel database; in questo modo sarà possibile, ripercorrendo i passaggi illustrati con gli eventuali dovuti accorgimenti, integrare nell’applicazione un nuovo dataset.

66

Page 69: Analisi degli Accessi al Sistema di Verbalizzazione Esami Online

6.1. Strumenti dell’Applicazione L’applicazione si presenta come una mappa interattiva, sulla quale all’avvio vengono

contrassegnate tutte le posizioni geografiche da cui sono state eseguite le sessioni autenticate al sistema ESSE3.

L’utente può interagire con la mappa zoomando e spostandosi nella zona di interesse. Si possono inoltre settare alcuni filtri per le sessioni e visualizzare le informazioni delle sessioni di interesse mediante gli strumenti seguenti.

control panel Consente di modificare l’aspetto dei marker e di definire diversi parametri per la selezione delle sessioni di interesse; consente inoltre di richiamare lo user selector . È sempre presente e visibile sulla mappa.

info window Consente di visualizzare diverse informazioni sulle sessioni eseguite dalla posizione geografica selezionata. Viene richiamata cliccando sui marker.

user selector Consente di definire l’insieme degli utenti per la selezione delle sessioni e inoltre permette di impostare degli “alert” sulle statistiche degli utenti. Viene richiamato dal control panel .

I prossimi sottoparagrafi sono dedicati alla descrizione delle possibilità offerte dagli strumenti

presentati.

67

Page 70: Analisi degli Accessi al Sistema di Verbalizzazione Esami Online

6.1.1. Control Panel Questo strumento, sempre presente e visibile sulla mappa, consente di modificare l’aspetto dei

marker e di impostare alcuni filtri sulle sessioni; consente inoltre di richiamare lo user selector e mostra una serie di statistiche complessive sulle sessioni selezionate.

❶ Icona utilizzata per contrassegnare le posizioni geografiche.

❷ Variabile associata alle posizioni geografiche secondo la quale assegnare il colore delle icone.

❸ Dimensione delle icone.

❹ Colori assegnati ai valori estremi assunti dalla variabile selezionata in ❷.

❺ Periodo temporale di selezione delle sessioni.

❻ Fasce orarie di selezione delle sessioni.

❼ Flag sessioni di verbalizzazione.

❽ Flag sessioni eseguite durante giornate festive.

❾ Attivazione / disattivazione e apertura dello user selector .

➓ Statistiche complessive sulle sessioni selezionate.

6.1.2. Info Window Ogniqualvolta viene premuto un marker sulla mappa compare una finestra ancorata alla

rispettiva posizione geografica che permette di visualizzare tutte le informazioni sulle sessioni eseguite dalla posizione geografica selezionata.

68

Page 71: Analisi degli Accessi al Sistema di Verbalizzazione Esami Online

❶ Coordinate geografiche.

❷ Indirizzi IP associati alla rispettiva posizione geografica; possibilità di filtrare le sessioni in base all’indirizzo IP.

❸ Informazioni relative all’indirizzo IP selezionato.

❹ Informazioni sulle sessioni eseguite dalla posizione geografica selezionata.

❺ Informazioni sugli utenti responsabili delle sessioni eseguite dalla posizione geografica selezionata.

6.1.3. User Selector Questo strumento viene richiamato premendo il rispettivo campo sul control panel e consente

di definire l’insieme degli utenti per i quali verranno contrassegnate sulla mappa le posizioni geografiche delle rispettive sessioni.

69

Page 72: Analisi degli Accessi al Sistema di Verbalizzazione Esami Online

❶ Ciascuna riga della tabella corrisponde ad un utente i cui parametri rispettano tutti i vincoli

imposti mediante i filtri. Cliccando sui campi dell’intestazione è possibile ordinare le righe della tabella in base ai valori della colonna selezionata.

❷ Le righe della tabella verranno filtrate in base agli utenti selezionati.

❸ Le righe della tabella verranno filtrate in base ai dipartimenti selezionati.

❹ Le righe della tabella verranno filtrate in base al flag structured .

❺ Si possono impostare uno o più “alert” sulle statistiche degli utenti, definendo un intervallo in termini di percentili a cui il valore assunto dalla statistica deve (o non deve) appartenere per essere evidenziato.

Per quanto riguarda gli “alert” sulle statistiche degli utenti, se si volessero ad esempio

evidenziare i valori della statistica (ip, ua ), ovvero il numero delle coppie (indirizzo IP, user agent) utilizzate durante le sessioni di verbalizzazione, che non appartengono all’intervallo definito dal primo e dal terzo quartile, allora si potrebbe impostare l’alert per la rispettiva statistica come mostrato nella figura seguente.

Oppure se si volessero evidenziare tra i ricercatori universitari del dipartimento di ingegneria e architettura gli utenti per i quali i valori della variabile holiday % (percentuale delle sessioni di verbalizzazione effettuate in giornate festive) siano superiori al terzo quartile e contemporaneamente i valori della variabile μ time (durata media delle sessioni di verbalizzazione) siano inferiori al primo

70

Page 73: Analisi degli Accessi al Sistema di Verbalizzazione Esami Online

quartile, allora gli “alert” per le rispettive statistiche si potrebbe impostare come mostrato nella figura seguente.

6.2. Struttura dell’Applicazione L’applicazione è stata realizzata utilizzando diversi linguaggi di programmazione ed alcune

librerie libere. In particolare, sul lato client viene utilizzata la consolidata triade HTML, CSS e JavaScript; inoltre si è fatto un uso cospicuo delle librerie jQuery e Google Maps APIs. Mentre sul lato server viene utilizzato PHP come linguaggio di scripting e SQL per interfacciarsi ed interrogare il database.

L’applicazione è stata sviluppata e testata principalmente nella configurazione (Windows 10, Chrome 53.0) e non sono stati compiuti sforzi per garantire la compatibilità con ulteriori coppie (sistema operativo, browser).

6.2.1. Ciclo di interazione Utente-Mappa In questo sottoparagrafo viene descritto, con un esempio esplicativo, il ciclo di interazione

utente-mappa dell’applicazione.

71

Page 74: Analisi degli Accessi al Sistema di Verbalizzazione Esami Online

❶ L’utente interagisce con la mappa modificando, ad esempio, uno o più campi sul control panel .

❷ Lo script sul lato client cattura i valori dei campi modificati dall’utente e li imposta come parametri della richiesta HTTP inviata al server.

❸ Il server recupera il documento richiesto e lo esegue. Lo script lato server costruisce una query SQL sulla base dei parametri ricevuti dal client con cui interroga il database.

❹ Il DBMS elabora la richiesta ricevuta e restituisce i risultati allo script lato server.

❺ Lo script lato server interpreta i dati ricevuti dal database e li inoltra al client sotto forma di documento HTML oppure in formato JSON.

❻ Lo script lato client reagisce alla risposta ricevuta dal server aggiungendo e/o rimuovendo, ad esempio, alcuni marker dalla mappa tramite le API di Google Maps.

6.2.2. Implementazione del Database Il DBMS utilizzato dall’applicazione è MySQL e le tabelle che costituiscono il database

dell’applicazione sono di seguito elencate. mysql> SHOW TABLES;

+-----------------+

| Tables_in_mapdb |

+-----------------+

| ip_table |

| session_table |

| user_table |

+-----------------+

72

Page 75: Analisi degli Accessi al Sistema di Verbalizzazione Esami Online

Di seguito verranno fornite le istruzioni SQL necessarie alla costruzione di ciascuna delle tabelle elencate.

Session Table La tabella session ospita un record per ciascuna sessione autenticata al sistema ESSE3

presente nel dataset sessioni. mysql> CREATE TABLE session_table (session_id CHAR(20) NOT NULL PRIMARY KEY, jsessionid CHAR(52)

NOT NULL, ip_address CHAR(15) NOT NULL, user_id CHAR(6) NOT NULL, session_start CHAR(16) NOT NULL,

session_end CHAR(16) NOT NULL, session_start_string CHAR(26) NOT NULL, session_end_string CHAR(26)

NOT NULL, coordinates CHAR(17) NOT NULL, verbalization BOOL NOT NULL, holiday BOOL NOT NULL,

ua_slim_id CHAR(3), ua_slim_string CHAR(74));

mysql> DESCRIBE session_table;

+----------------------+------------+------+-----+---------+-------+

| Field | Type | Null | Key | Default | Extra |

+----------------------+------------+------+-----+---------+-------+

| session_id | char(20) | NO | PRI | NULL | |

| jsessionid | char(52) | NO | | NULL | |

| ip_address | char(15) | NO | | NULL | |

| user_id | char(6) | NO | | NULL | |

| session_start | char(16) | NO | | NULL | |

| session_end | char(16) | NO | | NULL | |

| session_start_string | char(26) | NO | | NULL | |

| session_end_string | char(26) | NO | | NULL | |

| coordinates | char(17) | NO | | NULL | |

| verbalization | tinyint(1) | NO | | NULL | |

| holiday | tinyint(1) | NO | | NULL | |

| ua_slim_id | char(3) | YES | | NULL | |

| ua_slim_string | char(74) | YES | | NULL | |

+----------------------+------------+------+-----+---------+-------+

IP Address Table La tabella ip address ospita un record per ciascun indirizzo IP tradotto in coordinate

geografiche. mysql> CREATE TABLE ip_table (ip_address CHAR(15) NOT NULL PRIMARY KEY, as_code CHAR(70), city_name

CHAR(29), country_name CHAR(20), country_code CHAR(2), isp CHAR(50), coordinates CHAR(17) NOT NULL,

organisation CHAR(50), region_code CHAR(3), region_name CHAR(29), timezone CHAR(30), zip_code

CHAR(8), status CHAR(7));

mysql> DESCRIBE ip_table;

+--------------+----------+------+-----+---------+-------+

| Field | Type | Null | Key | Default | Extra |

+--------------+----------+------+-----+---------+-------+

| ip_address | char(15) | NO | PRI | NULL | |

| as_code | char(70) | YES | | NULL | |

| city_name | char(29) | YES | | NULL | |

| country_name | char(20) | YES | | NULL | |

| country_code | char(2) | YES | | NULL | |

| isp | char(50) | YES | | NULL | |

| coordinates | char(17) | NO | | NULL | |

| organisation | char(50) | YES | | NULL | |

| region_code | char(3) | YES | | NULL | |

| region_name | char(29) | YES | | NULL | |

73

Page 76: Analisi degli Accessi al Sistema di Verbalizzazione Esami Online

| timezone | char(30) | YES | | NULL | |

| zip_code | char(8) | YES | | NULL | |

| status | char(7) | YES | | NULL | |

+--------------+----------+------+-----+---------+-------+

User Table Infine la tabella user ospita un record per ciascun user id unico presente nella tabella session .

mysql> CREATE TABLE user_table (user_id CHAR(6) NOT NULL PRIMARY KEY, role_id CHAR(2),

department_id CHAR(6), structured BOOL, session SMALLINT UNSIGNED NOT NULL, verbalization SMALLINT

UNSIGNED NOT NULL, ip_address SMALLINT UNSIGNED, ua_slim SMALLINT UNSIGNED, pair_ip_ua SMALLINT

UNSIGNED, country SMALLINT UNSIGNED, italy SMALLINT UNSIGNED, region SMALLINT UNSIGNED, fvg

SMALLINT UNSIGNED, holiday SMALLINT UNSIGNED, mean_duration FLOAT UNSIGNED);

mysql> DESCRIBE user_table;

+---------------+----------------------+------+-----+---------+-------+

| Field | Type | Null | Key | Default | Extra |

+---------------+----------------------+------+-----+---------+-------+

| user_id | char(6) | NO | PRI | NULL | |

| role_id | char(2) | YES | | NULL | |

| department_id | char(6) | YES | | NULL | |

| structured | tinyint(1) | YES | | NULL | |

| session | smallint(5) unsigned | NO | | NULL | |

| verbalization | smallint(5) unsigned | NO | | NULL | |

| ip_address | smallint(5) unsigned | YES | | NULL | |

| ua_slim | smallint(5) unsigned | YES | | NULL | |

| pair_ip_ua | smallint(5) unsigned | YES | | NULL | |

| country | smallint(5) unsigned | YES | | NULL | |

| italy | smallint(5) unsigned | YES | | NULL | |

| region | smallint(5) unsigned | YES | | NULL | |

| fvg | smallint(5) unsigned | YES | | NULL | |

| holiday | smallint(5) unsigned | YES | | NULL | |

| mean_duration | float unsigned | YES | | NULL | |

+---------------+----------------------+------+-----+---------+-------+

6.3. Caricamento dei Dati In quest’ultimo paragrafo si vedrà come i dati sono stati preparati in R per l’esportazione e

successivamente integrati nel database relazionale. Le istruzioni descritte potranno essere utilizzate, con i dovuti accorgimenti, anche per inserire in seguito un nuovo set di dati all’interno del database.

6.3.1. Session Table In questo sottoparagrafo verranno descritte le istruzioni eseguite al fine della preparazione dei

dati per la compilazione della tabella session nel database. > # coordinates

> tmp <- data.frame(

+ ip.dat["ip_address" ],

+ "coordinates" = paste(ip.dat$ latitude, ip.dat $ longitude),

+ stringsAsFactors = FALSE

+ )

74

Page 77: Analisi degli Accessi al Sistema di Verbalizzazione Esami Online

> tmp <- merge(session.dat[c( "session_id", "ip_address")], tmp)

> tmp <- tmp[- 1 ]

> # ua slim string

> x <- merge(

+ session.dat[c("session_id" , "ua_slim_id")],

+ ua_slim.dat[c("ua_slim_id" , "ua_slim_string" )],

+ all.x = TRUE

+ )

> tmp <- merge(tmp, x[ - 1])

> # session start string, session end string

> x <- merge(

+ session.dat[c("session_id" , "ip_address", "session_start" , "session_end")],

+ ip.dat[c("ip_address", "timezone" )]

+ )

> x <- apply(x, 1, function (x) {

+ c(

+ x[ "session_id"],

+ format(

+ as.POSIXct(x["session_start" ], "%Y/%m/%d %H:%M" , tz = x[ "timezone"]),

+ format = "%a %d/%m/%Y %H:%M %z"

+ ),

+ format(

+ as.POSIXct(x["session_end"], "%Y/%m/%d %H:%M", tz = x[ "timezone"]),

+ format = "%a %d/%m/%Y %H:%M %z"

+ )

+ )

+ })

> x <- t(x)

> colnames(x)[- 1 ] <- paste(colnames(x)[- 1 ], "_string" , sep = "" )

> tmp <- merge(tmp, as.data.frame(x, stringsAsFactors = FALSE))

> tmp <- merge(session.dat, tmp)

> tmp <- tmp[c( 1 :4 , 6 : 7 , 12 : 13 , 10 , 5 , 8 , 9 , 11 )]

> for (x in names(tmp)) {

+ if (typeof(tmp[, x]) == "logical" ) {

+ # cat(paste(x, "logical"), fill = TRUE)

+ tmp[, x] <- as.numeric(tmp[, x])

+ }

+ if (any(is.na(tmp[, x]))) {

+ # cat(paste(x, "NA"), fill = TRUE)

+ tmp[is.na(tmp[, x]), x] <- " \\ N"

+ }

+ }

> str(tmp)

75

Page 78: Analisi degli Accessi al Sistema di Verbalizzazione Esami Online

'data.frame': 27514 obs. of 13 variables :

$ session_id : chr "00IvNTlsKJdSSy72ekW6" "00jSYWnHLp2vIbT18Yej" "00NDik575H6ge5b9upUI"

...

$ jsessionid : chr "6982D50C88356887DC375C9FCDCD278E.esse3-units-prod-03" ...

$ ip_address : chr "140.105.162.xyz" "140.105.251.xyz" "82.58.203.xyz" ...

$ user_id : int 114926 110414 115157 112099 110286 113107 110725 110714 111515 ...

$ session_start : chr "2016/02/03 13:21" "2016/02/01 16:45" "2016/02/25 20:43" ...

$ session_end : chr "2016/02/03 13:27" "2016/02/01 17:58" "2016/02/25 22:03" ...

$ session_start_string : chr "Wed 03/02/2016 13:21 +0100" "Mon 01/02/2016 16:45 +0100" ...

$ session_end_string : chr "Wed 03/02/2016 13:27 +0100" "Mon 01/02/2016 17:58 +0100" ...

$ coordinates : chr "45.6486 13.78" "45.6486 13.78" "46.065 13.2587" "45.6486 13.78" ...

$ verbalization : num 1 1 1 1 1 1 1 0 1 1 ...

$ holiday : num 0 0 0 0 0 0 1 0 0 0 ...

$ ua_slim_id : chr "308" "4" "101" "191" ...

$ ua_slim_string : chr "desktop Chrome 48.0 Windows NT 6.1 (Windows 7)" ...

> write.table(tmp, "session.txt", sep = " \t ", quote = FALSE, row.names = FALSE, col.names = FALSE )

Infine i dati esportati da R vengono inseriti nella tabella session del database tramite

l’istruzione seguente. mysql> LOAD DATA LOCAL INFILE 'session.txt' INTO TABLE session_table;

6.3.2. IP Address Table In questo sottoparagrafo verranno descritte le istruzioni eseguite al fine della preparazione dei

dati per la compilazione della tabella ip address nel database. > tmp <- cbind(

+ ip.dat,

+ "coordinates" = paste(ip.dat$ latitude, ip.dat $ longitude),

+ stringsAsFactors = FALSE

+ )

> tmp <- tmp[c( 1 :6 , 15 , 9 : 14 )]

> for (x in names(tmp)) {

+ if (typeof(tmp[, x]) == "logical" ) {

+ # cat(paste(x, "logical"), fill = TRUE)

+ tmp[, x] <- as.numeric(tmp[, x])

+ }

+ if (any(is.na(tmp[, x]))) {

+ # cat(paste(x, "NA"), fill = TRUE)

+ tmp[is.na(tmp[, x]), x] <- " \\ N"

+ }

+ }

> str(tmp)

'data.frame': 2802 obs. of 13 variables :

76

Page 79: Analisi degli Accessi al Sistema di Verbalizzazione Esami Online

$ ip_address : chr "1.4.208.xyz" "2.32.116.xyz" "2.32.125.xyz" "2.32.219.xyz" ...

$ as_code : chr "AS23969 TOT Public Company Limited" "AS30722 VODAFONE-IT-ASN" ...

$ city_name : chr "Bangkok" "Udine" "Venice" "Milan" ...

$ country_name: chr "Thailand" "Italy" "Italy" "Italy" ...

$ country_code: chr "TH" "IT" "IT" "IT" ...

$ isp : chr "TOT" "Vodafone Italia DSL" "Vodafone Italia DSL" "Vodafone Italia DSL" ...

$ coordinates : chr "13.7563 100.502" "46.065 13.2587" "45.4371 12.3327" "45.4667 9.2" ...

$ organisation: chr "TOT" "Vodafone Italia DSL" "Vodafone Italia DSL" "Vodafone Italia DSL" ...

$ region_code : chr "10" "36" "34" "25" ...

$ region_name : chr "Bangkok" "Friuli-Venezia Giulia" "Veneto" "Lombardia" ...

$ timezone : chr "Asia/Bangkok" "Europe/Rome" "Europe/Rome" "Europe/Rome" ...

$ zip_code : chr " \\N" "33100" "30135" "20123" ...

$ status : chr "success" "success" "success" "success" ...

> write.table(tmp, "ip.txt" , sep = " \t" , quote = FALSE, row.names = FALSE, col.names = FALSE)

Infine i dati esportati da R vengono inseriti nella tabella ip address del database tramite

l’istruzione seguente. mysql> LOAD DATA LOCAL INFILE 'ip.txt' INTO TABLE ip_table;

6.3.3. User Table In questo sottoparagrafo verranno descritte le istruzioni eseguite al fine della preparazione dei

dati per la compilazione della tabella user nel database. > tmp <- user.dat[c(1 , 3: 4 , 2 , 5 : 15 )]

> for (x in names(tmp)) {

+ if (typeof(tmp[, x]) == "logical" ) {

+ # cat(paste(x, "logical"), fill = TRUE)

+ tmp[, x] <- as.numeric(tmp[, x])

+ }

+ if (any(is.na(tmp[, x]))) {

+ # cat(paste(x, "NA"), fill = TRUE)

+ tmp[is.na(tmp[, x]), x] <- " \\ N"

+ }

+ }

> str(tmp)

'data.frame': 1086 obs. of 15 variables :

$ user_id : int 109762 109794 109815 109825 109871 109873 109891 109919 109936 109937 ...

$ role_id : chr "1" "2" "2" "1" ...

$ department_id : chr "017080" "042000" "029000" "084000" ...

$ structured : chr "1" "0" "0" "1" ...

$ session : num 2 1 7 22 49 23 14 32 59 11 ...

$ verbalization : num 2 1 6 22 43 23 12 23 55 10 ...

$ ip_address : chr "2" "1" "4" "9" ...

77

Page 80: Analisi degli Accessi al Sistema di Verbalizzazione Esami Online

$ ua_slim : chr "2" "1" "2" "4" ...

$ pair_ip_ua : chr "2" "1" "4" "9" ...

$ country : chr "1" "1" "1" "1" ...

$ italy : chr "2" "1" "6" "22" ...

$ region : chr "1" "1" "1" "3" ...

$ fvg : chr "2" "1" "6" "20" ...

$ holiday : chr "0" "0" "2" "4" ...

$ mean_duration : chr "19" "64" "7.33" "33.18" ...

> write.table(tmp, "user.txt" , sep = "\t " , quote = FALSE , row.names = FALSE, col.names = FALSE)

Infine i dati esportati da R vengono inseriti nella tabella user del database tramite l’istruzione

seguente. mysql> LOAD DATA LOCAL INFILE 'user.txt' INTO TABLE user_table;

6.3.4. Ulteriori Accorgimenti A seguito dell’inserimento di un nuovo set di dati, potrebbe rendersi necessario apportare

alcune modifiche al codice sorgente dell’applicazione per evitare di incorrere in errori durante l’esecuzione.

In particolare, nel codice JavaScript del documento index.html , andranno modificati i valori della variabile quantiles , ricavandoli in base ai nuovi valori delle statistiche degli utenti.

Inoltre, se nel nuovo insieme di dati sono presenti dei ruoli o dei dipartimenti non osservati nel dataset oggetto di questa relazione, occorre aggiungere, ancora nel codice JavaScript del documento index.html , le eventuali nuove coppie (role id, role string) e (department id, department string) rispettivamente alle variabili roles e departments , facilmente rintracciabili all’interno del codice tramite la funzione “find” di un qualsiasi editor di testo.

78

Page 81: Analisi degli Accessi al Sistema di Verbalizzazione Esami Online

79

Page 82: Analisi degli Accessi al Sistema di Verbalizzazione Esami Online

Conclusione

In questa breve sezione conclusiva verranno semplicemente citate le principali idee rimaste tali e non messe in pratica durante i tre mesi di lavoro dedicati all’analisi dei dati e allo sviluppo dell’applicazione descritti nella presente relazione.

Sequenza Richieste / Risposte HTTP delle Sessioni di Verbalizzazione Il punto principale che l’analisi descritta in questa relazione non è purtroppo riuscita a coprire

è stata la ricostruzione della sequenza di richieste e risposte HTTP avvenute all’interno delle sessioni di verbalizzazione.

Questa operazione può essere teoricamente compiuta raggruppando nel dataset cineca le richieste aventi il medesimo jsessionid e permetterebbe, sempre in via teorica, di individuare tra le sessioni di verbalizzazione quelle in cui vi è stata l’effettiva registrazione dell’esito di un esame.

Inoltre si potrebbe calcolare per ciascuna sessione di verbalizzazione la velocità media di richieste, rapportando il numero di richieste della sessione (al numeratore) con la durata della sessione (al denominatore). Questo valore, oltre a fornire un’informazione in più sulla sessione stessa, potrebbe essere utilizzato per calcolare una nuova statistica per gli utenti, definendo cioè la velocità media di richieste - ad esempio, al minuto - per ciascun utente.

Profili Utenti Con le statistiche individuali degli utenti calcolate al capitolo quinto si potrebbero ora definire

un certo numero di profili o classi di utenti; ad esempio la classe degli utenti che effettuano le sessioni di verbalizzazione durante la mattina nei giorni feriali oppure la classe degli utenti che effettuano le sessioni di verbalizzazione durante il weekend ad orari serali e così via.

Una volta definito l’insieme delle classi, a ciascun utente verrebbe assegnato il profilo più confacente alle proprie caratteristiche.

Tuttavia, se il profilo di un utente non dovesse combaciare con nessuno tra quelli definiti, allora il comportamento di tale utente potrebbe essere segnalato come “atipico” oppure, se si riscontrassero altri profili simili a quello dell’utente sotto osservazione, si potrebbe aggiungere una nuova classe all’insieme precedentemente costruito e associare l’utente in esame al nuovo profilo definito.

Grafici sulla Mappa Interattiva Un’altra idea discussa ma che non ha trovato purtroppo una realizzazione praticata è stata

l’integrazione di un grafico dinamico sulla info window dell’applicazione web descritta al capitolo sesto.

Ad esempio si sarebbe potuta mostrare la distribuzione giornaliera (nell’intervallo temporale definito sul control panel ) del numero di sessioni effettuate dalla posizione geografica selezionata.

80