Come creare dei report widgets per la dashboard di October

Luca Benati

October CMS può essere usato anche come dashboard per la visualizzazione di dati, vediamo come sviluppare una report widget personalizzato.


Pubblicato da Luca Benati il 13 marzo 2020

Tra le innumerevoli possibilità di utilizzo che ci rende disponibili October, c'è anche quella di poterlo utilizzare sfruttando solo la dashboard per visualizzare grafici e dati provenienti da fonti esterne quali altri gestionali, ecommerce, IoT ecc. ecc.

Detto questo in questo articolo sfrutteremo i widget inclusi nel framwork di October per recuperare dati in formato JSON da una fonte esterna.

Dato il momento catartico che stiamo attraversando abbiamo deciso di sfruttare i dati froniti dalla protezione civile che ci vengono forniti già in un comodo formato JSON tramite comodi endpoints per merito di Enrico Candino, che approfitto per ringraziare, rendendoli disponibili sul suo repository Github COVID-19 Json API, semplificandoci di tanto la vita dato che tutti i dati disponibili sono in csv, formato decisamente meno comodo, pratico e leggibile da maneggiare per i nostri scopi.

Il servizio offre gli endpoints per i dati relativi al contagio da Coronavirus aggregati a livello mondiale e per ogni nazione, per l'Italia abbiamo un ulteriore dettaglio sia a livello regionale che a livvelo provinciale laddove i dati esistano, nel nostro caso utilizzeremo quest'ultimo disponibile qui:

https://enrichman.github.io/covid19/local/italy/full.json

Una volta sviluppato il nostro widget avremo la panoramica aggiornata di tutti i dati disponibili a livello nazionale, regionale e provinciale della situazione di contagio del Coronavisrus (Covid-19) del nostro paese.

Per cominciare a sviluppare il nostro report widget con October come prima cosa lo andiamo a registrare nel file Plugin.php presente nella root del nostro plugin in questo modo:



namespace Lbenati\Mioplugin;

use System\Classes\PluginBase;

class Plugin extends PluginBase
{
    public function registerComponents()
    {
    }

    public function registerSettings()
    {
    }

    public function registerReportWidgets()
    {
        return [
            'Lbenati\Mioplugin\ReportWidgets\CovidStats' => [
                'label'   => 'Statistiche Covid-19 (Italia)',
                'context' => 'dashboard'
            ]
        ];
    }
}


Per permettere al nostro plugin di registrare Widgets facciamo l'override del metodo pubblico registerReportWidget al quale facciamo ritornare una array di Widgets utilizzando la classe del widget come chiave e un array contenente la sua configurazione che consite nella definizione di una etichetta(label), un contesto(context) ed eventualmente i permessi(permissions) necessari per accedervi.

Ora che abbiamo registrato il widget, come si intuisce dal codice stesso, andiamo a creare un file nominato CovidStats.php dove creeremo la classe del widget che estende la classe ReportWidgetBase



namespace Lbenati\Mioplugin\ReportWidgets;

use Backend\Classes\ReportWidgetBase;

use GuzzleHttp\Client;

class CovidStats extends ReportWidgetBase
{

    public function defineProperties()
    {
        return [
            'title' => [
                'title'             => 'Statistiche Covid-19',
                'default'           => 'Statistiche Covid-19',
                'type'              => 'string',
                'validationPattern' => '^.+$',
                'validationMessage' => 'Errore nel titolo',
            ]
        ];
    }

    public function render()
    {
        $this->vars['stats'] = $this->getStats();

        return $this->makePartial('stats_widget');
    }

    public function getStats(){

        $client = new Client();

        $response = $client->request(
                               'GET', 
                               'https://enrichman.github.io/covid19/local/italy/full.json'
                           );

        $data = json_decode($response->getBody());

        return $data;
    }

}


La classe del Widget deve eseguire l'override del metodo render per renderizzare il widget stesso sfruttando un partial file, anche in questo caso October sfrutta una struttura predefinita di cartelle e di nomi di file, quindi andiamo a creare due nuove cartelle covidstats/partials all'interno della cartella reportwidgets, all'intero di quest'ultima creiamo il nostro file partial _stats_widget.htm.

Fatto questo dovremo trovarci con un percorso al file di questo tipo:

[ROOT]/plugins/lbenati/mioplugin/reportwidgets/covidstats/partials/_stats_widget.htm

All'interno del metodo render inseriamo tramite l'array $this->vars la chiave stats che come valore avrà i dati ritornati dall'endpoint, tutte le chiavi definite in $this->vars saranno disponibili nel file partial come variabili, quindi in questo caso nel partial avremo la variabile $stats contenente tutti i dati di nostro interesse.

In questo caso per valorizzarla chiamiamo un metodo interno alal classe che si occupa di eseguire la chiamata vera e propria e ritornari il risultato.

