Widgets
===============================================

To place widgets in your dashboard you need to create a javascript file, where you call each widget that you need to place with the correct options, each widget provides an event that you can call in any javascript file to update the data.

For example if you create a number widget

.. code-block:: javascript

    var dashboard = new Dashboard();
    
    ...
    
    dashboard.addWidget('current_valuation_widget', 'Number', {
        getData: function () {
            $.extend(this.scope, {
                title: 'Current Valuation',
                moreInfo: 'In billions',
                updatedAt: 'Last updated at 14:10',
                value: '$35',
                detail: '64%'
            });
        }
    });

Then you can publish at any moment the event ``dashboard.publish('example_widget/getData')`` to get new data and update the widget.

Note that in this example the `getData` method will be executed each 1000 milliseconds because it is the default value of `interval` option in a `Number` widget.

Clock Widget
------------

This widget can display a specific day or an hour.

Options
~~~~~~~~~~~~

row
    Number of rows occupied by the widget. *(default: 1)*

col
    Number of columns occupied by the widget. *(default: 1)*

scope
    JSON object that represent the date and time in format 

    .. code-block:: javascript

        {
            time: 'hh:mm:ss',
            date: 'Month Day DD sYYYY'
        }

getData
    Function responsible to update the `scope` value, this function is executed each time interval specified in `interval` variable.
    You can rewrite this function to get data from an external source.
    *(default: return the browser time in a valid JSON format)*

getWidget
    Return the DOM element that represent the widget.

interval
    Actualization interval of widget scope on milliseconds. *(default: 500)*

Graph Widget
------------

This widget can display a value with an associated graph as a background.

Options
~~~~~~~~~~~~

row
    Number of rows occupied by the widget. *(default: 1)*

col
    Number of columns occupied by the widget. *(default: 2)*

scope
    JSON object in this format

    .. code-block:: javascript

        {
            data: [
                    {x: /x0/, y: /y0/},
                    {x: /x1/, y: /y1/}
                    ...
                  ],
            value: /string/,
            title: /string/,
            moreInfo: /string/,
            beforeRender: /function/,
            afterRender: /function/,
            xFormat: /function/,
            yFormat: /function/,
            properties: /object/,
        }

getData
    Function responsible to update the widget `scope`, this function is executed each time interval specified in `interval` variable.
    You can rewrite this function to get data from an external source.
    *(default: empty function)*

getWidget
    Return the DOM element that represents the widget.

interval
    Actualization interval of widget scope on milliseconds. *(default: 1000)*

Graph options
~~~~~~~~~~~~

To render the graph this widget use Rickshaw_ library, for now the config options are quite limited, if you need to be more specific you can overwrite the rivetsjs binder (rv-dashing-graph) or write a custom widget using this as a guide.

To configure the X and Y axis you must define custom methods *xFormat* and *yFormat* in the scope, also you can use the methods *beforeRender* and *afterRender* to execute arbitrary javascript before or after rendering, for example:


.. code-block:: javascript

    myDashboard.addWidget('AvailabilityAverageWidget', 'Graph', {
        color: 'steelblue',
        scope: {
            xFormat(n) {
                return '(' + n + ')';
            },
            afterRender: function() {
                alert('graph shown');
            }
        },
        getData: function () {
            var self = this;
            Dashing.utils.get('availability', function(data) {
                $.extend(self.scope, data);
            });
        },
        interval: 60000
    });

You can specify any properties that the graph constructor accepts in the `scope` object, for example a valid `scope` may be:

.. code-block:: javascript

    {
        data: [
            { x: 0, y: 29 },
            { x: 1, y: 42 },
            { x: 2, y: 12 }
        ],
        value: 12,
        title: 'Yeah!',
        moreInfo: 'Django Rocks',
        properties: {
            renderer: 'line',
            padding: {
                top: 0.1,
                right: 0.1
            }
        },
    }


.. _Rickshaw: https://tech.shutterstock.com/rickshaw/

Python Class
~~~~~~~~~~~~

This class helps return valid scope to be used by the widget, you can see the definition in GitHub__

.. _GraphWidgetDefinition: https://github.com/talpor/django-dashing/blob/59def5a53d5b76db232196f2fffacd49270b27e1/dashing/widgets.py#L94-118

__ GraphWidgetDefinition_

