TC3.11 - New graphics widget can be created, Dashboard Widget Creation, by programming

×

Warning message

You can't delete this newsletter because it has not been sent to all its subscribers.

Test Case Title

TC3.11 - New graphics widget can be created, Dashboard Widget Creation, by programming

Goal

I can

  • Create new graphic Widget for the City Dashboards, for developers.
  • Create new graphic Widget for Banana Dashboard (AMMA, DevDash and ResDash tools).
  • Create a new SmartCity Map panel, by integrating Km4City Servicemap data.
  • Integrate achieved through SmartCity API calls before rendering data on the map and also on click events on specific service markers.
  • Integrate SmartCity Map view mutuated by the Dashboard Builder, for full integration, with the same interface, same icons and access to referral data directly.
  • Create a new Origin/Destination panel, derived from Sunburst panel by adding sum functionality for user selected field (in the original Sunburst panel, only the count functionality is present).
  • Create a new panel as a resource for monitoring Origin/Destination facets, selecting desired facet fields (for instance “Motivation”, “Agent” etc., see for further details the test case TC3.1 describing the AMMA toll usage details)

Prerequisites

The user is logged in the system and has the privileges as RootAdmin

Using a PC or Mobile with a web browser.

Access to the development environment for PHP, JavaScript, AngularJS.

The following functionalities are available only for specific Snap4city users with specific privileges.

Expected successful result

 

Steps

 

Please note, that recently it has been added a soution for creating custom synoptics/widgets exploiting graphics in SVG. Custom Synoptics and Widgets for Dashboards

please note that Custom Widgets can be directly controlled from IOT Applications to create event driven solutions: TC9.19: Custom Widgets / Synoptics controlled by IOT Applications

Please note that some of the following links could be accessible only for registered users.

In the following Test Case, how to build a new custom panel both in the Dashboard Builder, as well in the Banana dashboard environment (that is, for AMMA, DevDash and ResDash tools), is briefly explained.

Dashboard Builder: New graphic Widgets can be easily created using PHP programming. Dashboard Builder is open source and it is based on a well-known D3 graphic library, which can be used to easily create new graphic Widgets and be exploited in: Dashboards, embedded in mobile and web Apps and in web pages.

Banana Dashboards: allow developers to create custom panels (widgets): https://github.com/lucidworks/banana/wiki/Tutorial:-How-to-Build-a-Custom-Panel

Each new panel/widget is created by using HTML5 and JavaScript:

  • An HTML file for the editing dialog;
  • An HTML file for the widget/panel itself, which can include CSS code.

A JavaScript file, exploiting D3.js, Angular.js, etc. 


Example 1: Creating a new widget for City Dashboard

It is possible to manually define a new widget, for example to represent particular and specific metrics.

  1. Log-in in snap4city portal as RootAdmin privileges
  2. In the left main menu, click on the Settings item and then click on the Dashboard Widget Parameters sub-item. 

  1. Click the button on the top right to add a new widget.

 

