diff --git a/README.rst b/README.rst index 020295c..df5bb15 100644 --- a/README.rst +++ b/README.rst @@ -81,8 +81,7 @@ The JSON model files must conform to this schema: * join: list of join names, indicate that some joins must be used when using this dimension, - * type: indicate the type of the dimension, numerical, time-like, - geographical, duration, etc.. + * type: indicate the type of the dimension: integer, string, bool, date * value: SQL expression giving the value for the dimension, it can be different than the value used for filtering or grouping, diff --git a/bijoe/schemas.py b/bijoe/schemas.py index e4a2fba..f1f00cb 100644 --- a/bijoe/schemas.py +++ b/bijoe/schemas.py @@ -250,6 +250,8 @@ class Dimension(Base): pass return ' AND '.join(filters), values else: + if not isinstance(filter_values, list): + filter_values = [filter_values] if not filter_values: return '', [] if self.type == 'integer': diff --git a/bijoe/templates/bijoe/cube_table.html b/bijoe/templates/bijoe/cube_table.html index 8b1e314..d75728c 100644 --- a/bijoe/templates/bijoe/cube_table.html +++ b/bijoe/templates/bijoe/cube_table.html @@ -12,9 +12,8 @@ {% for row in table.table %} {% for value in row %} - - {% if value == None %}0{% else %}{{ value }}{% endif %} - + {% comment %}Only django 1.10 allow is None/True/False{% endcomment %} + {% if value|stringformat:"r" == "None" %}0{% elif value|stringformat:"r" == "True" %}{% trans "Oui" %}{% elif value|stringformat:"r" == "False" %}{% trans "Non" %}{% else %}{{ value }}{% endif %} {% endfor %} {% endfor %} diff --git a/bijoe/visualization/forms.py b/bijoe/visualization/forms.py index 1ecb716..e46c89c 100644 --- a/bijoe/visualization/forms.py +++ b/bijoe/visualization/forms.py @@ -19,7 +19,7 @@ from django import forms from django.core.exceptions import ValidationError from django.utils.translation import ugettext as _ from django.utils.safestring import mark_safe -from django.forms import ModelForm, TextInput +from django.forms import ModelForm, TextInput, NullBooleanField from django.conf import settings try: @@ -179,6 +179,9 @@ class CubeForm(forms.Form): if dimension.type == 'date': self.base_fields[field_name] = DateRangeField( label=dimension.label.capitalize(), required=False) + elif dimension.type == 'bool': + self.base_fields[field_name] = NullBooleanField( + label=dimension.label.capitalize(), required=False) else: self.base_fields[field_name] = forms.MultipleChoiceField( label=dimension.label.capitalize(), diff --git a/bijoe/visualization/utils.py b/bijoe/visualization/utils.py index f57412c..2fdd4f4 100644 --- a/bijoe/visualization/utils.py +++ b/bijoe/visualization/utils.py @@ -24,6 +24,7 @@ import copy from django.utils.safestring import mark_safe from django.utils.translation import ugettext_lazy as _ +from django.utils import six from django.core.cache import cache from django.http import Http404 from django.conf import settings @@ -120,17 +121,23 @@ class Visualization(object): @property def key(self): - l = [self.cube.engine.warehouse.name, self.cube.name] + keys = [self.cube.engine.warehouse.name, self.cube.name] if self.loop: - l.append(self.loop.name) - for kw, values in self.filters.iteritems(): - if values: - if isinstance(values, dict): - values = values.items() - l.append('$'.join([kw] + sorted(map(unicode, values)))) - l += [dim.name for dim in self.drilldown] - l += [self.measure.name] - key = '$'.join(v.encode('utf8') for v in l) + keys.append(self.loop.name) + for kw, value in self.filters.iteritems(): + if value is None: + continue + elif isinstance(value, (dict, list, tuple)): + # multiple values + if isinstance(value, dict): + value = value.items() + keys.append('$'.join([kw] + sorted(map(six.text_type, value)))) + else: + # scalar values + keys.append(u'%s$%s' % (kw, six.text_type(value))) + keys += [dim.name for dim in self.drilldown] + keys += [self.measure.name] + key = '$'.join(v.encode('utf8') for v in keys) return hashlib.md5(key).hexdigest() def stringified(self): diff --git a/tests/fixtures/schema1/01_schema.json b/tests/fixtures/schema1/01_schema.json index 3c8b3ae..823dfbe 100644 --- a/tests/fixtures/schema1/01_schema.json +++ b/tests/fixtures/schema1/01_schema.json @@ -74,6 +74,12 @@ "type": "date", "value": "date" }, + { + "name": "boolean", + "label": "Boolean", + "type": "bool", + "value": "boolean" + }, { "name": "hour", "type": "integer", diff --git a/tests/fixtures/schema1/01_schema.sql b/tests/fixtures/schema1/01_schema.sql index 3a2eacd..da77db4 100644 --- a/tests/fixtures/schema1/01_schema.sql +++ b/tests/fixtures/schema1/01_schema.sql @@ -20,6 +20,7 @@ CREATE TABLE facts ( date date, datetime timestamp with time zone, integer integer, + boolean boolean, cnt integer default(1), innersubcategory_id integer references schema1.subcategory(id), leftsubcategory_id integer references schema1.subcategory(id), @@ -44,21 +45,21 @@ INSERT INTO subcategory (category_id, ord, label) VALUES (3, 0, 'subé9'); -INSERT INTO facts (date, datetime, integer, cnt, innersubcategory_id, leftsubcategory_id, rightsubcategory_id, outersubcategory_id) VALUES - ('2017-01-01', '2017-01-01 10:00', 1, 10, 1, 1, 1, 1), - ('2017-01-02', '2017-01-02 10:00', 1, 10, 3, 3, 3, 3), - ('2017-01-03', '2017-01-03 10:00', 1, 10, NULL, NULL, NULL, NULL), - ('2017-01-04', '2017-01-04 10:00', 1, 10, 1, 1, 1, 1), - ('2017-01-05', '2017-01-05 10:00', 1, 10, 1, 1, 1, 1), - ('2017-01-06', '2017-01-06 10:00', 1, 10, 1, 1, 1, 1), - ('2017-01-07', '2017-01-07 10:00', 1, 10, 1, 1, 1, 1), - ('2017-01-08', '2017-01-08 10:00', 1, 10, 1, 1, 1, 1), - ('2017-01-09', '2017-01-09 10:00', 1, 10, 1, 1, 1, 1), - ('2017-01-10', '2017-01-10 10:00', 1, 10, 1, 1, 1, 1), - ('2017-02-01', '2017-02-01 10:00', 1, 10, 1, 1, 1, 1), - ('2017-03-01', '2017-03-01 10:00', 1, 10, 1, 1, 1, 1), - ('2017-04-01', '2017-04-01 10:00', 1, 10, 1, 1, 1, 1), - ('2017-05-01', '2017-05-01 10:00', 1, 10, 1, 1, 1, 1), - ('2017-06-01', '2017-06-01 10:00', 1, 10, 1, 1, 1, 1), - ('2017-07-01', '2017-07-01 10:00', 1, 10, 1, 1, 1, 1), - ('2017-08-01', '2017-08-01 10:00', 1, 10, 1, 1, 1, 1); +INSERT INTO facts (date, datetime, integer, boolean, cnt, innersubcategory_id, leftsubcategory_id, rightsubcategory_id, outersubcategory_id) VALUES + ('2017-01-01', '2017-01-01 10:00', 1, FALSE, 10, 1, 1, 1, 1), + ('2017-01-02', '2017-01-02 10:00', 1, TRUE, 10, 3, 3, 3, 3), + ('2017-01-03', '2017-01-03 10:00', 1, FALSE, 10, NULL, NULL, NULL, NULL), + ('2017-01-04', '2017-01-04 10:00', 1, FALSE, 10, 1, 1, 1, 1), + ('2017-01-05', '2017-01-05 10:00', 1, TRUE, 10, 1, 1, 1, 1), + ('2017-01-06', '2017-01-06 10:00', 1, FALSE, 10, 1, 1, 1, 1), + ('2017-01-07', '2017-01-07 10:00', 1, TRUE, 10, 1, 1, 1, 1), + ('2017-01-08', '2017-01-08 10:00', 1, FALSE, 10, 1, 1, 1, 1), + ('2017-01-09', '2017-01-09 10:00', 1, TRUE, 10, 1, 1, 1, 1), + ('2017-01-10', '2017-01-10 10:00', 1, FALSE, 10, 1, 1, 1, 1), + ('2017-02-01', '2017-02-01 10:00', 1, TRUE, 10, 1, 1, 1, 1), + ('2017-03-01', '2017-03-01 10:00', 1, FALSE, 10, 1, 1, 1, 1), + ('2017-04-01', '2017-04-01 10:00', 1, TRUE, 10, 1, 1, 1, 1), + ('2017-05-01', '2017-05-01 10:00', 1, FALSE, 10, 1, 1, 1, 1), + ('2017-06-01', '2017-06-01 10:00', 1, TRUE, 10, 1, 1, 1, 1), + ('2017-07-01', '2017-07-01 10:00', 1, FALSE, 10, 1, 1, 1, 1), + ('2017-08-01', '2017-08-01 10:00', 1, TRUE, 10, 1, 1, 1, 1); diff --git a/tests/test_schema1.py b/tests/test_schema1.py index 60c3606..9d79d72 100644 --- a/tests/test_schema1.py +++ b/tests/test_schema1.py @@ -53,3 +53,18 @@ def test_truncated_previous_year_range(schema1, app, admin, freezer): ['2017', '10', '1', '1', '1', '1', '1', '1', '1', '17'], ['Total', '10', '1', '1', '1', '1', '1', '1', '1', '17'], ] + + +def test_boolean_dimension(schema1, app, admin): + login(app, admin) + response = app.get('/').follow() + response = response.click('Facts 1') + form = response.form + form.set('representation', 'table') + form.set('measure', 'simple_count') + form.set('drilldown_x', 'boolean') + response = form.submit('visualize') + assert get_table(response) == [['Boolean', 'Non', 'Oui'], ['number of rows', '9', '8']] + form.set('filter__boolean', [o[0] for o in form.fields['filter__boolean'][0].options if o[2] == 'Oui'][0]) + response = form.submit('visualize') + assert get_table(response) == [['Boolean', 'Oui'], ['number of rows', '8']] diff --git a/tests/utils.py b/tests/utils.py index 5f4794b..4a6956e 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -26,6 +26,6 @@ def get_table(response): row = [] table.append(row) for td in tr.findall('td'): - row.append(td.text.strip()) + row.append((td.text or '').strip()) return table