0002-allow-use-of-join-in-measures-fixes-29914.patch
bijoe/engine.py | ||
---|---|---|
261 | 261 | |
262 | 262 |
for dimension_name in drilldown: |
263 | 263 |
dimension = self.dimensions[dimension_name] |
264 |
joins.update(dimension.join or []) |
|
264 |
if dimension.join: |
|
265 |
joins.update(dimension.join) |
|
265 | 266 |
projections.append('%s AS %s' % (dimension.value_label or dimension.value, |
266 | 267 |
dimension.name)) |
267 | 268 |
group_by.append(dimension.group_by or dimension.value) |
... | ... | |
273 | 274 | |
274 | 275 |
for measure_name in measures: |
275 | 276 |
measure = self.get_measure(measure_name) |
277 |
if measure.join: |
|
278 |
joins.update(measure.join) |
|
276 | 279 |
if measure.expression not in projections: |
277 | 280 |
projections.append(measure.expression + ' AS ' + measure.name) |
278 | 281 |
sql = 'SELECT ' + ', '.join(projections) |
bijoe/schemas.py | ||
---|---|---|
125 | 125 | |
126 | 126 | |
127 | 127 |
class Measure(Base): |
128 |
__slots__ = ['name', 'label', 'type', 'expression'] |
|
128 |
__slots__ = ['name', 'label', 'type', 'expression', 'join']
|
|
129 | 129 |
__types__ = { |
130 | 130 |
'name': str, |
131 | 131 |
'label': six.text_type, |
132 | 132 |
'type': type_cast, |
133 | 133 |
'expression': str, |
134 |
'join': [str], |
|
134 | 135 |
} |
136 |
join = None |
|
135 | 137 | |
136 | 138 | |
137 | 139 |
class Dimension(Base): |
tests/fixtures/schema1/01_schema.json | ||
---|---|---|
65 | 65 |
"master": "outersubcategory.category_id", |
66 | 66 |
"detail": "id", |
67 | 67 |
"kind": "full" |
68 |
}, |
|
69 |
{ |
|
70 |
"name": "subfacts", |
|
71 |
"table": "subfacts", |
|
72 |
"master": "id", |
|
73 |
"detail": "fact_id", |
|
74 |
"kind": "left" |
|
68 | 75 |
} |
69 | 76 |
], |
70 | 77 |
"dimensions": [ |
... | ... | |
183 | 190 |
"label": "pourcentage des demandes", |
184 | 191 |
"type": "percent", |
185 | 192 |
"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" |
193 |
}, |
|
194 |
{ |
|
195 |
"name": "subfacts_min_delay_integer_1", |
|
196 |
"label": "subfacts min delay with integer=1", |
|
197 |
"type": "duration", |
|
198 |
"expression": "min(subfacts.duration) filter (where subfacts.integer = 1)", |
|
199 |
"join": ["subfacts"] |
|
200 |
}, |
|
201 |
{ |
|
202 |
"name": "subfacts_max_delay_integer_1", |
|
203 |
"label": "subfacts max delay with integer=1", |
|
204 |
"type": "duration", |
|
205 |
"expression": "max(subfacts.duration) filter (where subfacts.integer = 1)", |
|
206 |
"join": ["subfacts"] |
|
186 | 207 |
} |
187 | 208 |
] |
188 | 209 |
} |
tests/fixtures/schema1/01_schema.sql | ||
---|---|---|
29 | 29 |
string varchar |
30 | 30 |
); |
31 | 31 | |
32 |
CREATE TABLE subfacts ( |
|
33 |
id serial primary key, |
|
34 |
fact_id integer references schema1.facts(id), |
|
35 |
integer integer, |
|
36 |
datetime timestamp with time zone, |
|
37 |
duration interval |
|
38 |
); |
|
39 | ||
32 | 40 |
INSERT INTO category (ord, label) VALUES |
33 | 41 |
(1, 'caté1'), |
34 | 42 |
(0, 'caté2'), |
... | ... | |
50 | 58 |
('2017-01-01', '2017-01-01 10:00', 1, FALSE, 10, 1, 1, 1, 1, 'a'), |
51 | 59 |
('2017-01-02', '2017-01-02 10:00', 1, TRUE, 10, 3, 3, 3, 3, 'b'), |
52 | 60 |
('2017-01-03', '2017-01-03 10:00', 1, FALSE, 10, NULL, NULL, NULL, NULL, 'a'), |
53 |
('2017-01-04', '2017-01-04 10:00', 1, FALSE, 10, 1, 1, 1, 1, 'a'),
|
|
54 |
('2017-01-05', '2017-01-05 10:00', 1, TRUE, 10, 1, 1, 1, 1, 'c'),
|
|
55 |
('2017-01-06', '2017-01-06 10:00', 1, FALSE, 10, 1, 1, 1, 1, NULL),
|
|
61 |
('2017-01-04', '2017-01-04 10:00', 1, FALSE, 10, 4, 1, 1, 1, 'a'),
|
|
62 |
('2017-01-05', '2017-01-05 10:00', 1, TRUE, 10, 6, 1, 1, 1, 'c'),
|
|
63 |
('2017-01-06', '2017-01-06 10:00', 1, FALSE, 10, 7, 1, 1, 1, NULL),
|
|
56 | 64 |
('2017-01-07', '2017-01-07 10:00', 1, TRUE, 10, 1, 1, 1, 1, 'a'), |
57 | 65 |
('2017-01-08', '2017-01-08 10:00', 1, FALSE, 10, 1, 1, 1, 1, 'a'), |
58 | 66 |
('2017-01-09', '2017-01-09 10:00', 1, TRUE, 10, 1, 1, 1, 1, 'a'), |
59 |
('2017-01-10', '2017-01-10 10:00', 1, FALSE, 10, 1, 1, 1, 1, 'a'),
|
|
67 |
('2017-01-10', '2017-01-10 10:00', 1, FALSE, 10, 5, 1, 1, 1, 'a'),
|
|
60 | 68 |
('2017-02-01', '2017-02-01 10:00', 1, TRUE, 10, 1, 1, 1, 1, 'a'), |
61 |
('2017-03-01', '2017-03-01 10:00', 1, FALSE, 10, 1, 1, 1, 1, 'c'),
|
|
69 |
('2017-03-01', '2017-03-01 10:00', 1, FALSE, 10, 9, 1, 1, 1, 'c'),
|
|
62 | 70 |
('2017-04-01', '2017-04-01 10:00', 1, TRUE, 10, 1, 1, 1, 1, 'a'), |
63 | 71 |
('2017-05-01', '2017-05-01 10:00', 1, FALSE, 10, 1, 1, 1, 1, 'a'), |
64 |
('2017-06-01', '2017-06-01 10:00', 1, TRUE, 10, 1, 1, 1, 1, 'c'),
|
|
72 |
('2017-06-01', '2017-06-01 10:00', 1, TRUE, 10, 8, 1, 1, 1, 'c'),
|
|
65 | 73 |
('2017-07-01', '2017-07-01 10:00', 1, FALSE, 10, 1, 1, 1, 1, 'a'), |
66 | 74 |
('2017-08-01', '2017-08-01 10:00', 1, TRUE, 10, 1, 1, 1, 1, 'b'); |
75 | ||
76 |
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); |
tests/test_schema1.py | ||
---|---|---|
16 | 16 |
form.set('drilldown_x', 'innersubcategory') |
17 | 17 |
response = form.submit('visualize') |
18 | 18 |
assert 'big-msg-info' not in response |
19 |
assert get_table(response) == [ |
|
20 |
[u'Inner SubCategory', u'subé3', u'subé1'], |
|
21 |
['number of rows', '1', '15'], |
|
22 |
] |
|
19 |
assert get_table(response) == [['Inner SubCategory', u'sub\xe94', u'sub\xe95', |
|
20 |
u'sub\xe96', u'sub\xe98', u'sub\xe99', |
|
21 |
u'sub\xe97', u'sub\xe93', u'sub\xe91'], |
|
22 |
['number of rows', '1', '1', '1', '1', '1', |
|
23 |
'1', '1', '9']] |
|
23 | 24 |
form = response.form |
24 | 25 |
form.set('representation', 'table') |
25 | 26 |
form.set('measure', 'simple_count') |
... | ... | |
118 | 119 |
assert get_table(response) == get_ods_table(ods_response)[1:] |
119 | 120 |
root = get_ods_document(ods_response) |
120 | 121 |
nodes = root.findall('.//{%s}table-cell' % TABLE_NS) |
121 |
assert len([node for node in nodes if node.attrib['{%s}value-type' % OFFICE_NS] == 'float']) == 2 |
|
122 |
assert len([node for node in nodes if node.attrib['{%s}value-type' % OFFICE_NS] == 'float']) == 8 |
|
123 | ||
124 | ||
125 |
def test_complex_measure_with_join(schema1, app, admin): |
|
126 |
login(app, admin) |
|
127 |
response = app.get('/').follow() |
|
128 |
response = response.click('Facts 1') |
|
129 |
form = response.form |
|
130 |
form.set('representation', 'table') |
|
131 |
form.set('measure', 'subfacts_min_delay_integer_1') |
|
132 |
form.set('drilldown_x', 'innercategory') |
|
133 |
response = form.submit('visualize') |
|
134 |
assert 'big-msg-info' not in response |
|
135 |
# skip first line of ODS table as it's a header not present in the HTML display |
|
136 |
table = get_table(response) |
|
137 |
#[['Inner Category', u'cat\xe92', u'cat\xe93', u'cat\xe91'], |
|
138 |
#['subfacts min delay with integer=1', |
|
139 |
# "moins d'1 heure", |
|
140 |
# "moins d'1 heure", |
|
141 |
# "moins d'1 heure"]] |
|
142 |
assert all(len(row) == 4 for row in table) |
|
143 |
assert table[0][0] == 'Inner Category' |
|
144 |
assert table[1][0] == 'subfacts min delay with integer=1' |
|
145 | ||
146 |
form.set('measure', 'subfacts_max_delay_integer_1') |
|
147 |
response = form.submit('visualize') |
|
148 |
table = get_table(response) |
|
149 |
#[['Inner Category', u'cat\xe92', u'cat\xe93', u'cat\xe91'], |
|
150 |
# ['subfacts max delay with integer=1', '9 heure(s)', '8 heure(s)', '10 heure(s)']] |
|
151 |
assert all(len(row) == 4 for row in table) |
|
152 |
assert table[0][0] == 'Inner Category' |
|
153 |
assert table[1][0] == 'subfacts max delay with integer=1' |
|
122 |
- |