The following are the parameters to be set up to define a new widget:

  • Widget name;
  • PHP filename: the PHP file name that contains the PHP code of the widget. The PHP files has to be in /widgets folder;
  • N.of managed metrics: number of metrics that the widget can show (please note that typically a widget shows just one metric but some widgets are present showing 2 or 3 metrics);
  • Metric type(s): one or more type of metrics separated by a pipe “|” char. For example “Intero|Percentuale|Float”;
  • Unique metric managed: in case a widget is specific for a particular metric, specify here the metric name (for example, this is the case of the Weather and SmartDS widgets;
  • Size limits in terms of number of cells within which the widget can be instantiated.

A template of the widget PHP code is available to facilitate the creation of new widgets. 

<?php

/* Dashboard Builder.

   Copyright (C) 2017 DISIT Lab https://www.disit.org - University of Florence

 

   This program is free software; you can redistribute it and/or

   modify it under the terms of the GNU General Public License

   as published by the Free Software Foundation; either version 2

   of the License, or (at your option) any later version.

   This program is distributed in the hope that it is useful,

   but WITHOUT ANY WARRANTY; without even the implied warranty of

   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the

   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License

   along with this program; if not, write to the Free Software

   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA. */

   include('../config.php');

   header("Cache-Control: private, max-age=$cacheControlMaxAge");

?>

 

<script type='text/javascript'>

    var colors = {

      GREEN: '#008800',

      ORANGE: '#FF9933',

      LOW_YELLOW: '#ffffcc',

      RED: '#FF0000'

    };

    var parameters = {};

 

    $(document).ready(function <?= $_GET['name'] ?>(firstLoad, metricNameFromDriver, widgetTitleFromDriver, widgetHeaderColorFromDriver, widgetHeaderFontColorFromDriver, fromGisExternalContent, fromGisExternalContentServiceUri, fromGisExternalContentField, fromGisExternalContentRange, /*randomSingleGeoJsonIndex,*/ fromGisMarker, fromGisMapRef)

    {

        <?php

            $titlePatterns = array();

            $titlePatterns[0] = '/_/';

            $titlePatterns[1] = '/\'/';

            $replacements = array();

            $replacements[0] = ' ';

            $replacements[1] = '&apos;';

            $title = $_GET['title'];

        ?>

               

        var hostFile = "<?= $_GET['hostFile'] ?>";

        var widgetName = "<?= $_GET['name'] ?>";

        var divContainer = $("#<?= $_GET['name'] ?>_content");

        var widgetContentColor = "<?= $_GET['color'] ?>";

        var widgetHeaderColor = "<?= $_GET['frame_color'] ?>";

        var widgetHeaderFontColor = "<?= $_GET['headerFontColor'] ?>";

        var nome_wid = "<?= $_GET['name'] ?>_div";

        var linkElement = $('#<?= $_GET['name'] ?>_link_w');

        var color = '<?= $_GET['color'] ?>';

        var fontSize = "<?= $_GET['fontSize'] ?>";

        var fontColor = "<?= $_GET['fontColor'] ?>";

        var timeToReload = <?= $_GET['freq'] ?>;

        var widgetPropertiesString, widgetProperties, thresholdObject, infoJson, styleParameters, metricType, metricData, pattern, totValues, shownValues,             descriptions, udm, threshold, thresholdEval, stopsArray, delta, deltaPerc, seriesObj, dataObj, pieObj, legendLength, rangeMin, rangeMax, widgetParameters = null;

        var metricId = "<?= $_GET['metric'] ?>";

        var elToEmpty = $("#<?= $_GET['name'] ?>_chartContainer");

        var url = "<?= $_GET['link_w'] ?>";

        var barColors = new Array();

        var embedWidget = <?= $_GET['embedWidget'] ?>;

        var embedWidgetPolicy = '<?= $_GET['embedWidgetPolicy'] ?>';          

        var headerHeight = 25;

       

        if(url === "null")

        {            url = null;        }

       

        if((embedWidget === true) && (embedWidgetPolicy === 'auto'))

        {            var showHeader = false;        }

        else

        {             var showHeader = true;         }

       

        //Specific functions definition

       

        //End functions definition

       

        setWidgetLayout(hostFile, widgetName, widgetContentColor, widgetHeaderColor, widgetHeaderFontColor, showHeader, headerHeight);

        if(firstLoad === false)

        {            showWidgetContent(widgetName);        }

        else

        {            setupLoadingPanel(widgetName, widgetContentColor, firstLoad);        }

        addLink(widgetName, url, linkElement, divContainer);

        $("#<?= $_GET['name'] ?>_titleDiv").html("<?= preg_replace($titlePatterns, $replacements, $title) ?>");

        widgetProperties = getWidgetProperties(widgetName);

       

        if((widgetProperties !== null) && (widgetProperties !== 'undefined'))

        {

            //Start of the eventual ad-hoc code based on widget properties

            styleParameters = getStyleParameters();

            widgetParameters = widgetProperties.param.parameters;

            manageInfoButtonVisibility(widgetProperties.param.infoMessage_w, $('#<?= $_GET['name'] ?>_header'));

 

            if(widgetParameters !== null)             {

                // Code to insert in case you want to use parameters of the widget

            }

            else              {

                // Code to insert in case you want to use parameters of the widget but they are not present

            }

           

            // End of the ad-hoc code based on widget properties

            metricData = getMetricData(metricId);

            if(metricData !== null)

            {

                if(metricData.data[0] !== 'undefined')

                {

                    if(metricData.data.length > 0)

                    {

                        // Start of the ad hoc code based on metric data

                        var metricType = metricData.data[0].commit.author.metricType;

                       

                        if(firstLoad !== false)

                        {                            showWidgetContent(widgetName);                        }

                        else

                        {                            elToEmpty.empty();                        }

                        //End of the ad hoc code based on metric data metrica

                    }

                    else

                    {                        showWidgetContent(widgetName);

                        $('#<?= $_GET['name'] ?>_noDataAlert').show();

                    } 

                }

                else

                {                    showWidgetContent(widgetName);

                    $('#<?= $_GET['name'] ?>_noDataAlert').show();

                }

            }

            else

            {

                showWidgetContent(widgetName);

                $('#<?= $_GET['name'] ?>_noDataAlert').show();

            }

        }

        else

        {            console.log("Error in the widget properties loading");        }

        startCountdown(widgetName, timeToReload, <?= $_GET['name'] ?>, metricNameFromDriver, widgetTitleFromDriver, widgetHeaderColorFromDriver, widgetHeaderFontColorFromDriver, fromGisExternalContent, fromGisExternalContentServiceUri, fromGisExternalContentField, fromGisExternalContentRange, fromGisMarker, fromGisMapRef);

    });

</script>

 

<div class="widget" id="<?= $_GET['name'] ?>_div">

    <div class='ui-widget-content'>

        <div id='<?= $_GET['name'] ?>_header' class="widgetHeader">

            <div id="<?= $_GET['name'] ?>_infoButtonDiv" class="infoButtonContainer">

               <a id ="info_modal" href="#" class="info_source"><i id="source_<?= $_GET['name'] ?>" class="source_button fa fa-info-circle" style="font-size: 22px"></i></a>

            </div>   

            <div id="<?= $_GET['name'] ?>_titleDiv" class="titleDiv"></div>

            <div id="<?= $_GET['name'] ?>_buttonsDiv" class="buttonsContainer">

                <div class="singleBtnContainer"><a class="icon-cfg-widget" href="#"><span class="glyphicon glyphicon-cog glyphicon-modify-widget" aria-hidden="true"></span></a></div>

                    <div class="singleBtnContainer"><a class="icon-remove-widget" href="#"><span class="glyphicon glyphicon-remove glyphicon-modify-widget" aria-hidden="true"></span></a></div>

            </div>

            <div id="<?= $_GET['name'] ?>_countdownContainerDiv" class="countdownContainer">

                <div id="<?= $_GET['name'] ?>_countdownDiv" class="countdown"></div>

            </div>  

        </div>

       

        <div id="<?= $_GET['name'] ?>_loading" class="loadingDiv">

            <div class="loadingTextDiv">

                <p>Loading data, please wait</p>

            </div>

            <div class ="loadingIconDiv">

                <i class='fa fa-spinner fa-spin'></i>

            </div>

        </div>

        

        <div id="<?= $_GET['name'] ?>_content" class="content">

            <p id="<?= $_GET['name'] ?>_noDataAlert" style='text-align: center; font-size: 18px; display:none'>No data available</p>

            <div id="<?= $_GET['name'] ?>_chartContainer" class="chartContainer"></div>

        </div>

    </div>   

</div>

 


Example 2: Sharing Widget on Dashboard Builder

Once a Dashboard Widget is created it can be loaded on the Dashboard Builder. In addition it could be also shared with other installations of the Dashboard Builder.

If they are developed according to the guidelines they can be loaded and shared among cities and among different Dashboard Builders. In the Dashboard Builder manual there is a section dedicated to creating new Widgets via PHP programming also reported in this document above.


Example 3: Creating a new widget for AMMA, DevDash and ResDash tools

  1. Download the template directory from the DISIT-GitHub repository here: https://github.com/disit/banana and copy it to your banana/src/app/panels/ and rename it to reflect the functionality of our panel. For example, in this tutorial, we will create a custom panel that draw a bar chart using D3.js, so let's name this panel, bar, and rename the directory accordingly to banana/src/app/panels/bar/.
  1.  Inside the bar panel directory, you should see three files:
  • editor.html – representing the panel's settings page;
  • module.html - visual representation on the dashboard;
  • module.js - panel's controller definition. 
  1. editor.html defines the settings for the bar panel. To keep it simple, it defines only two settings. First setting is the field for displaying values on the bar chart. And second setting is the number of rows that we want to retrieve results from SOLR.

Panel's settings page

  1. Next, let's edit module.html to display the bar chart on the dashboard.
    • It is also possible to include CSS code to customize the chart stylings. Here we will specify two stylings; one for 'rect' element to show the chart in steel blue color, and the other for 'text' element to show the text color on the chart in white.
       
  2. Note that in module.html <bar-chart> is an AngularJS directive that is defined in the next step.
    • This directive is responsible for drawing the chart (the presentation layer).

Our bar panel

  1. module.js is where the controller code lives.
    • It defines the default settings of the panel, the business logics (e.g. sending requests to SOLR and transforming responses from SOLR), and the panel's directive for drawing the chart.
       
  2. Since D3.js library is used to draw the chart,
    • it is needed to inject it into the module at the beginning of module.js. 
  3. Add the new panel name to panel_names array in banana/src/config.js 
    • so that it shows up in the drop-down box in the "Add Panel" interface.
       
  panel_names: [
      'bar',
      ...
  ]

Add panel page

Now you should be able to add the bar panel to the dashboard after refreshing your browser. If the panel does not show up, you may need to restart your web server that hosting Banana or clearing your browser's cache.

 


Example 4: Create a more complex panel with MAP in Developer Dashboard, exploiting Smart City API

In the newest version of the AMMA, DevDash and ResDash tools, a new SmartCity Map panel/widget has been developed and made available for use as a selectable panel from Banana menu. It has been derived from the Bettermap panel/widget by adding integration with SmartCity data related to services and locations retrieved in the map.

The SmartCity Map is aimed at showing enriched SmartCity information on selected geolocalized markers, provided that tooltips are valid Km4City Service URIs. Additional data, to be displayed as popup when clicking on single map markers, are retrieved on click events by calling the SmartCity API.

The new SmartCIty Map panel has been provided with the same interface, style and direct data referral of the Dasbhoard Builder, in order to achieve full integration with the other Dashboards view and data management.

For instance, in the new SmartCIty Map panel it is now possible to:

  • visualize different marker icons depending on the service type;
  • exploding additional information in a popup by clicking on a single marker (representing a service);
  • navigating the information contained in the popup, presenting three different tabs for: description and visualization of real-time data (if present).

In the following, the detailed steps to create a new SmartCityMap panel on the AMMA tool are described (the same steps can be equivalently followed to create a new SmartCityMap panel also on DevDash and ResDash tools): 

  1. Log in the Snap4City portal and click on the “Management” menu item on the left-side bar. A sub-menu opens, in which you can find a list of management tools, including “Traffic Analyzer: AMMA” (Application and MicroService Monitor and Analyser), “Data Analyzer: DevDash” (Development Dashboard) and “Backoffice Resource Analyzer: ResDash” (Resource Dashboard) tools. Click, for example, on the “Traffic Analyzer: AMMA”: 
     

  1. Select the smartcitymap panel from the widget selector when adding a new panel:
     

 

  1. Configure the map with desired coordinates and data fields:
     

 

  1. Visualize data with style and SmartCity-enriched data integrated and mutuated from Dashboard Builder: 

 

  1. Click on a single service marker, explode the informative popup and navigate SmartCity-enriched data: 

 

  1. If Real-Time data are available for a selected Service (see the “RT Data” tab in the popup), they can be displayed in the dedicated TimeTrend panel in the bottom (providing the possibility to navigate among “Last 4 hours”, “Last 24 hours”, “Last 7 days” and “Last 30 days” options):

 

 

 


Example 5: Create an Origin/Destination facet widget/panel for AMMA, DevDash and ResDash tools

In the newest version of the AMMA, DevDash and ResDash tools, the original Banana Sunburst widget/panel has been customized in order to allow sum statistics on selected fields, and it has been made available for use. This is useful, for instance, for monitoring in the AMMA tool the total amount of data traffic (and not only message/communication counts) from local IPs to external IPs, allowing also to facet through selected fields.

Such customization has been implemented since the original Sunburst panel provides only count functionality. In the following, the detailed steps to create a new Origin/Destionation sunburs panel on the AMMA tool are described (the same steps can be equivalently followed to create new Origin/Destionation sunburs panel also on DevDash and ResDash tools): 

  1. Log in the Snap4City portal and click on the “Management” menu item on the left-side bar. A sub-menu opens, in which you can find a list of management tools, including “Traffic Analyzer: AMMA” (Application and MicroService Monitor and Analyser), “Data Analyzer: DevDash” (Development Dashboard) and “Backoffice Resource Analyzer: ResDash” (Resource Dashboard) tools. Click, for example, on the “Traffic Analyzer: AMMA”:
     
     
  2. Select the Sunburst panel from the widget selector when adding a new panel:
     
     

As in the original Sunburst panel, you can select the facet fields inserting them as a facet input string. Furthermore, now you can select also the “sum(values)” mode, in addition to the “count” mode, as shown in the next figure. 

 

  1. Visualize the new custom Sunburst panel and interact with its faceting functionalities: 

      

From the above example detail, it can be noticed how Sunburst charts can be very useful for applying more than one facet filter at time, allowing a very deep level of drill-down on faceted data.