├── .gitignore
├── .travis.yml
├── LISCENSES.txt
├── MANIFEST.in
├── README.md
├── chartjs_engine
├── __init__.py
├── admin.py
├── apps.py
├── models.py
├── templates
│ └── chartjs_engine
│ │ └── chart.html
├── tests.py
├── urls.py
└── views
│ ├── __init__.py
│ ├── bar.py
│ ├── base.py
│ ├── engine.py
│ ├── line.py
│ └── pie_doughnut.py
├── manage.py
├── project
├── __init__.py
├── settings.py
├── urls.py
└── wsgi.py
├── requirements.txt
└── setup.py
/.gitignore:
--------------------------------------------------------------------------------
1 | *.pyc
2 | dist/*
3 | django_chartjs_engine.egg-info/*
4 | db.sqlite3
5 |
6 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: python
2 | python:
3 | - "2.7"
4 |
5 | before_script:
6 | - pip install python-coveralls
7 |
8 | script:
9 | - coverage run --source=chartjs_engine manage.py test chartjs_engine
10 |
11 | after_success:
12 | - coveralls
13 |
--------------------------------------------------------------------------------
/LISCENSES.txt:
--------------------------------------------------------------------------------
1 | Copyright 2016 Jeff Willette (jrwillette88@gmail.com)
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4 |
5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6 |
7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
8 |
--------------------------------------------------------------------------------
/MANIFEST.in:
--------------------------------------------------------------------------------
1 | include chartjs_engine/*
2 | include chartjs_engine/templates/chartjs_engine/*
3 | include chartjs_engine/views/*
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | [](https://travis-ci.org/deltaskelta/django-chartjs-engine)
2 |
3 | [](https://coveralls.io/github/deltaskelta/django-chartjs-engine?branch=master)
4 |
5 | ## Chartjs_engine
6 |
7 | `chartjs_engine` aims to provide a django app that will produce [chartjs](http://www.chartjs.org/)
8 | charts by instantiating a chart engine which renders the chart to a string of html and `'
36 | ```
37 |
38 | You must also add the `chartjs` script to your html file somewhere. It has been
39 | excluded from the template in order to allow it to be loaded exactly once. (Each
40 | page may have multiple charts, and if it was included in the template it would load
41 | once for every chart)
42 |
43 | In order to include it in the response, it is best to put the script source in settings and then
44 | add it to the response later.
45 |
46 | ## Method 1: Pass data to the Engine And Return Chart as Response
47 |
48 | ```python
49 | from django.http import HttpResponse
50 | from chartjs_engine.views.engine import Engine
51 | from django.conf import settings
52 |
53 |
54 | def chart_view(request):
55 | """construct data form a source and send it to the chart engine"""
56 |
57 | chart_setup = {
58 | 'chart_name': 'testchart',
59 | 'chart_type': 'line',
60 | 'chart_labels': ['the', 'labels'],
61 | 'options': 'options',
62 | 'datasets': {
63 | 'data1': [1, 2],
64 | 'data2': [3, 4],
65 | }
66 | }
67 |
68 | engine = ChartEngine(**chart_setup)
69 | chart = engine.make_chart()
70 | return HttpResponse(settings.CHARTJS_SCRIPT + chart)
71 | ```
72 |
73 | ## Method 2: Creating Custom Markup on a Database Object
74 |
75 | If you have a database object which is returned in a response (like a blog post)
76 | you can make some custom markup which can be substituted for chart html when the
77 | view is loaded.
78 |
79 | #### Example:
80 |
81 | ##### settings.py
82 | ```python
83 | CHARTJS_REGEX = re.compile(r'(\[chartjs\].*?\[/chartjs\])', re.DOTALL)
84 | ```
85 |
86 | The above regular expression will capture everything in between and including
87 | `[chartjs]` and `[/chartjs]` tags in the database.
88 |
89 | Then, with the following model method, you could turn some markup (like the example
90 | below into a chartjs javascript chart...
91 |
92 | ```
93 | [chartjs]
94 | name: testchart
95 | type: line
96 | labels: the, labels
97 | data-foo: 1, 2
98 | data-bar: 3, 4
99 | [/chartjs]
100 | ```
101 |
102 | ##### models.py
103 | ```python
104 | class BlogPost(models.Model):
105 | """A model that has everything needed for a blogpost"""
106 |
107 | # All of the model fields would be here
108 | ...
109 |
110 | def insert_chart(self):
111 | """
112 | Finds the chartjs data by regex in a post and calls the function to
113 | replace markup with chart javascript in the post.
114 | """
115 | chart_data = {}
116 | # re.DOTALL makes "." accept all characters. chart_data var is list of matches.
117 | pattern = settings.CHARTJS_REGEX
118 | #Finding all the chartjs markup and iterating to parse each one
119 | markup_data = re.findall(pattern, self.post)
120 | if markup_data:
121 | # Adding the chartjs javascript library (exactly once) to the html
122 | self.post = settings.CHARTJS_SCRIPT + self.post
123 | for data in markup_data:
124 | # Regex captures the "[chartsjs]" tags, omitting them...
125 | data = data.split('\r\n')[1:-1]
126 | # name and type are in list as "name: the-name" so split/strip whitespace
127 | chart_data['chart_name'] = data[0].split(':')[1].strip()
128 | chart_data['chart_type'] = data[1].split(':')[1].strip()
129 |
130 | # Split label by item delimited by "," stripped of whitespace.
131 | chart_data['chart_labels'] = [item.strip() for item in \
132 | data[2].split(':')[1].split(',')]
133 |
134 | chart_data['datasets'] = {}
135 | # data[3:] is going to be chart data so split/strip and convert to json.
136 | for data_set in data[3:]:
137 | # split by ':', then [0] index will be the dataset title
138 | d = data_set.split(':')
139 | chart_data['datasets'][d[0]] = [item.strip() for item in \
140 | d[1].split(',')]
141 |
142 | # Left for future use, this may cause errors with chart specific
143 | # options in views if changed
144 | chart_data['options'] = 'options'
145 |
146 | # Instantiate the ChartEngine and make the chart
147 | engine = ChartEngine(**chart_data)
148 | # replace 1 match in the post with the chart javascript
149 | self.post = re.sub(pattern, engine.make_chart(), self.post, count=1)
150 | ```
151 |
--------------------------------------------------------------------------------
/chartjs_engine/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jeffwillette/django-chartjs-engine/2658d7840e1e92ef4a5170490de0688baa6b8695/chartjs_engine/__init__.py
--------------------------------------------------------------------------------
/chartjs_engine/admin.py:
--------------------------------------------------------------------------------
1 | from django.contrib import admin
2 |
3 | # Register your models here.
4 |
--------------------------------------------------------------------------------
/chartjs_engine/apps.py:
--------------------------------------------------------------------------------
1 | from __future__ import unicode_literals
2 |
3 | from django.apps import AppConfig
4 |
5 |
6 | class ChartsConfig(AppConfig):
7 | name = 'chartjs_engine'
8 |
--------------------------------------------------------------------------------
/chartjs_engine/models.py:
--------------------------------------------------------------------------------
1 | from __future__ import unicode_literals
2 |
3 | from django.db import models
4 |
5 | # Create your models here.
6 |
--------------------------------------------------------------------------------
/chartjs_engine/templates/chartjs_engine/chart.html:
--------------------------------------------------------------------------------
1 | {% load static %}
2 |
3 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/chartjs_engine/tests.py:
--------------------------------------------------------------------------------
1 | from mock import patch
2 | from django.test import TestCase, Client
3 | from django.template.loader import render_to_string
4 | from .views.engine import *
5 |
6 | # Create your tests here.
7 | class ChartTests(TestCase):
8 | """Tests for the blog views"""
9 |
10 | def setUp(self):
11 | """Making tests for the charting app. Uses another chartjs app installed to virtualenv"""
12 | self.chart = Chart(
13 | chart_type='line',
14 | chart_name='this',
15 | options={'options': 'options'},
16 | chart_labels=['the', 'labels'],
17 | datasets={'data': (1, 2), 'data2': (3, 4)})
18 |
19 | def test_chart_class(self):
20 | """Testing that the chart class instantiates"""
21 | self.assertEqual(self.chart.chart_type, 'line')
22 |
23 | def test_random_color(self):
24 | """Testing that the random color generator returns a tuple of length 2"""
25 | colors = self.chart.random_color()
26 | self.assertEqual(len(colors), 2)
27 |
28 | def test_make_js(self):
29 | """
30 | Testing that the exception is raised when I call the Chart class make_js
31 | method
32 | """
33 | with self.assertRaises(Exception):
34 | self.chart.render_template()
35 |
36 | def test_exception_when_kwargs_missing(self):
37 | """Testing that there is an exception rasied when all kwargs not present"""
38 | with self.assertRaises(Exception):
39 | chart = Chart()
40 |
41 |
42 |
43 | class LineChartTests(TestCase):
44 | """Tests for the different chart class types"""
45 |
46 | def setUp(self):
47 | """Setting up what needs to be done for the line charts"""
48 | self.chart = LineChart(
49 | chart_type='line',
50 | chart_name='the_chart',
51 | chart_labels=['the', 'labels'],
52 | datasets={'data': (1, 2), 'data2': (3, 4)})
53 |
54 | def test_get_options(self):
55 | """Testing the get options method"""
56 | self.assertFalse(self.chart.options)
57 | options = self.chart.get_options()
58 | self.assertTrue(options)
59 |
60 | def test_get_data(self):
61 | """Testing that get data returns everything equal to the input"""
62 | data = self.chart.get_data()
63 | keys = ['label', 'backgroundColor', 'borderColor', 'data']
64 | for k in keys:
65 | for d in data['datasets']:
66 | self.assertTrue(d[k])
67 |
68 | def test_make_context(self):
69 | """Testing that context returns"""
70 | context = self.chart.make_context()
71 | keys = ['chart_type', 'chart_name', 'data', 'options']
72 | for k in keys:
73 | self.assertTrue(context[k])
74 |
75 | @patch('chartjs_engine.views.line.render_to_string')
76 | def test_render_template_method(self, mock_rts):
77 | """Testing that everything has rendered correctly 'rts' is render_to_string"""
78 | chart = self.chart.to_string()
79 | mock_rts.assert_called()
80 |
81 |
82 |
83 | class BarChartTests(TestCase):
84 | """Testing the bar chart class"""
85 |
86 | def setUp(self):
87 | """Setting up the options for a bar chart"""
88 |
89 | self.chart_type = 'bar'
90 | self.chart_name = 'the_chart'
91 | self.chart_labels = ['the', 'labels']
92 | self.datasets = {'data': (1, 2), 'data2': (3, 4)}
93 | # For testing bar chart behavior with single dataset
94 | self.datasets2 = {'data': (1, 2)}
95 |
96 | self.chart = BarChart(
97 | chart_type=self.chart_type,
98 | chart_name=self.chart_name,
99 | chart_labels=self.chart_labels,
100 | datasets=self.datasets)
101 |
102 | self.single_dataset_chart = BarChart(
103 | chart_type=self.chart_type,
104 | chart_name=self.chart_name,
105 | chart_labels=self.chart_labels,
106 | datasets=self.datasets2)
107 |
108 | def test_get_options(self):
109 | """Testing the get options method"""
110 | self.assertFalse(self.chart.options)
111 | options = self.chart.get_options()
112 | self.assertTrue(options)
113 |
114 | def test_get_data(self):
115 | """Testing that get data returns everything equal to the input"""
116 | data = self.chart.get_data()
117 | keys = ['label', 'backgroundColor', 'borderColor', 'borderWidth', 'data']
118 | for k in keys:
119 | for d in data['datasets']:
120 | self.assertTrue(d[k])
121 |
122 | def test_make_context(self):
123 | """Testing that context returns"""
124 | context = self.chart.make_context()
125 | keys = ['chart_type', 'chart_name', 'data', 'options']
126 | for k in keys:
127 | self.assertTrue(context[k])
128 |
129 | @patch('chartjs_engine.views.bar.render_to_string')
130 | def test_to_string_method(self, mock_rts):
131 | """Testing rendering the bar chart to a template"""
132 | chart = self.chart.to_string()
133 | mock_rts.assert_called()
134 |
135 | def test_one_color_if_multiple_datasets(self):
136 | """
137 | Testing that there will be one color per dataset when multiple datasets
138 | are present
139 | """
140 | chart = self.chart.to_string()
141 | for ds in self.chart.data['datasets']:
142 | self.assertEqual(ds['backgroundColor'][0], ds['backgroundColor'][1])
143 | self.assertEqual(ds['borderColor'][0], ds['borderColor'][1])
144 |
145 | def test_multiple_colors_if_one_dataset(self):
146 | """Testing that there will be multiple colors if there is only one dataset"""
147 | chart = self.single_dataset_chart.to_string()
148 | for ds in self.single_dataset_chart.data['datasets']:
149 | self.assertNotEqual(ds['backgroundColor'][0], ds['backgroundColor'][1])
150 | self.assertNotEqual(ds['borderColor'][0], ds['borderColor'][1])
151 |
152 |
153 |
154 | class PieDoughnutChartTests(TestCase):
155 | """Testing that Pie and Doughnut Charts render correctly"""
156 |
157 | def setUp(self):
158 | """Setting up multiple and single datasets"""
159 | self.chart = PieDoughnutChart(
160 | chart_type='pie',
161 | chart_name='the_chart',
162 | chart_labels=['the', 'labels'],
163 | datasets={'data': (1, 2), 'data2': (3, 4)})
164 |
165 | self.doughnut_chart = PieDoughnutChart(
166 | chart_type='doughnut',
167 | chart_name='the_chart',
168 | chart_labels=['the', 'labels'],
169 | datasets={'data': (1, 2)})
170 |
171 | self.single_dataset_chart = PieDoughnutChart(
172 | chart_type='pie',
173 | chart_name='the_chart',
174 | chart_labels=['the', 'labels'],
175 | datasets={'data': (1, 2)})
176 |
177 | def test_get_options(self):
178 | """Testing the get options method"""
179 | self.assertFalse(self.chart.options)
180 | options = self.chart.get_options()
181 | self.assertFalse(options)
182 |
183 | def test_get_data(self):
184 | """Testing that get data returns everything equal to the input"""
185 | data = self.single_dataset_chart.get_data()
186 | keys = ['label', 'backgroundColor', 'hoverBackgroundColor', 'data']
187 | for k in keys:
188 | for d in data['datasets']:
189 | self.assertTrue(d[k])
190 |
191 | def test_make_context(self):
192 | """Testing that context returns"""
193 | context = self.chart.make_context()
194 | keys = ['chart_type', 'chart_name', 'data', 'options']
195 | for k in keys:
196 | self.assertTrue(context[k])
197 |
198 | @patch('chartjs_engine.views.pie_doughnut.render_to_string')
199 | def test_one_dataset_returns_chart(self, mock_rts):
200 | """Testing that one dataset will return a chart"""
201 | chart = self.single_dataset_chart.to_string()
202 | mock_rts.assert_called()
203 |
204 | @patch('chartjs_engine.views.pie_doughnut.render_to_string')
205 | def test_doughnut_chart_returns_chart(self, mock_rts):
206 | """Testing that passing in settings for doughnut chart returns a chart"""
207 | chart = self.doughnut_chart.to_string()
208 | mock_rts.assert_called()
209 |
210 | def test_two_dataset_returns_error_as_string(self):
211 | """Testing that two datasets will return an error as a string."""
212 | with self.assertRaises(Exception):
213 | self.chart.to_string()
214 |
215 |
216 | class ChartEngineTests(TestCase):
217 | """Testing that the chart engine works with proper input"""
218 |
219 | def setUp(self):
220 | """Setting up the data that will be plugged into the charts."""
221 |
222 | self.chart_setup = {
223 | 'chart_name': 'testchart',
224 | 'chart_type': 'line',
225 | 'chart_labels': ['the', 'labels'],
226 | 'options': 'options',
227 | 'datasets': {
228 | 'data1': [1, 2],
229 | 'data2': [3, 4],
230 | }
231 | }
232 |
233 | def test_make_line_chart(self):
234 | """Testing that the engine delivers the charts as expected"""
235 | self.engine = ChartEngine(**self.chart_setup)
236 | self.assertIn('line', self.engine.make_chart())
237 |
238 | def test_make_bar_chart(self):
239 | """Testing that the engine delivers charts with "bar" as the title"""
240 | self.chart_setup['chart_type'] = 'bar'
241 | self.engine = ChartEngine(**self.chart_setup)
242 | self.assertIn('bar', self.engine.make_chart())
243 |
244 | def test_make_pie_chart(self):
245 | """Testing that the engine delivers charts with pie as the type"""
246 | self.chart_setup['chart_type'] = 'pie'
247 | self.chart_setup['datasets'] = {'data1': [1, 2]}
248 | self.engine = ChartEngine(**self.chart_setup)
249 | self.assertIn('pie', self.engine.make_chart())
250 |
251 | def test_make_doughnut_chart(self):
252 | """Testing that the engine delivers charts with dughnut as the type"""
253 | self.chart_setup['chart_type'] = 'doughnut'
254 | self.chart_setup['datasets'] = {'data1': [1, 2]}
255 | self.engine = ChartEngine(**self.chart_setup)
256 | self.assertIn('doughnut', self.engine.make_chart())
257 |
--------------------------------------------------------------------------------
/chartjs_engine/urls.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jeffwillette/django-chartjs-engine/2658d7840e1e92ef4a5170490de0688baa6b8695/chartjs_engine/urls.py
--------------------------------------------------------------------------------
/chartjs_engine/views/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jeffwillette/django-chartjs-engine/2658d7840e1e92ef4a5170490de0688baa6b8695/chartjs_engine/views/__init__.py
--------------------------------------------------------------------------------
/chartjs_engine/views/bar.py:
--------------------------------------------------------------------------------
1 | """
2 | The bar chart class
3 | """
4 | from .base import Chart
5 | from django.template.loader import render_to_string
6 | import json
7 |
8 |
9 | class BarChart(Chart):
10 | """
11 | Making the JSON data necessary for a bar chart
12 | DOCS: http://www.chartjs.org/docs/#bar-chart-introduction
13 | """
14 |
15 | def get_options(self):
16 | """Gets the options for the chart"""
17 | self.options = {
18 | 'scales': {
19 | 'yAxes': [{
20 | 'ticks': {
21 | 'beginAtZero': True
22 | }
23 | }]
24 | }
25 | }
26 | return self.options
27 |
28 | def get_data(self):
29 | """Populating self.data with self.datasets"""
30 |
31 | # self.data['labels'] already set in the Chart base class
32 | self.data['datasets'] = []
33 |
34 | for i, name in enumerate(self.datasets):
35 |
36 | if len(self.datasets) == 1:
37 | # If there is one dataset, each label will have a different color
38 | self.colors = [self.random_color() for d in self.datasets[name]]
39 | else:
40 | # If there is more than one dataset, each dataset will have its own
41 | # color which stays the same throughout the chart.
42 | self.rand_color = self.random_color()
43 | self.colors = [self.rand_color for d in self.datasets[name]]
44 |
45 | self.data['datasets'].append({
46 | 'label': name,
47 | 'backgroundColor': [color[0] for color in self.colors],
48 | 'borderColor': [color[1] for color in self.colors],
49 | 'borderWidth': 3,
50 | 'data': self.datasets[name],
51 | })
52 | return self.data
53 |
54 | def make_context(self):
55 | """Making the context to be returned to the render functions"""
56 | self.context = {
57 | 'chart_type': self.chart_type,
58 | 'chart_name': self.chart_name,
59 | 'data': json.dumps(self.data),
60 | 'options': json.dumps(self.options)
61 | }
62 | return self.context
63 |
64 | def to_string(self):
65 | """Rendering bar chart data to a template, returning string"""
66 | self.get_options()
67 | self.get_data()
68 | self.make_context()
69 | return render_to_string('chartjs_engine/chart.html', self.context)
70 |
--------------------------------------------------------------------------------
/chartjs_engine/views/base.py:
--------------------------------------------------------------------------------
1 | """
2 | The base chart class that all the chart type cublcasses inherits from
3 |
4 | TODO:
5 |
6 | 1. Construct the data in JSON and make it accpet all types of charts.
7 | 2. Move [chartjs] markup splitting to the model so chart engine can be agnostic,
8 | and just take chart settings as input
9 | 3. split each chart type class into smaller methods
10 | """
11 | import random
12 |
13 |
14 |
15 | class Chart(object):
16 | """A base class for charts, init will store all the data needed for subclasses"""
17 |
18 | def __init__(self, chart_type=None, chart_name=None, options=None, \
19 | chart_labels=None, datasets=None):
20 | """
21 | Setting all of the settings that will be needed in the charts subclasses
22 | """
23 | self.chart_type = chart_type
24 | # datasets will be put into self.data in each charts get_data method
25 | self.datasets = datasets
26 | self.chart_name = chart_name
27 | self.options = options
28 | self.data = {'labels': chart_labels,}
29 | # Figure out how to access the kwargs as a list and make sure none of them
30 | # are None. Raise exception if they are and test.
31 | if not all([self.chart_type, self.chart_name, self.data['labels'], \
32 | self.datasets]):
33 | raise Exception(
34 | "Chart class needs to have all keyword arguments specified")
35 |
36 |
37 | def random_color(self):
38 | """Generates a random javascript valid rgba color for each set in datasets
39 | return: tuple of javascript rgba color strings."""
40 |
41 | red, green, blue = random.randint(0, 255), \
42 | random.randint(0, 255), random.randint(0, 150)
43 |
44 | return ("rgba(%s, %s, %s, .4)" % (red, green, blue), \
45 | "rgba(%s, %s, %s, 1)" % (red, green, blue))
46 |
47 |
48 | def to_string(self):
49 | """
50 | This method is meant to be overridden in the child chart type classes
51 | """
52 | raise Exception("to_string method has not been overridden")
53 |
54 |
55 | '''
56 | class RadarChart(Chart):
57 | """
58 | Making the JSON data necessary for a radar chart
59 | DOCS: http://www.chartjs.org/docs/#radar-chart-introduction
60 | """
61 | pass
62 |
63 |
64 | class PolarAreaChart(Chart):
65 | """
66 | Making JSON data necessary for a polar area chart
67 | DOCS: http://www.chartjs.org/docs/#polar-area-chart-introduction"""
68 | pass
69 |
70 |
71 | class BubbleChart(Chart):
72 | """
73 | Making the JSON necessary for a bubble chart
74 | DOCS: http://www.chartjs.org/docs/#bubble-chart-introduction
75 | """
76 | pass
77 | '''
--------------------------------------------------------------------------------
/chartjs_engine/views/engine.py:
--------------------------------------------------------------------------------
1 | """
2 | Charting engine which takes the input and routes it to the proper Chart sublcass.
3 | """
4 | from .line import LineChart
5 | from .bar import BarChart
6 | from .pie_doughnut import PieDoughnutChart
7 | from .base import Chart
8 |
9 |
10 |
11 | class ChartEngine(object):
12 | """An engine to make all of the charts necessary"""
13 |
14 | def __init__(self, **kwargs):
15 | """take in chart options and decide what kind of chart to make"""
16 | charts = {
17 | 'line': LineChart,
18 | 'bar': BarChart,
19 | 'pie': PieDoughnutChart,
20 | 'doughnut': PieDoughnutChart,
21 | }
22 |
23 | self.chart = charts[kwargs['chart_type']](
24 | chart_name=kwargs['chart_name'],
25 | chart_type=kwargs['chart_type'],
26 | chart_labels=kwargs['chart_labels'],
27 | # Options is not used but left for possible future use
28 | options=kwargs['options'],
29 | datasets=kwargs['datasets'])
30 |
31 |
32 | def make_chart(self):
33 | """Render the proper chart from the given"""
34 | return self.chart.to_string()
35 |
--------------------------------------------------------------------------------
/chartjs_engine/views/line.py:
--------------------------------------------------------------------------------
1 | """
2 | The line chart class
3 | """
4 | from .base import Chart
5 | from django.template.loader import render_to_string
6 | import json
7 |
8 |
9 | class LineChart(Chart):
10 | """
11 | Making the JSON data necessary for line charts.
12 | DOCS: http://www.chartjs.org/docs/#line-chart-introduction
13 | """
14 |
15 | def get_options(self):
16 | """Gets the options for the chart"""
17 | self.options = {
18 | 'scales': {
19 | 'yAxes': [{
20 | 'ticks': {
21 | 'beginAtZero': True
22 | }
23 | }]
24 | }
25 | }
26 | return self.options
27 |
28 | def get_data(self):
29 | """Populating self.data with self.datasets"""
30 |
31 | # self.data['labels'] already set in the Chart base class
32 | self.data['datasets'] = []
33 | self.colors = [self.random_color() for sets in self.datasets]
34 |
35 | for i, name in enumerate(self.datasets):
36 | self.data['datasets'].append({
37 | 'label': name,
38 | 'fill': False,
39 | 'lineTension': 0.1,
40 | 'borderCapStyle': 'butt',
41 | 'borderDash': [],
42 | 'borderDashOffset': 0.0,
43 | 'borderJoinStyle': 'miter',
44 | 'pointBorderColor': "rgba(75,192,192,1)",
45 | 'pointBackgroundColor': "#fff",
46 | 'pointBorderWidth': 1,
47 | 'pointHoverRadius': 5,
48 | 'pointHoverBackgroundColor': "rgba(75,192,192,1)",
49 | 'pointHoverBorderColor': "rgba(220,220,220,1)",
50 | 'pointHoverBorderWidth': 2,
51 | 'pointRadius': 1,
52 | 'pointHitRadius': 10,
53 | 'spanGaps': False,
54 | 'backgroundColor': self.colors[i][0],
55 | 'borderColor': self.colors[i][1],
56 | 'data': self.datasets[name],
57 | })
58 | return self.data
59 |
60 | def make_context(self):
61 | """Making the context to be returned to the render functions"""
62 | self.context = {
63 | 'chart_type': self.chart_type,
64 | 'chart_name': self.chart_name,
65 | 'data': json.dumps(self.data),
66 | 'options': json.dumps(self.options)
67 | }
68 | return self.context
69 |
70 |
71 | def to_string(self):
72 | """Generating the javascript needed for a line chart."""
73 |
74 | self.get_options()
75 | self.get_data()
76 | self.make_context()
77 | return render_to_string('chartjs_engine/chart.html', self.context)
78 |
--------------------------------------------------------------------------------
/chartjs_engine/views/pie_doughnut.py:
--------------------------------------------------------------------------------
1 | """
2 | The pie/doughnut chart class
3 | """
4 | from .base import Chart
5 | from django.template.loader import render_to_string
6 | import json
7 |
8 |
9 |
10 | class PieDoughnutChart(Chart):
11 | """
12 | Making the JSON necessary for a pie or doughnut chart
13 | DOCS: http://www.chartjs.org/docs/#doughnut-pie-chart-introduction
14 | """
15 |
16 | def get_options(self):
17 | """Gets the options for the chart"""
18 | self.options = {}
19 | return self.options
20 |
21 | def get_data(self):
22 | """Populating self.data with self.datasets"""
23 |
24 | # self.data['labels'] already set in the Chart base class
25 | self.data['datasets'] = []
26 |
27 | for i, name in enumerate(self.datasets):
28 |
29 | if len(self.datasets) == 1:
30 | self.colors = [self.random_color() for d in self.datasets[name]]
31 | else:
32 | raise Exception(
33 | 'Pie/Doughnut charts only support one dataset at this time')
34 |
35 | self.data['datasets'].append({
36 | # Labels in a pie chart have no function as of now
37 | 'label': name,
38 | 'backgroundColor': [color[0] for color in self.colors],
39 | 'hoverBackgroundColor': [color[1] for color in self.colors],
40 | 'borderWidth': 3,
41 | # For future support of multiple datasets
42 | 'data': self.datasets[name],
43 | })
44 |
45 | return self.data
46 |
47 | def make_context(self):
48 | """Making the context to be returned to the render functions"""
49 | self.context = {
50 | 'chart_type': self.chart_type,
51 | 'chart_name': self.chart_name,
52 | 'data': json.dumps(self.data),
53 | 'options': json.dumps(self.options)
54 | }
55 | return self.context
56 |
57 | def to_string(self):
58 | """Rendering pie or doughnut chart data to a template"""
59 | self.get_options()
60 | self.get_data()
61 | self.make_context()
62 | return render_to_string('chartjs_engine/chart.html', self.context)
--------------------------------------------------------------------------------
/manage.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | import os
3 | import sys
4 |
5 | if __name__ == "__main__":
6 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "project.settings")
7 | try:
8 | from django.core.management import execute_from_command_line
9 | except ImportError:
10 | # The above import may fail for some other reason. Ensure that the
11 | # issue is really that Django is missing to avoid masking other
12 | # exceptions on Python 2.
13 | try:
14 | import django
15 | except ImportError:
16 | raise ImportError(
17 | "Couldn't import Django. Are you sure it's installed and "
18 | "available on your PYTHONPATH environment variable? Did you "
19 | "forget to activate a virtual environment?"
20 | )
21 | raise
22 | execute_from_command_line(sys.argv)
23 |
--------------------------------------------------------------------------------
/project/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jeffwillette/django-chartjs-engine/2658d7840e1e92ef4a5170490de0688baa6b8695/project/__init__.py
--------------------------------------------------------------------------------
/project/settings.py:
--------------------------------------------------------------------------------
1 | """
2 | Django settings for chartjsx project.
3 |
4 | Generated by 'django-admin startproject' using Django 1.10.4.
5 |
6 | For more information on this file, see
7 | https://docs.djangoproject.com/en/1.10/topics/settings/
8 |
9 | For the full list of settings and their values, see
10 | https://docs.djangoproject.com/en/1.10/ref/settings/
11 | """
12 |
13 | import os
14 |
15 | # Build paths inside the project like this: os.path.join(BASE_DIR, ...)
16 | BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
17 |
18 |
19 | # Quick-start development settings - unsuitable for production
20 | # See https://docs.djangoproject.com/en/1.10/howto/deployment/checklist/
21 |
22 | # SECURITY WARNING: keep the secret key used in production secret!
23 | SECRET_KEY = '_j#%z=pk-1p6ycuh@$#ja1y*11$mq7v%*$m-g=_6c4))6r0q5w'
24 |
25 | # SECURITY WARNING: don't run with debug turned on in production!
26 | DEBUG = True
27 |
28 | ALLOWED_HOSTS = []
29 |
30 |
31 | # Application definition
32 |
33 | INSTALLED_APPS = [
34 | 'django.contrib.admin',
35 | 'django.contrib.auth',
36 | 'django.contrib.contenttypes',
37 | 'django.contrib.sessions',
38 | 'django.contrib.messages',
39 | 'django.contrib.staticfiles',
40 | 'chartjs_engine',
41 | ]
42 |
43 | MIDDLEWARE = [
44 | 'django.middleware.security.SecurityMiddleware',
45 | 'django.contrib.sessions.middleware.SessionMiddleware',
46 | 'django.middleware.common.CommonMiddleware',
47 | 'django.middleware.csrf.CsrfViewMiddleware',
48 | 'django.contrib.auth.middleware.AuthenticationMiddleware',
49 | 'django.contrib.messages.middleware.MessageMiddleware',
50 | 'django.middleware.clickjacking.XFrameOptionsMiddleware',
51 | ]
52 |
53 | ROOT_URLCONF = 'project.urls'
54 |
55 | TEMPLATES = [
56 | {
57 | 'BACKEND': 'django.template.backends.django.DjangoTemplates',
58 | 'DIRS': [],
59 | 'APP_DIRS': True,
60 | 'OPTIONS': {
61 | 'context_processors': [
62 | 'django.template.context_processors.debug',
63 | 'django.template.context_processors.request',
64 | 'django.contrib.auth.context_processors.auth',
65 | 'django.contrib.messages.context_processors.messages',
66 | ],
67 | },
68 | },
69 | ]
70 |
71 | WSGI_APPLICATION = 'project.wsgi.application'
72 |
73 |
74 | # Database
75 | # https://docs.djangoproject.com/en/1.10/ref/settings/#databases
76 |
77 | DATABASES = {
78 | 'default': {
79 | 'ENGINE': 'django.db.backends.sqlite3',
80 | 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
81 | }
82 | }
83 |
84 |
85 | # Password validation
86 | # https://docs.djangoproject.com/en/1.10/ref/settings/#auth-password-validators
87 |
88 | AUTH_PASSWORD_VALIDATORS = [
89 | {
90 | 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
91 | },
92 | {
93 | 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
94 | },
95 | {
96 | 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
97 | },
98 | {
99 | 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
100 | },
101 | ]
102 |
103 |
104 | # Internationalization
105 | # https://docs.djangoproject.com/en/1.10/topics/i18n/
106 |
107 | LANGUAGE_CODE = 'en-us'
108 |
109 | TIME_ZONE = 'UTC'
110 |
111 | USE_I18N = True
112 |
113 | USE_L10N = True
114 |
115 | USE_TZ = True
116 |
117 |
118 | # Static files (CSS, JavaScript, Images)
119 | # https://docs.djangoproject.com/en/1.10/howto/static-files/
120 |
121 | STATIC_URL = '/static/'
122 |
--------------------------------------------------------------------------------
/project/urls.py:
--------------------------------------------------------------------------------
1 | """chartjsx URL Configuration
2 |
3 | The `urlpatterns` list routes URLs to views. For more information please see:
4 | https://docs.djangoproject.com/en/1.10/topics/http/urls/
5 | Examples:
6 | Function views
7 | 1. Add an import: from my_app import views
8 | 2. Add a URL to urlpatterns: url(r'^$', views.home, name='home')
9 | Class-based views
10 | 1. Add an import: from other_app.views import Home
11 | 2. Add a URL to urlpatterns: url(r'^$', Home.as_view(), name='home')
12 | Including another URLconf
13 | 1. Import the include() function: from django.conf.urls import url, include
14 | 2. Add a URL to urlpatterns: url(r'^blog/', include('blog.urls'))
15 | """
16 | from django.conf.urls import url
17 | from django.contrib import admin
18 |
19 | urlpatterns = [
20 | url(r'^admin/', admin.site.urls),
21 | ]
22 |
--------------------------------------------------------------------------------
/project/wsgi.py:
--------------------------------------------------------------------------------
1 | """
2 | WSGI config for chartjsx project.
3 |
4 | It exposes the WSGI callable as a module-level variable named ``application``.
5 |
6 | For more information on this file, see
7 | https://docs.djangoproject.com/en/1.10/howto/deployment/wsgi/
8 | """
9 |
10 | import os
11 |
12 | from django.core.wsgi import get_wsgi_application
13 |
14 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "chartjsx.settings")
15 |
16 | application = get_wsgi_application()
17 |
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
1 | Django==1.10.4
2 | funcsigs==1.0.2
3 | mock==2.0.0
4 | pbr==1.10.0
5 | six==1.10.0
6 |
--------------------------------------------------------------------------------
/setup.py:
--------------------------------------------------------------------------------
1 | from setuptools import setup
2 |
3 | setup(name='django-chartjs-engine',
4 | version='0.0.3',
5 | description='Django app to build chartjs javascript charts',
6 | url='https://github.com/deltaskelta/django-chartjs-engine',
7 | author='Jeff Willette',
8 | author_email='jrwillette88@gmail.com',
9 | keywords = ['django', 'chartjs', 'javascript', 'charts'],
10 | packages = ['chartjs_engine'],
11 | include_package_data = True,
12 | )
13 |
14 | # The command to upload: python setup.py sdist upload -r pypi
15 |
--------------------------------------------------------------------------------