matthisk / django-jchart

📈 A Django package for plotting charts using the excellent Chart.JS library.
BSD 3-Clause "New" or "Revised" License
121 stars 23 forks source link

Passing data to class-based django-jchart views #20

Open joeDiHare opened 6 years ago

joeDiHare commented 6 years ago

Is there a way to pass custom data to the views instead of hard-coding it? The examples in this repo have hardcoded data like:

from jchart import Chart
from jchart.config import Axes, DataSet, rgba

class TimeSeriesChart(Chart):
    chart_type = 'line'

...
    def get_datasets(self, *args, **kwargs):
        data = [{'y': 0, 'x': '2017-01-02T00:00:00'}, {'y': 1, 'x': '2017-01-03T00:00:00'}, {'y': 4, 'x': '2017-01-04T00:00:00'}, {'y': 9, 'x': '2017-01-05T00:00:00'}, ...
        return [{
            'label': "My Dataset",
            'data': kwargs['data']
        }]

But I can't seem to overwrite get_datasets() to accept data from other scripts, eg. this won't work and will load empty data:

#views.py
class MainView(ListView, FormMixin):
    template_name = "src/template.html"
    model = DataClass

    def get_context_data(self, **kwargs):
        context = super(SingleFileView, self).get_context_data(**kwargs)

        lc = LineChart()
        lc.get_datasets(data=[loc.latitude for loc in locations_dis])
        context['line_chart'] = lc#data=[loc.latitude for loc in locations_dis])

        return context

class LineChart(Chart):
    chart_type = 'line'

    def get_datasets(self, **kwargs):
        try:
            data = kwargs['data']
        except KeyError:
            data = []
        return [{
            'label': "My Dataset",
            'data': data
        }]

Similarly, adding a init() will mess things up.

What is the django-jchart way to pass data do a view?

This gets more confusing when trying to implement the async loading. As per the doc, we should add in the url.py:

from src.views import LineChart

urlpatterns = [
    url(r'^src/', include('src.urls')),
    url(r'^admin/', admin.site.urls),
    url(r'^charts/line_chart/$', ChartView.from_chart(LineChart()), name='line_chart'),
]

but how to pass the data to LineChart()?

imdatacenters commented 6 years ago

That's a good question, and one that I struggled with Meta for awhile. I think the easiest way to just modify the init function so that you can provide a self.data and a self.labels. I do love this project, and use it often; I also understand why it was done this way, but the default behavior should allow for explicit attachment of data and labels for those of us getting both in a single api call.

intiocean commented 6 years ago

I define the below class and then initialise it with the data

class TimeSeriesChart(Chart):
    chart_type = 'line'
    scales = {
        'xAxes': [Axes(type='time', position='bottom')],
        }

    # make bezier curves pleasant but not too curvy - value found by experimentation
    options = {'elements': {'line': {'tension': 0.15}}}

    def __init__(self, data_labels, datasets, title=None, *args, **kwargs):
        """
        :param data_labels: A list of labels for each dataset
        :param datasets: A list of dicts in the format `{'x': <x-value>, 'y': <y-value>}` where the `x-value` is a date
        and the `y-value` is numeric
        """
        super(TimeSeriesChart, self).__init__(*args, **kwargs)
        self.data_labels = data_labels if data_labels else [None] * len(datasets)
        self.datasets = datasets
        if not data_labels:  # hide the legend
            self.legend = Legend(display=False)
        if title:
            self.title = Title(text=title, display=True)

    def get_datasets(self, **kwargs):
        res = []
        for i, (label, data) in enumerate(zip(self.data_labels, self.datasets)):
            res.append(DataSet(type='line', label=label, data=data, borderColor=colours[i % len(colours)], fill=False))
        return res