Compare commits
2 Commits
main
...
wip/29914-
Author | SHA1 | Date |
---|---|---|
Benjamin Dauvergne | edf147d6be | |
Benjamin Dauvergne | c2b5d1c012 |
|
@ -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)
|
||||
|
|
|
@ -16,6 +16,8 @@
|
|||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import six
|
||||
|
||||
import datetime
|
||||
import decimal
|
||||
import collections
|
||||
|
@ -81,7 +83,7 @@ class Base(object):
|
|||
for key in slots:
|
||||
assert key in d or hasattr(cls, key), \
|
||||
'%s.%s is is a mandatory attribute' % (cls.__name__, key)
|
||||
if not key in d:
|
||||
if key not in d:
|
||||
continue
|
||||
value = d[key]
|
||||
if key in types:
|
||||
|
@ -123,13 +125,15 @@ class Base(object):
|
|||
|
||||
|
||||
class Measure(Base):
|
||||
__slots__ = ['name', 'label', 'type', 'expression']
|
||||
__slots__ = ['name', 'label', 'type', 'expression', 'join']
|
||||
__types__ = {
|
||||
'name': str,
|
||||
'label': unicode,
|
||||
'label': six.text_type,
|
||||
'type': type_cast,
|
||||
'expression': str,
|
||||
'join': [str],
|
||||
}
|
||||
join = None
|
||||
|
||||
|
||||
class Dimension(Base):
|
||||
|
@ -138,7 +142,7 @@ class Dimension(Base):
|
|||
'filter_needs_join', 'filter_expression']
|
||||
__types__ = {
|
||||
'name': str,
|
||||
'label': unicode,
|
||||
'label': six.text_type,
|
||||
'type': str,
|
||||
'join': [str],
|
||||
'value': str,
|
||||
|
@ -193,9 +197,9 @@ class Dimension(Base):
|
|||
value='TO_CHAR(EXTRACT(month from %s), \'00\') || \'/\' || EXTRACT(year from %s)'
|
||||
% (self.value, self.value),
|
||||
group_by='EXTRACT(year from %s), EXTRACT(month from %s)' % (self.value,
|
||||
self.value),
|
||||
self.value),
|
||||
order_by=['EXTRACT(year from %s), EXTRACT(month from %s)' % (self.value,
|
||||
self.value)],
|
||||
self.value)],
|
||||
filter=False),
|
||||
Dimension(
|
||||
label=u'mois (%s)' % self.label,
|
||||
|
@ -316,7 +320,7 @@ class Cube(Base):
|
|||
'measures']
|
||||
__types__ = {
|
||||
'name': str,
|
||||
'label': unicode,
|
||||
'label': six.text_type,
|
||||
'fact_table': str,
|
||||
'json_field': str,
|
||||
'key': str,
|
||||
|
@ -370,11 +374,10 @@ class Warehouse(Base):
|
|||
__slots__ = ['name', 'label', 'pg_dsn', 'search_path', 'cubes']
|
||||
__types__ = {
|
||||
'name': str,
|
||||
'label': unicode,
|
||||
'label': six.text_type,
|
||||
'pg_dsn': str,
|
||||
'search_path': [str],
|
||||
'cubes': [Cube],
|
||||
'search_path': [str],
|
||||
}
|
||||
|
||||
def check(self):
|
||||
|
|
|
@ -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"]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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'
|
||||
|
|
Loading…
Reference in New Issue