• Non ci sono risultati.

Dashboard START

Nel documento START: la decisione oltre al dato (pagine 56-66)

Capitolo 2 – Caso di Studio

3.5 Visualizzazione dei risultati

3.5.3 Dashboard START

La dashboard informativa è stata creata con Visual Studio, utilizzando il framework MVC con linguaggio C# e alcune librerie front-end per gli stili e i grafici. Tra queste ultime la più importante è senz’altro d3.js, scoperta durante l’esame di Visual Analytics sostenuto nel periodo di studi. D3 è una libreria JavaScript che permette di realizzare visualizzazioni interattive mediante Html, Svg e CSS. Grazie all’utilizzo di alcuni standard, d3 offre tutte le funzionalità dei browser moderni senza la necessità di legarsi ad alcun framework, combinando componenti di visualizzazione ad un approccio basato sulla manipolazione del DOM. Unitamente a d3.js è stata utilizzata anche NVD3, una libreria che permette di creare componenti grafici riutilizzabili con d3.

Il progetto MVC ha una struttura semplice: si compone di viste, modelli e controller come previsto dal framework, ma anche da un file che gestisce l’importazione di script e stili CSS nelle varie pagine (BundleConfig.cs) e un file (_Layout.cshtml) che contiene la struttura di default che ogni vista deve avere.

Per il progetto formativo sono state create 2 viste: la prima di esse è la home page e contiene lo spazio per ricercare un codice cliente.

Figura 53 Home page del progetto MVC START

La textbox nella home page del progetto accetta solo valori numerici interi: effettuando una ricerca sbagliata, il programma gestisce l’errore ed effettua un redirect alla home page da cui è possibile ricercare un nuovo cliente. Il controller relativo alla home page è molto semplice: contiene la funzione Index che ritorna la vista principale; inoltre contiene la funzione CercaCliente che viene richiamata tramite l’apposito pulsante “Cerca” nella vista stessa. Tale funzione permette di trasferire il controllo al controller della vista successiva, passandole un ClienteModel istanziato con il corretto codice cliente.

public class HomeController : BaseController {

public ClienteModel c; public ActionResult Index() {

return View(); }

public ActionResult CercaCliente(String codCliente) {

c = new ClienteModel(); try

{

c.codCliente = int.Parse(codCliente); }

{

return RedirectToAction("Index", "Home"); }

return RedirectToAction("Index", "Cliente", c); }

}

La classe ClienteController contiene un ClienteModel, ovvero il modello che conterrà tutto ciò che riguarda il cliente ricercato. Inizialmente il modello contiene solo il codice cliente; verrà popolato con le altre informazioni relative al cliente una volta che il controller lo avrà aggiornato. ClienteController infatti, contiene una sola funzione “Index” che si occupa di aggiornare il modello richiamando i metodi che esso espone, e quindi di ritornare la vista con la dashboard vera e propria.

public class ClienteController : BaseController {

public ClienteModel c;

public ActionResult Index(ClienteModel cc) { try { this.c = cc; c.anagrafica(); c.fatturatoPerMese(); c.fatturatoPerBu(); c.calcolaTot(); c.calcolafattSuperBu(); c.calcolaCluster(); c.makeJson();

return View("Index", c); }

catch (Exception e) {

return RedirectToAction("Index", "Home"); }

} }

All’interno della funzione Index, il controller richiama funzioni del modello in modo da caricare i dati necessari per i grafici e per la visualizzazione della dashboard. La classe ClienteModel contiene varie proprietà: il codice cliente, la ragione sociale, l’indirizzo, la regione, la data di creazione e il cluster a cui esso appartiene. Essa inoltre contiene alcune strutture dati nella forma di Dictionary, che memorizzano il fatturato per Business Unit, per Super Business Unit, per mese e per provenienza. Ogni funzione di ClienteModel richiamata da ClienteController, instaura una connessione con il cubo OLAP presente in locale mediante la libreria Adomd di Microsoft. Le istruzioni di definizione e di apertura della connessione sono le seguenti:

