From 435d63a02f216a8b01d57afe783a5d45b9487c4e Mon Sep 17 00:00:00 2001 From: Benjamin Dauvergne Date: Fri, 18 Jan 2019 21:34:15 +0100 Subject: [PATCH 2/3] allow use of join in measures (fixes #29914) --- bijoe/engine.py | 5 +++- bijoe/schemas.py | 4 ++- tests/fixtures/schema1/01_schema.json | 21 ++++++++++++++ tests/fixtures/schema1/01_schema.sql | 22 ++++++++++---- tests/test_schema1.py | 42 +++++++++++++++++++++++---- 5 files changed, 81 insertions(+), 13 deletions(-) diff --git a/bijoe/engine.py b/bijoe/engine.py index 9783b8f..d2f2f02 100644 --- a/bijoe/engine.py +++ b/bijoe/engine.py @@ -261,7 +261,8 @@ class EngineCube(object): for dimension_name in drilldown: dimension = self.dimensions[dimension_name] - joins.update(dimension.join or []) + if dimension.join: + joins.update(dimension.join) projections.append('%s AS %s' % (dimension.value_label or dimension.value, dimension.name)) group_by.append(dimension.group_by or dimension.value) @@ -273,6 +274,8 @@ class EngineCube(object): for measure_name in measures: measure = self.get_measure(measure_name) + if measure.join: + joins.update(measure.join) if measure.expression not in projections: projections.append(measure.expression + ' AS ' + measure.name) sql = 'SELECT ' + ', '.join(projections) diff --git a/bijoe/schemas.py b/bijoe/schemas.py index 5cc5ee2..9459aac 100644 --- a/bijoe/schemas.py +++ b/bijoe/schemas.py @@ -125,13 +125,15 @@ class Base(object): class Measure(Base): - __slots__ = ['name', 'label', 'type', 'expression'] + __slots__ = ['name', 'label', 'type', 'expression', 'join'] __types__ = { 'name': str, 'label': six.text_type, 'type': type_cast, 'expression': str, + 'join': [str], } + join = None class Dimension(Base): diff --git a/tests/fixtures/schema1/01_schema.json b/tests/fixtures/schema1/01_schema.json index 282f1ae..94f9c45 100644 --- a/tests/fixtures/schema1/01_schema.json +++ b/tests/fixtures/schema1/01_schema.json @@ -65,6 +65,13 @@ "master": "outersubcategory.category_id", "detail": "id", "kind": "full" + }, + { + "name": "subfacts", + "table": "subfacts", + "master": "id", + "detail": "fact_id", + "kind": "left" } ], "dimensions": [ @@ -189,6 +196,20 @@ "label": "pourcentage des demandes", "type": "percent", "expression": "case (select count({fact_table}.id) from {table_expression} where {where_conditions}) when 0 then null else count({fact_table}.id) * 100. / (select count({fact_table}.id) from {table_expression} where {where_conditions}) end" + }, + { + "name": "subfacts_min_delay_integer_1", + "label": "subfacts min delay with integer=1", + "type": "duration", + "expression": "min(subfacts.duration) filter (where subfacts.integer = 1)", + "join": ["subfacts"] + }, + { + "name": "subfacts_max_delay_integer_1", + "label": "subfacts max delay with integer=1", + "type": "duration", + "expression": "max(subfacts.duration) filter (where subfacts.integer = 1)", + "join": ["subfacts"] } ] } diff --git a/tests/fixtures/schema1/01_schema.sql b/tests/fixtures/schema1/01_schema.sql index ecf1242..9db6e6f 100644 --- a/tests/fixtures/schema1/01_schema.sql +++ b/tests/fixtures/schema1/01_schema.sql @@ -29,6 +29,14 @@ CREATE TABLE facts ( string varchar ); +CREATE TABLE subfacts ( + id serial primary key, + fact_id integer references schema1.facts(id), + integer integer, + datetime timestamp with time zone, + duration interval +); + INSERT INTO category (ord, label) VALUES (1, 'caté1'), (0, 'caté2'), @@ -50,17 +58,19 @@ INSERT INTO facts (date, datetime, integer, boolean, cnt, innersubcategory_id, l ('2017-01-01', '2017-01-01 10:00', 1, FALSE, 10, 1, 1, 1, 1, 'a'), ('2017-01-02', '2017-01-02 10:00', 1, TRUE, 10, 3, 3, 3, 3, 'b'), ('2017-01-03', '2017-01-03 10:00', 1, FALSE, 10, NULL, NULL, NULL, NULL, 'a'), - ('2017-01-04', '2017-01-04 10:00', 1, FALSE, 10, 1, 1, 1, 1, 'a'), - ('2017-01-05', '2017-01-05 10:00', 1, TRUE, 10, 1, 1, 1, 1, 'c'), - ('2017-01-06', '2017-01-06 10:00', 1, FALSE, 10, 1, 1, 1, 1, NULL), + ('2017-01-04', '2017-01-04 10:00', 1, FALSE, 10, 4, 1, 1, 1, 'a'), + ('2017-01-05', '2017-01-05 10:00', 1, TRUE, 10, 6, 1, 1, 1, 'c'), + ('2017-01-06', '2017-01-06 10:00', 1, FALSE, 10, 7, 1, 1, 1, NULL), ('2017-01-07', '2017-01-07 10:00', 1, TRUE, 10, 1, 1, 1, 1, 'a'), ('2017-01-08', '2017-01-08 10:00', 1, FALSE, 10, 1, 1, 1, 1, 'a'), ('2017-01-09', '2017-01-09 10:00', 1, TRUE, 10, 1, 1, 1, 1, 'a'), - ('2017-01-10', '2017-01-10 10:00', 1, FALSE, 10, 1, 1, 1, 1, 'a'), + ('2017-01-10', '2017-01-10 10:00', 1, FALSE, 10, 5, 1, 1, 1, 'a'), ('2017-02-01', '2017-02-01 10:00', 1, TRUE, 10, 1, 1, 1, 1, 'a'), - ('2017-03-01', '2017-03-01 10:00', 1, FALSE, 10, 1, 1, 1, 1, 'c'), + ('2017-03-01', '2017-03-01 10:00', 1, FALSE, 10, 9, 1, 1, 1, 'c'), ('2017-04-01', '2017-04-01 10:00', 1, TRUE, 10, 1, 1, 1, 1, 'a'), ('2017-05-01', '2017-05-01 10:00', 1, FALSE, 10, 1, 1, 1, 1, 'a'), - ('2017-06-01', '2017-06-01 10:00', 1, TRUE, 10, 1, 1, 1, 1, 'c'), + ('2017-06-01', '2017-06-01 10:00', 1, TRUE, 10, 8, 1, 1, 1, 'c'), ('2017-07-01', '2017-07-01 10:00', 1, FALSE, 10, 1, 1, 1, 1, 'a'), ('2017-08-01', '2017-08-01 10:00', 1, TRUE, 10, 1, 1, 1, 1, 'b'); + +INSERT INTO subfacts (fact_id, integer, datetime, duration) SELECT facts.id, floor(random() * 3), val, (serie.val - facts.datetime) FROM facts, LATERAL (SELECT * FROM generate_series(facts.datetime, facts.datetime + '10 hours'::interval, '1 hour')) as serie(val); diff --git a/tests/test_schema1.py b/tests/test_schema1.py index 097ba0e..39bdd55 100644 --- a/tests/test_schema1.py +++ b/tests/test_schema1.py @@ -16,10 +16,11 @@ def test_simple(schema1, app, admin): form.set('drilldown_x', 'innersubcategory') response = form.submit('visualize') assert 'big-msg-info' not in response - assert get_table(response) == [ - [u'Inner SubCategory', u'subé3', u'subé1'], - ['number of rows', '1', '15'], - ] + assert get_table(response) == [['Inner SubCategory', u'sub\xe94', u'sub\xe95', + u'sub\xe96', u'sub\xe98', u'sub\xe99', + u'sub\xe97', u'sub\xe93', u'sub\xe91'], + ['number of rows', '1', '1', '1', '1', '1', + '1', '1', '9']] form = response.form form.set('representation', 'table') form.set('measure', 'simple_count') @@ -121,7 +122,7 @@ def test_ods(schema1, app, admin): assert get_table(response) == get_ods_table(ods_response)[1:] root = get_ods_document(ods_response) nodes = root.findall('.//{%s}table-cell' % TABLE_NS) - assert len([node for node in nodes if node.attrib['{%s}value-type' % OFFICE_NS] == 'float']) == 2 + assert len([node for node in nodes if node.attrib['{%s}value-type' % OFFICE_NS] == 'float']) == 8 def test_truncated_previous_year_range_on_datetime(schema1, app, admin, freezer): @@ -147,3 +148,34 @@ def test_truncated_previous_year_range_on_datetime(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_complex_measure_with_join(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', 'subfacts_min_delay_integer_1') + form.set('drilldown_x', 'innercategory') + response = form.submit('visualize') + assert 'big-msg-info' not in response + # skip first line of ODS table as it's a header not present in the HTML display + table = get_table(response) +#[['Inner Category', u'cat\xe92', u'cat\xe93', u'cat\xe91'], +#['subfacts min delay with integer=1', +# "moins d'1 heure", +# "moins d'1 heure", +# "moins d'1 heure"]] + assert all(len(row) == 4 for row in table) + assert table[0][0] == 'Inner Category' + assert table[1][0] == 'subfacts min delay with integer=1' + + form.set('measure', 'subfacts_max_delay_integer_1') + response = form.submit('visualize') + table = get_table(response) +#[['Inner Category', u'cat\xe92', u'cat\xe93', u'cat\xe91'], +# ['subfacts max delay with integer=1', '9 heure(s)', '8 heure(s)', '10 heure(s)']] + assert all(len(row) == 4 for row in table) + assert table[0][0] == 'Inner Category' + assert table[1][0] == 'subfacts max delay with integer=1' -- 2.20.1