Here is an example of a graph widget where `value` is displayed the total number of Errands and in `data` returns an array with the last two hour of activity

.. code-block:: python
    
    from dashing.widgets import GraphWidget

    class HourlyErrandsWidget(GraphWidget):
        title = 'Hourly Errands'
        more_info = ''

        def get_value(self):
            return SearchQuerySet().filter(django_ct='errands.errand').count()

        def get_data(self):
            latest_hours = datetime.now() - timedelta(hours=2)
            latest_errands = SearchQuerySet().filter(
                                django_ct='errands.errand',
                                created__gt=latest_hours).values('created')
            intervals = []
            for errand in latest_errands:
                delta = datetime.now() - errand['created']

                for m in range(10, 120, 10):
                    if delta < timedelta(minutes=m):
                        intervals.append(13 - m/10)
                        break

            rlist = Counter([x for x in intervals])
            return [{'x': x, 'y': y} for x, y in rlist.most_common()]



List Widget
------------

This widget can display a list of elements with an associated value.

Options
~~~~~~~~~~~~

row
    Number of rows occupied by the widget. *(default: 2)*

col
    Number of columns occupied by the widget. *(default: 1)*

render
    Function responsible of modify the DOM elements of the widget.

scope
    JSON object in this format

    .. code-block:: javascript

        {
            data: [
                    {
                        label: /string/,
                        name: /string/
                    },
                    {
                        label: /string/,
                        name: /string/
                    },
                    ...
                  ],
            title: /string/,
            moreInfo: /string/,
            updatedAt: /string/
        }

getData
    Function responsible to update the `scope` value, this function is executed each time interval specified in `interval` variable.
    You can rewrite this function to get data from an external source.
    *(default: empty function)*

getWidget
    Return the DOM element that represent the widget.

interval
    Actualization interval of widget data on milliseconds. *(default: 10000)*

Python Class
~~~~~~~~~~~~

This class helps to return valid data to be use by the widget, you can see the definition in GitHub__

.. _ListWidgetDefinition: https://github.com/talpor/django-dashing/blob/59def5a53d5b76db232196f2fffacd49270b27e1/dashing/widgets.py#L67-91

__ ListWidgetDefinition_

Here's an example of a graph widget where in the `scope` returns an array with the messengers who have more requests

.. code-block:: python
    
    from dashing.widgets import ListWidget

    class ActiveMessengersWidget(ListWidget):
        title = 'Active Messengers'
        more_info = 'Those who have more requests'

        def get_updated_at(self):
            modified = SearchQuerySet().filter(
                django_ct='errand').order_by('-modified')[0].modified
            return u'Last updated {}'.format(modified)

        def get_data(self):
            messengers = SearchQuerySet().filter(
                                    django_ct='messengers', active=True)
            rlist = Counter([x for x in messengers])
            return [{'label':x, 'value':y} for x, y in rlist.most_common(20)]


Number Widget
-------------

This widget can display a value with other interesting information.

Options
~~~~~~~~~~~~

row
    Number of rows occupied by the widget. *(default: 1)*

col
    Number of columns occupied by the widget. *(default: 1)*

scope
    JSON object in this format

    .. code-block:: javascript

        {
            value: /string/,
            title: /string/,
            detail: /string/,
            moreInfo: /string/,
            updatedAt: /string/
        }

getData
    Function responsible to update the `scope` value, this function is executed each time interval specified in `interval` variable.
    You can rewrite this function to get data from an external source.
    *(default: empty function)*

getWidget
    Return the DOM element that represent the widget.

interval
    Actualization interval of widget scope on milliseconds. *(default: 1000)*

Python Class
~~~~~~~~~~~~

This class helps to return valid data to be used by the widget, you can see the definition in GitHub__

.. _NumberWidgetDefinition: https://github.com/talpor/django-dashing/blob/59def5a53d5b76db232196f2fffacd49270b27e1/dashing/widgets.py#L35-64

__ NumberWidgetDefinition_

Here is an example of a graph widget where in `value` is displayed the total of payments and in the detail and moreInfo shows other information of interest

.. code-block:: python
    
    from dashing.widgets import NumberWidget

    class PaymentsWidget(NumberWidget):
        title = 'Payments Customers'

        def get_value(self):
            return Payment.objects.all().count()
        
        def get_detail(self):
            payments = Payment.objects.all()
            total = len([x for x in payments if x.status == Payment.STATUS.waiting])
            return '{} to approve'.format(total)

        def get_more_info(self):
            payments = Payment.objects.all()
            total = len([x for x in payments if x.status == Payment.STATUS.rejected])
            return '{} rejected'.format(total)