AdomdConnection conn = new AdomdConnection("Data Source = localhost; Catalog=PRG_Start");

conn.Open();

La query MDX o DMX necessaria ad estrarre le informazioni, viene inserita in una variabile di tipo stringa. Essa viene passata in un oggetto AdomdCommand, insieme alla connessione aperta in precedenza, creando così un nuovo comando in Adomd. L’esecuzione del comando mediante il metodo ExecuteReader, genera un AdomdDataReader, ovvero un oggetto che ci permette di leggere le informazioni estratte iterando su di esse.

AdomdCommand cmd = new AdomdCommand(commandText, conn); AdomdDataReader dr = cmd.ExecuteReader();

while (dr.Read()) {

//lettura e memorizzazione delle informazioni estratte }

Una volta finita la lettura delle informazioni, vengono chiusi il DataReader e quindi la connessione.

dr.Close(); conn.Close();

Analizziamo le query MDX costruite per estrarre le informazioni dal cubo necessarie alla compilazione della dashboard:

 Estrazione delle informazioni anagrafiche del cliente:

SELECT {[Measures].[Qta]} on 0, ([D_Cliente].[Cod Cliente].&[528], , [D_Cliente].[Ragione Sociale].[Ragione Sociale]

, [D_Cliente].[Indirizzo].[Indirizzo] , [D_Cliente].[Provincia].[Provincia] , [D_Cliente].[Regione].[Regione]

, [D_Cliente].[Data Creazione].[Data Creazione] , [D_Cliente].[Aperto Da Cash].[Aperto Da Cash] , [D_Cliente].[Cash Default].[Cash Default] , [D_Cliente].[Nazione].[Nazione]) on 1 FROM [DW START__Tesi]

 Estrazione del fatturato del cliente diviso per mese

SELECT {[Measures].[Valore]} on 0, ORDER(([D_Cliente].[Cod Cliente].&[528] , [D_Calendario].[Anno_Data].[Mese] , [D_Calendario].[Dex Mese].[Dex Mese]) , {[D_Calendario].[Mese].[Mese]}) on 1 , FROM [DW START__Tesi]

 Estrazione del fatturato del cliente diviso per Business Unit

SELECT [Measures].[Valore] on 0

, ORDER(([D_Cliente].[Cod Cliente].&[528]

, [D_Articolo].[SbuBUSettRepFamSottofam].[Cod Bu] , [D_Articolo].[Dex Bu].[Dex Bu])

, [Measures].[Valore], DESC) on 1 FROM [DW START__Tesi]

 Estrazione del fatturato del cliente diviso per Super Business Unit

WITH MEMBER val as IIF ([Measures].[Valore]= Null, 0, [Measures].[Valore]) SELECT val on 0,

([D_Cliente].[Cod Cliente].&[528]

, [D_Articolo].[Dex Superbu].[Dex Superbu]) on 1 FROM [DW START__Tesi]

 Estrazione del fatturato del cliente diviso per provenienza (sede o C&C)

SELECT [Measures].[Valore] on 0 , ([D_Cliente].[Cod Cliente].&[528]

, [D_Documento].[Flag Cec].[Flag Cec]) on 1 FROM [DW START__Tesi]

Una volta memorizzati, i dati vengono serializzati in un JSON che viene inserito in un hidden field sull’interfaccia. Essi vengono quindi parsati con Javascript per poter costruire i grafici con d3.js e nvd3.js. Il Pie Chart che mostra la divisione tra fatturato da C&C e da sede è stato ottenuto mediante il codice seguente:

function graficoFattCash(fattSede, fattCash) {

nv.addGraph(function () { // aggiunge un nuovo grafico con nvd3

var chart = nv.models.pieChart() //specifica il tipo di grafico

.y(function (d) { return d.value }) // assegna i valori all'asse y

.showLabels(true) //mostra i label

.labelType("percent") //configura le label come %

.donut(true)//mostra un grafico di tipo donut (non a torta, ma ad anello)

.donutRatio(0.35);//specifica il raggio del cerchio interno

chart.tooltip.contentGenerator(function (d) { //formattazione tooltip

return '<b>' + d.data.label + ' € ' + d.data.value + '</b>'; });

d3.select("#graficoFattCash svg") //seleziona un svg specifico

.datum( [ {

"label": "Fatturato da sede", "value": fattSede

}, {

"label": "Fatturato C&C", "value": fattCash

}

]) //assegna i dati

.transition().duration(350)

.call(chart);//richiama il grafico definito in precedenza

nv.utils.windowResize(chart.update);

//imposta il resize del grafico in base ai resize della pagina

return chart; });

}

Il grafico del fatturato per Super Business Unit viene costruito nello stesso modo, cambiando ovviamente i dati. Il Line Chart del fatturato suddiviso per mese viene costruito mediante le seguenti istruzioni:

function preparaDatiFattMese(dati, mesi) { var datiArray = [];

for (var i = 0; i < 12; i++) {

datiArray.push({ x: (i + 1), y: parseInt(dati[mesi[i + 1]]) }); }

return ([{values: datiArray, key: 'Fatturato', color: '#1f77b4'}]); }

function graficoFattMesi(dati) {

var mesi = ['', 'Gen', 'Feb', 'Mar', 'Apr', 'Mag', 'Giu', 'Lug', 'Ago',

'Set', 'Ott', 'Nov', 'Dic']; nv.addGraph(function () {

var chart = nv.models.lineChart() //specifica il tipo di grafico

.useInteractiveGuideline(true)

.showLegend(false) //non mostrare la legenda

.showYAxis(true) //mostra l'asse Y

.showXAxis(true) //mostra l'asse X

;

chart.xAxis //imposta l'asse X

.axisLabel('Mesi')

.tickValues([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]) .tickFormat(function (d) {

return mesi[d]; });

chart.yAxis //imposta l'asse Y

.tickFormat(function (d) { return "€ " + d3.format(".")(d) }) .showMaxMin(true)

;

d3.select('#graficoFattMese svg')

.datum(preparaDatiFattMese(dati, mesi))

.call(chart); //richiama il grafico nell'svg scelto

nv.utils.windowResize(chart.update); return chart;

}); }

Infine, l’istogramma o Bar Chart col fatturato diviso per Business Unit viene creato utilizzando la funzione seguente:

function preparaDatiGraficoBU(dati) { var valuesArray = [];

for (var key in dati) {

if (dati.hasOwnProperty(key)) { valuesArray.push({ "label": key, "value": parseInt(dati[key]) }); } }

return ([{ key: "Fatturato per BU", values: valuesArray }]); }

function graficoFattBU(dati) {

var datiPerGrafico = preparaDatiGraficoBU(dati); nv.addGraph(function () {

var chart = nv.models.discreteBarChart() // specifica il tipo di grafico

.x(function (d) { return d.label }) .y(function (d) { return d.value })

.showValues(true) //mostra i valori in cima a ogni barra

.valueFormat(function (d) { return "€ " + d3.format(".")(d) }); //formattazione

chart.yAxis //imposta l'asse Y

.tickFormat(function (d) { return "€ " + d3.format(".")(d) }) .showMaxMin(true); d3.select('#graficoFattBU svg') .datum(datiPerGrafico(dati)) .call(chart); nv.utils.windowResize(chart.update); return chart; }); }

Per identificare il cluster a cui appartiene il cliente ricercato, è stata realizzata anche la seguente query DMX per interrogare il modello di Data Mining. Nella visualizzazione della query, evidenziamo in blu i dati letti direttamente dal modello: essi sono stati concatenati con le stringhe che compongono la query.

SELECT Cluster(), ClusterProbability()

FROM [Clustering_EM_Scalabile] NATURAL PREDICTION JOIN (SELECT "

this.fattPerBu["BU1"] AS [BU1]

, this.apertoDaCash AS [Aperto Da Cash] , this.fattPerBu["BU2"] AS [BU2]

, this.cashDefault AS [Cash Default] , this.fattPerBu["BU3"] AS [BU3] , this.codCliente AS [Cod Cliente] , this.fattPerBu["BU4"] AS [BU4] , this.fattPerBu["BU5"] AS [BU5]

, this.dataCreazione AS [Data Creazione] , this.fattPerBu["BU6"] AS [BU6] , this.fattPerBu["BU7"] AS [BU7] , this.fattPerBu["BU8"] AS [BU8] , this.fattPerBu["BU9"] AS [BU9] , this.fattPerBu["BU10"] AS [BU10] , this.fattPerBu["BU11"] AS [BU11] , this.fattPerBu["BU12"] AS [BU12] , this.fattPerBu["BU13"] AS [BU13] , this.fattPerBu["BU14"] AS [JOPEN] , this.fattPerBu["BU15"] AS [BU15] , this.fattPerBu["BU16"] AS [BU16] , this.nazione AS [NAZIONE] , this.fattPerBu["BU17"] AS [BU17] , this.regione AS [REGIONE] , this.fattPerBu["BU18"] AS [BU18] , this.fattPerBu["BU19"] AS [BU19] , this.fattPerBu["BU20"] AS [BU20] , this.fattPerBu["BU21"] AS [BU21] , this.totFatt AS [Tot Fatt]

, this.totFattCeC AS [Tot Fatt Cec] , this.fattPerBu["BU22"] AS [BU22]" ) As t

La query DMX così composta, restituisce il cluster di appartenenza del cliente andando ad interrogare il modello di clustering creato con l’algoritmo Expectation Maximization. Tale risultato, viene anch’esso mostrato nella vista relativa al cliente, cliccando su “Analisi Cluster” dal menù laterale.

4 – Conclusioni, sviluppi futuri e ringraziamenti

Lo svolgimento del progetto formativo ha permesso sia di raggiungere l’obiettivo prefissato, ma anche di creare una nuova infrastruttura di Business Intelligence che rispecchiasse la struttura aziendale e che fosse pronta per analisi più avanzate. È importante, in fase conclusiva, evidenziare come si sia andata a formare quest’infrastruttura che ricordiamo, prima non esisteva. Oltre ad essere stato svolto rispettando le tempistiche prefissate, il progetto è stato completato interamente. Adesso che la struttura di base è stata costruita, potrebbe essere semplice importare nuovi campi e nuovi dati, creare analisi più complesse o diverse, come per esempio una Market Basket Analysis. Confrontando sempre più dati e mettendoli insieme in un’ottica BI, l’azienda potrebbe generare nuove informazioni a partire da quelle già in proprio possesso. Questo potrebbe far raggiungere un vantaggio competitivo enorme su un piano economico e innovativo, allargando le analisi e gli studi effettuati verso orizzonti sempre più interessanti.

Ringrazio l’azienda Sesa spa che mi ha permesso di effettuare il progetto formativo, in particolare Moreno Falchi e Andrea Biancalani che hanno seguito più da vicino lo svolgimento dell’analisi.

Ringrazio i miei genitori che si sono sempre interessati all’andamento dei miei studi, permettendomi di fare l’Università e aiutandomi sempre in ogni occasione. Ringrazio mia sorella Stefania che mi ha sempre ascoltata e supportata ogni volta che ne avevo bisogno, e spero di ricambiare il favore a mia volta. Ringrazio quindi tutta la mia famiglia, gli amici e i colleghi che conoscendo la mia situazione da studente-lavoratore non mi hanno mai fatto pesare nulla, e si sono sempre prodigati per aiutarmi in ogni modo. Un ultimo ringraziamento, ma non per questo meno importante, a colui che mi sopporta ogni giorno e che con me condivide la sua vita. Un compagno che sa sempre starmi accanto e soprattutto che sa come farmi sorridere: grazie Daniele.

5 – Bibliografia

Nel documento START: la decisione oltre al dato (pagine 56-66)

Documenti correlati