Compare commits

...

2 Commits

5 changed files with 90 additions and 21 deletions

View File

@ -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)

View File

@ -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):

View File

@ -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"]
}
]
}

View File

@ -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);

View File

@ -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'