0001-dataviz-add-support-for-loop-warn-if-there-are-three.patch
combo/apps/dataviz/models.py | ||
---|---|---|
30 | 30 |
from combo.utils import get_templated_url, requests |
31 | 31 | |
32 | 32 | |
33 |
class UnsupportedDataSet(Exception): |
|
34 |
pass |
|
35 | ||
36 | ||
33 | 37 |
@register_cell_class |
34 | 38 |
class Gauge(CellBase): |
35 | 39 |
title = models.CharField(_('Title'), max_length=150, blank=True, null=True) |
... | ... | |
157 | 161 |
def get_cell_extra_context(self, context): |
158 | 162 |
ctx = super(ChartNgCell, self).get_cell_extra_context(context) |
159 | 163 |
if self.chart_type == 'table': |
160 |
chart = self.get_chart(raise_if_not_cached=not(context.get('synchronous'))) |
|
161 |
ctx['table'] = chart.render_table( |
|
162 |
transpose=bool(chart.axis_count == 2), |
|
163 |
) |
|
164 |
ctx['table'] = ctx['table'].replace('<table>', '<table class="main">') |
|
164 |
try: |
|
165 |
chart = self.get_chart(raise_if_not_cached=not(context.get('synchronous'))) |
|
166 |
except UnsupportedDataSet: |
|
167 |
ctx['table'] = '<p>%s</p>' % _('Unsupported dataset.') |
|
168 |
else: |
|
169 |
ctx['table'] = chart.render_table( |
|
170 |
transpose=bool(chart.axis_count == 2), |
|
171 |
) |
|
172 |
ctx['table'] = ctx['table'].replace('<table>', '<table class="main">') |
|
165 | 173 |
return ctx |
166 | 174 | |
167 | 175 |
def get_chart(self, width=None, height=None, raise_if_not_cached=False): |
... | ... | |
186 | 194 | |
187 | 195 |
# normalize axis to have a fake axis when there are no dimensions and |
188 | 196 |
# always a x axis when there is a single dimension. |
197 |
data = response['data'] |
|
198 |
loop_labels = response['axis'].get('loop') or [] |
|
189 | 199 |
x_labels = response['axis'].get('x_labels') or [] |
190 | 200 |
y_labels = response['axis'].get('y_labels') or [] |
191 |
data = response['data'] |
|
201 |
if loop_labels: |
|
202 |
if x_labels and y_labels: |
|
203 |
# no support for three dimensions |
|
204 |
raise UnsupportedDataSet() |
|
205 |
if not y_labels: |
|
206 |
y_labels = loop_labels |
|
207 |
else: |
|
208 |
x_labels, y_labels = y_labels, loop_labels |
|
192 | 209 |
if not x_labels and not y_labels: # unidata |
193 | 210 |
x_labels = [''] |
194 | 211 |
y_labels = [''] |
combo/apps/dataviz/views.py | ||
---|---|---|
16 | 16 | |
17 | 17 |
from django.core.exceptions import PermissionDenied |
18 | 18 |
from django.http import HttpResponse |
19 |
from django.utils.translation import ugettext_lazy as _ |
|
19 | 20 | |
20 | 21 |
from combo.utils import get_templated_url, requests |
21 |
from .models import Gauge, ChartNgCell |
|
22 |
from .models import Gauge, ChartNgCell, UnsupportedDataSet
|
|
22 | 23 | |
23 | 24 | |
24 | 25 |
def ajax_gauge_count(request, *args, **kwargs): |
... | ... | |
33 | 34 |
raise PermissionDenied() |
34 | 35 |
if not cell.is_visible(request.user): |
35 | 36 |
raise PermissionDenied() |
36 |
chart = cell.get_chart( |
|
37 |
width=int(request.GET.get('width', 0)) or None, |
|
38 |
height=int(request.GET.get('height', 0)) or int(cell.height) |
|
39 |
) |
|
40 |
return HttpResponse(chart.render(), content_type='image/svg+xml') |
|
37 |
try: |
|
38 |
chart = cell.get_chart( |
|
39 |
width=int(request.GET.get('width', 0)) or None, |
|
40 |
height=int(request.GET.get('height', 0)) or int(cell.height) |
|
41 |
) |
|
42 |
except UnsupportedDataSet: |
|
43 |
svg = """<?xml version="1.0" encoding="UTF-8" standalone="no"?> |
|
44 |
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" |
|
45 |
viewBox="0 0 %(width)s 30" width="%(width)s" height="30"> |
|
46 |
<text |
|
47 |
y="20" |
|
48 |
x="10" |
|
49 |
style="font-family: sans-serif; font-size: 16px; fill:#000000;">%(text)s</text> |
|
50 |
</svg>""" % {'width': request.GET.get('width', 200), |
|
51 |
'text': _('Unsupported dataset.')} |
|
52 |
else: |
|
53 |
svg = chart.render() |
|
54 |
return HttpResponse(svg, content_type='image/svg+xml') |
tests/test_dataviz.py | ||
---|---|---|
8 | 8 |
from django.test import override_settings |
9 | 9 | |
10 | 10 |
from combo.data.models import Page |
11 |
from combo.apps.dataviz.models import Gauge, ChartNgCell |
|
11 |
from combo.apps.dataviz.models import Gauge, ChartNgCell, UnsupportedDataSet
|
|
12 | 12 | |
13 | 13 |
from .test_public import login, normal_user |
14 | 14 | |
... | ... | |
70 | 70 |
'name': 'fourth visualization (no axis)', |
71 | 71 |
'slug': 'fourth', |
72 | 72 |
}, |
73 |
{ |
|
74 |
'data-url': 'https://bijoe.example.com/visualization/5/json/', |
|
75 |
'path': 'https://bijoe.example.com/visualization/5/iframe/?signature=123', |
|
76 |
'name': 'fifth visualization (loop/X)', |
|
77 |
'slug': 'fifth', |
|
78 |
}, |
|
79 |
{ |
|
80 |
'data-url': 'https://bijoe.example.com/visualization/6/json/', |
|
81 |
'path': 'https://bijoe.example.com/visualization/6/iframe/?signature=123', |
|
82 |
'name': 'sixth visualization (loop/Y)', |
|
83 |
'slug': 'sixth', |
|
84 |
}, |
|
85 |
{ |
|
86 |
'data-url': 'https://bijoe.example.com/visualization/7/json/', |
|
87 |
'path': 'https://bijoe.example.com/visualization/7/iframe/?signature=123', |
|
88 |
'name': 'seventh visualization (loop/X/Y)', |
|
89 |
'slug': 'seventh', |
|
90 |
}, |
|
91 | ||
73 | 92 |
] |
74 | 93 | |
75 | 94 | |
... | ... | |
114 | 133 |
'axis': {} |
115 | 134 |
} |
116 | 135 |
return {'content': json.dumps(response), 'request': request, 'status_code': 200} |
136 |
if url.path == '/visualization/5/json/': |
|
137 |
response = { |
|
138 |
'format': '1', |
|
139 |
'data': [ |
|
140 |
[222, 134, 53], |
|
141 |
[122, 114, 33], |
|
142 |
], |
|
143 |
'axis': { |
|
144 |
'x_labels': ['web', 'mail', 'email'], |
|
145 |
'loop': ['foo', 'bar'], |
|
146 |
} |
|
147 |
} |
|
148 |
return {'content': json.dumps(response), 'request': request, 'status_code': 200} |
|
149 |
if url.path == '/visualization/6/json/': |
|
150 |
response = { |
|
151 |
'format': '1', |
|
152 |
'data': [ |
|
153 |
[222, 134, 53], |
|
154 |
[122, 114, 33], |
|
155 |
], |
|
156 |
'axis': { |
|
157 |
'y_labels': ['web', 'mail', 'email'], |
|
158 |
'loop': ['foo', 'bar'], |
|
159 |
} |
|
160 |
} |
|
161 |
return {'content': json.dumps(response), 'request': request, 'status_code': 200} |
|
162 |
if url.path == '/visualization/7/json/': |
|
163 |
response = { |
|
164 |
'format': '1', |
|
165 |
'data': [ |
|
166 |
[[222, 134, 53], [122, 114, 33]], |
|
167 |
[[222, 134, 53], [122, 114, 33]], |
|
168 |
[[222, 134, 53], [122, 114, 33]], |
|
169 |
[[222, 134, 53], [122, 114, 33]], |
|
170 |
], |
|
171 |
'axis': { |
|
172 |
'x_labels': ['foo', 'bar'], |
|
173 |
'y_labels': ['web', 'mail', 'email'], |
|
174 |
'loop': ['a', 'b', 'c', 'd'], |
|
175 |
} |
|
176 |
} |
|
177 |
return {'content': json.dumps(response), 'request': request, 'status_code': 200} |
|
117 | 178 | |
118 | 179 | |
119 | 180 |
def test_chartng_cell(app): |
... | ... | |
186 | 247 |
assert chart.x_labels == [''] |
187 | 248 |
assert chart.raw_series == [([222], {'title': ''})] |
188 | 249 | |
250 |
# loop/X |
|
251 |
cell.data_reference = 'plop:fifth' |
|
252 |
cell.save() |
|
253 |
chart = cell.get_chart() |
|
254 |
assert chart.x_labels == ['web', 'mail', 'email'] |
|
255 |
assert chart.raw_series == [ |
|
256 |
([222, 134, 53], {'title': u'foo'}), |
|
257 |
([122, 114, 33], {'title': u'bar'}), |
|
258 |
] |
|
259 | ||
260 |
# loop/Y |
|
261 |
cell.data_reference = 'plop:sixth' |
|
262 |
cell.save() |
|
263 |
chart = cell.get_chart() |
|
264 |
assert chart.x_labels == ['web', 'mail', 'email'] |
|
265 |
assert chart.raw_series == [ |
|
266 |
([222, 134, 53], {'title': u'foo'}), |
|
267 |
([122, 114, 33], {'title': u'bar'}), |
|
268 |
] |
|
269 | ||
270 |
# loop/X/Y |
|
271 |
cell.data_reference = 'plop:seventh' |
|
272 |
cell.save() |
|
273 |
with pytest.raises(UnsupportedDataSet): |
|
274 |
chart = cell.get_chart() |
|
275 | ||
276 | ||
189 | 277 |
def test_chartng_cell_view(app, normal_user): |
190 | 278 |
page = Page(title='One', slug='index') |
191 | 279 |
page.save() |
... | ... | |
230 | 318 |
resp = app.get('/') |
231 | 319 |
assert '<td>222</td>' in resp.text |
232 | 320 | |
321 |
# unsupported dataset |
|
322 |
cell.data_reference = 'plop:seventh' |
|
323 |
cell.save() |
|
324 |
resp = app.get('/') |
|
325 |
assert 'Unsupported dataset' in resp.text |
|
326 | ||
327 |
cell.chart_type = 'bar' |
|
328 |
cell.save() |
|
329 |
resp = app.get('/api/dataviz/graph/1/?width=400', status=200) |
|
330 |
assert 'Unsupported dataset' in resp.text |
|
331 | ||
233 | 332 | |
234 | 333 |
def test_chartng_cell_manager(app, admin_user): |
235 | 334 |
page = Page(title='One', slug='index') |
... | ... | |
247 | 346 |
resp = app.get('/manage/pages/%s/' % page.id) |
248 | 347 |
assert resp.form['cdataviz_chartngcell-%s-data_reference' % cell.id].options == [ |
249 | 348 |
(u'plop:example', True, u'example visualization (X)'), |
349 |
(u'plop:fifth', False, u'fifth visualization (loop/X)'), |
|
250 | 350 |
(u'plop:fourth', False, u'fourth visualization (no axis)'), |
251 | 351 |
(u'plop:second', False, u'second visualization (Y)'), |
352 |
(u'plop:seventh', False, u'seventh visualization (loop/X/Y)'), |
|
353 |
(u'plop:sixth', False, u'sixth visualization (loop/Y)'), |
|
252 | 354 |
(u'plop:third', False, u'third visualization (X/Y)'), |
253 | 355 |
] |
254 |
- |