Knob Widget
-------------

This widget can display a dial with other interesting information.

Options
~~~~~~~~~~~~

row
    Number of rows occupied by the widget. *(default: 1)*

col
    Number of columns occupied by the widget. *(default: 1)*

scope
    JSON object in this format

    .. code-block:: javascript

        {
            title: /string/,
            moreInfo: /string/,
            value: /number/,
            data: {
                // jquery knob options http://anthonyterrien.com/knob/
                angleArc: /number/,
                fgColor: /string/,
                angleOffset: /number/,
                displayInput: /boolean/,
                displayPrevious: /boolean/,
                step: /number/,
                min: /number/,
                max: /number/,
                readOnly: /boolean/
            }
        }

getData
    Function responsible to update the `scope` value, this function is executed each time interval specified in `interval` variable.
    You can rewrite this function to get data from an external source.
    *(default: empty function)*

getWidget
    Return the DOM element that represent the widget.

interval
    Actualization interval of widget scope on milliseconds. *(default: 10000)*

Python Class
~~~~~~~~~~~~

This class helps return valid scope to be used by the widget, you can see the definition in GitHub__

.. _KnobWidgetDefinition: https://github.com/talpor/django-dashing/blob/3dc6864569bf76d5391c33e5298c4c1a6be7da1e/dashing/widgets.py#L126-L155

__ KnobWidgetDefinition_

Map Widget
-------------

This widget display a google map widget with one or more markers grouped

You need to specify your GoogleMaps API key in ``WIDGET_CONFIGS`` for this widget to work properly, using the ``GOOGLE_MAPS_API_KEY`` key. For example:


.. code-block:: python

    DASHING = {
        'INSTALLED_WIDGETS': ('number', 'list', 'map',),
        'WIDGET_CONFIGS': {
                'map': {
                        'GOOGLE_MAPS_API_KEY': '<insert your GoogleMaps API key here>',
                },
        },
    }


Options
~~~~~~~~~~~~

row
    Number of rows occupied by the widget. *(default: 1)*

col
    Number of columns occupied by the widget. *(default: 1)*

scope
    JSON object in this format

    .. code-block:: javascript

        {
            theme: /black|blue|gray|green|orange|red|white|yellow/
            map: {
                zoom: /number/,
                doubleClickZoom: /boolean/,
                defaultUI: /boolean/,
                center: {
                    lat: /number/,
                    lng: /number/
                },
                markers: [
                    {
                        lat: /number/,
                        lng: /number/
                    },
                    {
                        lat: /number/,
                        lng: /number/
                    }
                ]
            }
        }

getData
    Function responsible to update the `scope` value, this function is executed each time interval specified in `interval` variable.
    You can rewrite this function to get data from an external source.
    *(default: empty function)*

getWidget
    Return the DOM element that represent the widget.

interval
    Actualization interval of widget scope on milliseconds. *(default: 0)*

Python Class
~~~~~~~~~~~~

This class helps return valid scope to be used by the widget, you can see the definition in GitHub__

.. _MapWidgetDefinition: https://github.com/talpor/django-dashing/blob/3dc6864569bf76d5391c33e5298c4c1a6be7da1e/dashing/widgets.py#L158-L194

__ MapWidgetDefinition_

Custom Widgets
===============================================

To make a custom widget you must create three static files to define configuration parameters and appearance. In addition, you can create a python class to communicate with the Django project.

To name your widgets should follow a naming convention were the name must be unique and searchable through the settings.

Static Files
-------------

Template File
~~~~~~~~~~~~

Its location should be ``<static_directory>/widgets/<widget_name>/<widget_name>.html`` this file describes its UI in plain HTML using the `Rivets.js conventions`__ to bind data to the script file.

.. _RivetsJsTemplates: http://rivetsjs.com/docs/guide/#usage-templates

__ RivetsJsTemplates_

For example ``{% static %}widgets/list/list.html`` looks like this:

.. code-block:: html

    <div>
        <h1>{ scope.title }</h1>
        <ul>
          <li rv-each-item="data">
            <span class="label">{ item.label }</span>
            <span class="value">{ item.value }</span>
          </li>
        </ul>
        <p class="more-info">{ moreInfo }</p>
        <p class="updated-at">{ updatedAt }</p>
    </div>