Come è possibile notare la chiamata viene eseguita sfruttandi la classe Client di Guzzle, un'ottima libreria per questo tipo di operazioni.

Guzzle però non è presente di default in October per poterlo avere disponibile però è sufficiente istallare il plugin Drivers che installera diverse librerie tra le quali anche Guzzle.

In un prossimo articolo vedremo come implementare una classe di questo tipo scaricandola e registrandola direttamente durante l'installazione del nostro plugin tramite composer.

Ora non resta che dare un bell'aspetto ai dati in modo da presentarli in maniera compresibile, immediata e di facile comprensione, per fare questo October ci viene in aiuto per l'ennesima volta con i suoi grafici e componenti predefiniti tutti consultabili su questa pagina.

Noi abbiamo sfruttato più che altro il grafico ad anello e i tab (anche i vari tipi di tab sono presenti sulla documentazione a questa pagina)

Per utilzzare il file partial lo richiamiamo nel all'interno del metodo render sfruttando il metodo makePartial in questo modo



$this->makePartial('stats_widget');


Come si nota anche in questo caso il file del partial comincia con un underscore che viene omesso quando utilizzato come parametro.

Inseriamo il seguente codice html nel file partial _stats_widget.htm creato precedentemente.



<div class="report-widget widget-covid-stats">

    <h3><?= e(trans($this->property('title'))) ?></h3>
    <div class="row" >
        <h3 class="m-b-0 ">Dati Nazionali (<?= $stats->stato; ?>)</h3>
        <hr  class="m-t-0 " />

        <div class="w-sm m-b" style="float:left;">
            <div class="scoreboard-item control-chart" data-control="chart-pie" data-size="200" data-center-text="<?= $stats->totale_casi?>">
                <ul>
                    <li data-color="#cc3300">Deceduti<span> <?=$stats->deceduti ?></span></li>
                    <li data-color="#95b753">Dimessi guariti<span> <?=$stats->dimessi_guariti ?></span></li>
                    <li data-color="#e5a91a">Attualmete positivi<span> <?=$stats->totale_attualmente_positivi?></span></li>

                </ul>
            </div>
        </div>
        <div class="w-lg m-b" style="float:left;">
            <div class="scoreboard">
                <div data-control="toolbar">

                    <div class="scoreboard-item title-value m-t">
                        <h4>Nuovi positivi</h4>
                        <p>
                        <i class="oc-icon-user-plus" style="color: #cc0000;"></i> 
                        <?= $stats->nuovi_attualmente_positivi; ?></p>
                    </div>

                    <div class="scoreboard-item title-value m-t">
                        <h4>Ospedalizzati</h4>
                        <p>
                        <i class="oc-icon-h-square" style="color: #1991d1;"></i> 
                        <?= $stats->totale_ospedalizzati; ?></p>
                    </div>

                    <div class="scoreboard-item title-value m-t">
                        <h4>Ricoverati con sintomi</h4>
                        <p>
                        <i class="oc-icon-bed" style="color: #e5a91a;"></i> 
                        <?= $stats->ricoverati_con_sintomi; ?></p>
                    </div>

                    <div class="scoreboard-item title-value m-t">
                        <h4>Terapia intensiva</h4>
                        <p>
                        <i class="oc-icon-heartbeat" style="color: #cc0000;"></i> 
                        <?= $stats->terapia_intensiva; ?></p>
                    </div>

                    <div class="scoreboard-item title-value m-t">
                        <h4>Isolamento domiciliare</h4>
                        <p>
                        <i class="oc-icon-home" style="color: #95b753;"></i> 
                        <?= $stats->isolamento_domiciliare; ?></p>
                    </div>

                    <div class="scoreboard-item title-value m-t">
                        <h4>Tamponi effettuati</h4>
                        <p>
                        <i class="oc-icon-medkit" style="color: #6CA6CD;"></i> 
                        <?= $stats->tamponi; ?></p>
                    </div>

                </div>
            </div>
        </div>
    </div>
    <div class="row">
        <h3 class="m-b-0 ">Dati per Regione</h3>
        <hr  class="m-t-0 " />

        <div class="control-tabs master-tabs m-t"   data-control="tab">
            <ul class="nav nav-tabs">
                <?php
                    $i = 0;
                    foreach($stats->regioni as $regione){
                        if($regione->denominazione_regione=='Friuli Venezia Giulia') continue;
                        $class = ($i==0) ? 'active' : '';
                ?>
                        <li class="<?= $class; ?>"><a href="#tab_<?= $regione->codice_regione; ?>"><?= $regione->denominazione_regione; ?></a></li>
                <?php
                    $i++;
                    }

                 ?>

            </ul>
            <h3 class=" m-t-lg m-b-0">Casi totali per provincia</h3>
            <div class="tab-content  ">
                <?php
                    $j = 0;
                    foreach($stats->regioni as $regione){
                        if($regione->denominazione_regione=='Friuli Venezia Giulia') continue;
                        $class = ($j==0) ? 'active' : '';
                ?>
                <div class="tab-pane tabs-offset <?= $class; ?>">
                        <div class="w-full m-l m-b" style="float:left;">

                            <div class="w-sm" style="float:left;" >
                                <div class="control-status-list">
                                    <ul>
                                        <?php

                                        if(isset($regione->province)){
                                            foreach($regione->province as $provincia){
                                         ?>
                                        <li>
                                            <span class="status-icon">
                                            <i class="icon-caret-right"></i></span>
                                            <span class="status-text"><?= $provincia->denominazione_provincia; ?></span>
                                            <span class="status-label primary"><?= $provincia->totale_casi; ?></span>
                                        </li>
                                        <?php
                                            }
                                        }
                                         ?>
                                </div>
                            </div>
                                <div class="w-lg" style="float:left;" >
                                    <div class="row m-l-md">

                                        <div class=" control-chart" data-control="chart-pie" data-size="100" data-center-text="<?= $regione->totale_casi?>">
                                            <ul>
                                                <li data-color="#cc3300">Deceduti<span><?= $regione->deceduti; ?></span></li>
                                                <li data-color="#95b753">Dimessi guariti<span><?= $regione->dimessi_guariti; ?></span></li>
                                                <li data-color="#e5a91a">Attualmete positivi<span><?= $regione->totale_attualmente_positivi; ?></span></li>
                                            </ul>
                                        </div>

                                    </div>
                                    <div class="row m-l-md">

                                        <div class="w-lg m-b" style="float:left;">
                                            <div class="scoreboard">
                                                <div data-control="toolbar">

                                                    <div class="scoreboard-item title-value m-t">
                                                        <h4>Nuovi positivi</h4>
                                                        <p><i class="oc-icon-user-plus" style="color: #cc0000;"></i> <?= $regione->nuovi_attualmente_positivi; ?></p>
                                                    </div>

                                                    <div class="scoreboard-item title-value m-t">
                                                        <h4>Ospedalizzati</h4>
                                                        <p><i class="oc-icon-h-square" style="color: #1991d1;"></i> <?= $regione->totale_ospedalizzati; ?></p>
                                                    </div>

                                                    <div class="scoreboard-item title-value m-t">
                                                        <h4>Ricoverati con sintomi</h4>
                                                        <p><i class="oc-icon-bed" style="color: #e5a91a;"></i> <?= $regione->ricoverati_con_sintomi; ?></p>
                                                    </div>

                                                    <div class="scoreboard-item title-value m-t">
                                                        <h4>Terapia intensiva</h4>
                                                        <p><i class="oc-icon-heartbeat" style="color: #cc0000;"></i> <?= $regione->terapia_intensiva; ?></p>
                                                    </div>

                                                    <div class="scoreboard-item title-value m-t">
                                                        <h4>Isolamento domiciliare</h4>
                                                        <p><i class="oc-icon-home" style="color: #95b753;"></i> <?= $regione->isolamento_domiciliare; ?></p>
                                                    </div>

                                                    <div class="scoreboard-item title-value m-t">
                                                        <h4>Tamponi effettuati</h4>
                                                        <p><i class="oc-icon-medkit" style="color: #6CA6CD;"></i> <?= $regione->tamponi; ?></p>
                                                    </div>

                                                </div>
                                            </div>
                                        </div>

                                    </div>
                                </div>
                        </div>

                        <div class="w-lg m-l m-b" style="float:left;">
                            <p class="pull-right" style="color:#aaa">
                                Ultimo aggiornamento: <?= date('d/m/Y H:i:s', (end($regione->ts))->t); ?>
                            </p>
                        </div>

                </div>
                <?php
                    $i++;
                    }

                 ?>

            </div>
        </div>
    </div>
</div>


A questo punto il metodo render ci restituirà a video il risultato ottenuto che non ci sembra per niente male; abbiamo una sezione superiore dove vediamo i dati generali a livello nazionale e nella parte sottostante tanti tab quante regioni sono presenti nei dati ricevuti e all'interno di ognuno di essi abbiamo inserito i dati generali della regione e la lista con il totale dei contagiati per ogni provincia, sempre se presente nel set di dati ricevuti.

Dashboard reports widgets October CMS

Anche stavolta October ci ha stupito per la semplicità e velocità con la quale è possibile produrre ottimi risultati.

Tra i tanti usi che mi vengono in mente uno che mi stuzzica particolarmente è quello di utilizzare la dashboard ad esempio per tenere monitorati sensori di domotica o prototipi IoT (qualcuno ha detto Arduino?) :)

Happy coding!


Lunga vita e prosperità

Ti interessa un argomento non trattato?