The classes are only for the stylesheet.

Style File
~~~~~~~~~~~~

Your location should be ``<static_directory>/widgets/<widget_name>/<widget_name>.css`` in this file defines the styles of widget.

Script File
~~~~~~~~~~~~

Your location should be ``<static_directory>/widgets/<widget_name>/<widget_name>.js`` in this file will be defined the configuration options and default values for the new widget, the idea is to create an object using the ``new`` keyword, then we define properties and methods using ``this`` keyword.

We must provide an ``__init__`` method were we bind the scope with the template and add to the dashboard, this function is quite similar in all widgets, then it is provided by ``Dashing.utils.widgetInit`` to facilitate implementation and improve the lecture of widgets, also must provide a ``scope`` element which will be binded to the template, and a ``getData`` function will surely be the to be overwritten to obtain relevant data as required,

For example ``{% static %}widgets/list/list.js`` looks like this:

.. code-block:: javascript

    /* global Dashboard */

    Dashing.widgets.List = function (dashboard) {
        var self = this,
            widget;
        this.__init__ = Dashing.utils.widgetInit(dashboard, 'list');
        this.row = 2;
        this.col = 1;
        this.scope = {};
        this.getWidget = function () {
            return widget;
        };
        this.getData = function () {};
        this.interval = 10000;
    };

If we want to initialize widget with the scope we can write:


.. code-block:: javascript
    
    ...
        this.col = 1;
        this.socpe = {
            title: 'Default Title',
            moreInfo: 'No data to display'
        };
        this.getWidget = function () {
    ...


Python Class
-------------

Surely in many cases it may be necessary to give the option to get some Dajngo project data into the widget, for this dashing has a Widget class that can be inherited to deliver properly serialized data, subsequently serving data using the dashing router.

For example ListWidget in ``dashing/widgets.py`` looks like this:

.. code-block:: python

    class ListWidget(Widget):
        title = ''
        more_info = ''
        updated_at = ''
        data = []

        def get_title(self):
            return self.title

        def get_more_info(self):
            return self.more_info

        def get_updated_at(self):
            return self.updated_at

        def get_data(self):
            return self.data

        def get_context(self):
            return {
                'title': self.get_title(),
                'moreInfo': self.get_more_info(),
                'updatedAt': self.get_updated_at(),
                'data': self.get_data(),
            }

If you develop your widget with python classes it is necessary that you distribute it via PyPI

Distribution
------------

To distribute a widget you have two options. The fastest way is through  Django Dashing Channel but it is a bit limited, and through PyPI, a bit trickier to pack but you have more options when developing the widget.

Via Django Dashing Channel
~~~~~~~~~~~~

Using this distribution method the users will only have to add the widget name on ``INSTALLED_WIDGETS`` then load the dashboard, this locates the static files from a remote location (specified in the preconfigured repository), if the user creates a copy of the files on your local static directory then these will open locally.

You will have to host your files into a CDN, I recommend creating a github project and use RawGit_ to serve through MaxCDN_, you can take `dj-dashing-weather-widget`__ project as a guide.

.. _RawGit: https://rawgit.com/
.. _MaxCDN: https://www.maxcdn.com/
.. _WeatherWidget: https://github.com/individuo7/dj-dashing-weather-widget
__ WeatherWidget_

Finally to publish your widget in Django Dashing Channel you need to make a fork of `django-dashing-channel`__, add your repository to repositories.json and send a pull request. In the repository root will be sought the widget static files (.js .css and .html)

You should create a README file for installation instructions.

.. _DashingChannel: https://github.com/talpor/django-dashing-channel
__ DashingChannel_

PyPI Package
~~~~~~~~~~~~

If your widget requires python code or you just want to provide an easy way to get the widget locally then a PyPI package is the way to go.

As a requirement it is necessary follow the widgets naming convention (`see static files`__). To create a PyPI package `see the documentation <https://docs.python.org/2/distutils/packageindex.html>`_, and should create a README file for installations instructions.

This is not excluding the previous way, you could create a minimalist version of your widget and upload it to django-dashing-channel and in the project instructions show how to install the PyPI version

.. _WidgetsNamingConvention: #static-files
__ WidgetsNamingConvention_
